Skip to content

Commit cb9e899

Browse files
nanotaboadaclaude
andcommitted
docs(instructions): upgrade to Claude Code-optimized agent instructions
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9c2df9e commit cb9e899

1 file changed

Lines changed: 105 additions & 29 deletions

File tree

.github/copilot-instructions.md

Lines changed: 105 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,130 @@
22

33
## Overview
44

5-
REST API for managing football players built with ASP.NET Core 10. Implements CRUD operations with a layered architecture, EF Core + SQLite persistence, FluentValidation, AutoMapper, and in-memory caching. Part of a cross-language comparison study (Go, Java, Python, Rust, TypeScript).
5+
REST API for managing football players built with ASP.NET Core 10. Implements CRUD operations with a layered architecture, EF Core + SQLite persistence, FluentValidation, AutoMapper, and in-memory caching. Part of a cross-language comparison study (Go, Java, Python, Rust, TypeScript). Primarily a learning and reference project — clarity and educational value take precedence over brevity.
66

77
## Tech Stack
88

9-
- **Language**: C# (.NET 10 LTS)
10-
- **Framework**: ASP.NET Core (MVC controllers)
11-
- **ORM**: Entity Framework Core 10
12-
- **Database**: SQLite
13-
- **Mapping**: AutoMapper
14-
- **Validation**: FluentValidation
15-
- **Caching**: `IMemoryCache` (1-hour TTL)
16-
- **Logging**: Serilog (structured, console + file)
17-
- **Testing**: xUnit + Moq + FluentAssertions
18-
- **Formatting**: CSharpier
19-
- **Containerization**: Docker
9+
| Category | Technology |
10+
|-----------------|-------------------------------------|
11+
| Language | C# (.NET 10 LTS) |
12+
| Framework | ASP.NET Core (MVC controllers) |
13+
| ORM | Entity Framework Core 10 |
14+
| Database | SQLite |
15+
| Mapping | AutoMapper |
16+
| Validation | FluentValidation |
17+
| Caching | `IMemoryCache` (1-hour TTL) |
18+
| Logging | Serilog (structured, console + file)|
19+
| Testing | xUnit + Moq + FluentAssertions |
20+
| Formatting | CSharpier |
21+
| Containerization| Docker |
2022

2123
## Structure
2224

23-
```text
25+
```tree
2426
src/Dotnet.Samples.AspNetCore.WebApi/
2527
├── Controllers/ — HTTP handlers; minimal logic, delegate to services [HTTP layer]
2628
├── Services/ — Business logic + IMemoryCache caching [business layer]
2729
├── Repositories/ — Generic Repository<T> + specific implementations [data layer]
28-
├── Models/ — Player entity + DTOs
29-
├── Validators/ — FluentValidation (structure only; business rules in services)
30-
├── Profiles/ — AutoMapper profiles
31-
├── Data/ — DbContext + DbInitializer
32-
└── Storage/ — SQLite database file
30+
├── Models/ — Player entity + request/response DTOs
31+
├── Validators/ — FluentValidation validators (one per request model)
32+
├── Mappings/ — AutoMapper profiles (PlayerMappingProfile)
33+
├── Enums/ — Position abbreviations and other domain enumerations
34+
├── Extensions/ — IServiceCollection extension methods (service registration)
35+
├── Configurations/ — Options classes bound from appsettings.json
36+
├── Middlewares/ — Custom ASP.NET Core middleware
37+
├── Data/ — DbContext + DbInitializer (seed data)
38+
└── Storage/ — SQLite database file (players.db)
3339
3440
test/Dotnet.Samples.AspNetCore.WebApi.Tests/
35-
├── ControllersTests/
36-
└── ServicesTests/
41+
├── Unit/ — Unit tests (controllers, services, validators)
42+
└── Utilities/ — Shared test helpers: PlayerFakes, PlayerMocks, PlayerStubs
3743
```
3844

3945
**Layer rule**: `Controller → Service → Repository → Database`. Controllers must not access repositories directly. Business logic must not live in controllers.
4046

47+
**Cross-cutting**: `Program.cs` wires health checks (`GET /health`), rate limiting, CORS (dev only), and Swagger UI (dev only). Serilog is configured at host level. All validators are registered via `AddValidatorsFromAssemblyContaining<PlayerRequestModelValidator>()`.
48+
4149
## Coding Guidelines
4250

43-
- **Naming**: PascalCase (public members), camelCase (private fields)
51+
- **Naming**: PascalCase (public members), camelCase (private fields with `_` prefix)
4452
- **DI**: Primary constructors everywhere
4553
- **Async**: All I/O operations use `async`/`await`; no `ConfigureAwait(false)` (unnecessary in ASP.NET Core)
4654
- **Reads**: Use `AsNoTracking()` for all EF Core read queries
47-
- **Errors**: RFC 7807 Problem Details for all error responses
55+
- **Errors**: RFC 7807 Problem Details (`TypedResults.Problem` / `TypedResults.ValidationProblem`) for all error responses
4856
- **Logging**: Structured logging via `ILogger<T>`; never `Console.Write`
49-
- **Tests**: xUnit + Moq + FluentAssertions; naming convention per layer:
50-
- Controller: `{HttpMethod}_{Resource}_{Condition}_Returns{Outcome}` (e.g. `Get_Players_Existing_ReturnsPlayers`)
51-
- Service / Validator: `{MethodName}_{StateUnderTest}_{ExpectedBehavior}` (e.g. `RetrieveAsync_CacheMiss_QueriesRepositoryAndCachesResult`)
5257
- **Avoid**: synchronous EF Core APIs, controller business logic, static service/repository classes
5358

59+
### Test naming conventions
60+
61+
Tests live under `test/.../Unit/`. Two naming patterns, strictly by layer:
62+
63+
| Layer | Pattern | Example |
64+
|----------------------|---------------------------------------------------------------|------------------------------------------------------------------|
65+
| Controller | `{HttpMethod}_{Resource}_{Condition}_Returns{Outcome}` | `Get_Players_Existing_ReturnsPlayers` |
66+
| Service / Validator | `{MethodName}_{StateUnderTest}_{ExpectedBehavior}` | `RetrieveAsync_CacheMiss_QueriesRepositoryAndCachesResult` |
67+
68+
Each pattern has exactly three underscore-delimited segments. Do not add a fourth segment.
69+
70+
### FluentValidation rule sets
71+
72+
Validators use CRUD-named rule sets to make intent explicit. Use `RuleSet("Create", ...)` and `RuleSet("Update", ...)` — never anonymous / default rules.
73+
74+
```csharp
75+
// "Create" rule set — POST /players
76+
// Includes BeUniqueSquadNumber to prevent duplicate squad numbers on insert.
77+
RuleSet("Create", () => {
78+
RuleFor(p => p.SquadNumber)
79+
.MustAsync(BeUniqueSquadNumber).WithMessage("SquadNumber must be unique.");
80+
// ... other rules
81+
});
82+
83+
// "Update" rule set — PUT /players/squadNumber/{n}
84+
// BeUniqueSquadNumber intentionally omitted: the player already exists in DB.
85+
RuleSet("Update", () => {
86+
// ... same structural rules, no uniqueness check
87+
});
88+
```
89+
90+
Controllers must call the appropriate rule set explicitly:
91+
92+
```csharp
93+
// POST
94+
await validator.ValidateAsync(player, opts => opts.IncludeRuleSets("Create"));
95+
// PUT
96+
await validator.ValidateAsync(player, opts => opts.IncludeRuleSets("Update"));
97+
```
98+
99+
### Mocking validators in controller tests
100+
101+
`ValidateAsync(T, Action<ValidationStrategy<T>>)` is a FluentValidation extension method. Internally it calls `ValidateAsync(IValidationContext, CancellationToken)`. Moq must target the **interface overload**, not the generic one:
102+
103+
```csharp
104+
// ✅ Correct — matches the overload actually called at runtime
105+
_validatorMock
106+
.Setup(v => v.ValidateAsync(It.IsAny<IValidationContext>(), It.IsAny<CancellationToken>()))
107+
.ReturnsAsync(new ValidationResult());
108+
109+
// ❌ Wrong — targets a different overload; mock is never hit → NullReferenceException
110+
_validatorMock
111+
.Setup(v => v.ValidateAsync(It.IsAny<PlayerRequestModel>(), It.IsAny<CancellationToken>()))
112+
.ReturnsAsync(new ValidationResult());
113+
```
114+
115+
Add `using FluentValidation;` to any test file that calls the rule set overload.
116+
117+
### Test utilities
118+
119+
`test/.../Utilities/` contains shared helpers used across all unit tests:
120+
121+
| Class | Purpose |
122+
|-----------------|-------------------------------------------------------------------------|
123+
| `PlayerFakes` | Deterministic in-memory objects: `MakeNew()`, `MakeRequestModelForCreate()`, `MakeRequestModelForUpdate(n)`, `MakeFromStarting11(n)` |
124+
| `PlayerMocks` | Pre-configured `Mock<T>` setups for common scenarios |
125+
| `PlayerStubs` | Simple stub implementations where Moq would be overkill |
126+
127+
Always prefer `PlayerFakes` factory methods over constructing test data inline.
128+
54129
## Commands
55130

56131
### Quick Start
@@ -69,7 +144,7 @@ docker compose up
69144
1. Update `CHANGELOG.md` `[Unreleased]` section (Added / Changed / Fixed / Removed)
70145
2. `dotnet build --configuration Release` — must succeed
71146
3. `dotnet test --settings .runsettings` — all tests must pass
72-
4. Verify code formatting with CSharpier
147+
4. `dotnet csharpier .` — format; fix any reported issues
73148
5. Commit message follows Conventional Commits format (enforced by commitlint)
74149

75150
### Commits
@@ -98,17 +173,19 @@ Example: `feat(api): add player search endpoint (#123)`
98173
- Application configuration (`appsettings.json`)
99174
- API contracts (breaking DTO changes)
100175
- Caching strategy or TTL values
176+
- FluentValidation rule set structure (adding or removing rule sets affects controller callers and tests)
101177

102178
### Never modify
103179

104180
- Production configurations or deployment secrets
105181
- `.runsettings` coverage thresholds
106182
- Port configuration (9000)
107183
- Database type (SQLite — demo/dev only)
184+
- CD pipeline tag format (`vX.Y.Z-stadium`) or the stadium name sequence — names are assigned sequentially A→Z from the list in `CHANGELOG.md`; the next name is always the next unused letter
108185

109186
### Key workflows
110187

111-
**Add an endpoint**: Add DTO in `Models/` → update `PlayerMappingProfile` in `Mappings/` (AutoMapper) → add repository method(s) in `Repositories/` → add service method in `Services/` → add controller action in `Controllers/` → add validator in `Validators/` → add tests → run pre-commit checks.
188+
**Add an endpoint**: Add DTO in `Models/` → update `PlayerMappingProfile` in `Mappings/` → add repository method(s) in `Repositories/` → add service method in `Services/` → add controller action in `Controllers/` → add/update validator rule set in `Validators/` → add tests in `test/.../Unit/` → run pre-commit checks.
112189

113190
**Modify schema**: Update `Player` entity → update DTOs → update AutoMapper profile → reset `Storage/players.db` → update tests → run `dotnet test`.
114191

@@ -117,6 +194,5 @@ Example: `feat(api): add player search endpoint (#123)`
117194
```text
118195
feat(scope): description (#issue)
119196
120-
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
121-
Co-authored-by: Claude <noreply@anthropic.com>
197+
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
122198
```

0 commit comments

Comments
 (0)