Skip to content

Commit b844951

Browse files
committed
Merge branch 'master' into test/bdd-given-when-then-pattern
1 parent 1f915ff commit b844951

File tree

2 files changed

+262
-354
lines changed

2 files changed

+262
-354
lines changed

.github/copilot-instructions.md

Lines changed: 104 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -2,212 +2,131 @@
22

33
## Project Overview
44

5-
This is a Spring Boot REST API for managing football players, demonstrating modern Java patterns and clean architecture. It's a learning-focused proof-of-concept that follows Spring Boot best practices with layered architecture (Controller → Service → Repository).
5+
REST API for managing football players built with Java 25 and Spring Boot 4. Demonstrates layered architecture (Controller → Service → Repository), Spring Data JPA with SQLite, comprehensive validation, caching, and Swagger documentation.
66

7-
## Stack
8-
9-
- **Java**: 25 (LTS, required for consistency)
10-
- **Spring Boot**: 4.0.0 (with Spring MVC)
11-
- **ORM**: Spring Data JPA with Hibernate
12-
- **Database**: SQLite (file-based for runtime, in-memory for tests)
13-
- **Build Tool**: Maven 3 (use `./mvnw` wrapper, NOT system Maven)
14-
- **Containers**: Docker with Docker Compose
15-
- **Logging**: SLF4J with Logback
16-
- **Monitoring**: Spring Boot Actuator
17-
- **API Docs**: SpringDoc OpenAPI 3 (Swagger UI)
18-
19-
## Project Patterns
20-
21-
- **Layered architecture**: Controller → Service → Repository → Database
22-
- **Dependency injection**: Constructor injection via Lombok's @RequiredArgsConstructor
23-
- **DTO pattern**: Never expose entities in controllers, use DTOs with ModelMapper
24-
- **Caching**: Spring Cache abstraction (@Cacheable) with 1-hour TTL
25-
- **Error handling**: @ControllerAdvice for global exception handling
26-
- **Validation**: Bean Validation (JSR-380) in DTOs
27-
- **Repository queries**: Mix of derived queries (findBySquadNumber) and custom JPQL (@Query)
28-
- **Date handling**: ISO-8601 strings with custom JPA converter for SQLite compatibility
29-
30-
## Code Conventions
31-
32-
### Naming
7+
## Quick Start
338

34-
- **Classes**: PascalCase (e.g., `PlayersController`, `PlayerDTO`)
35-
- **Methods/Variables**: camelCase (e.g., `findById`, `squadNumber`)
36-
- **Test methods**: `givenX_whenY_thenZ` (e.g., `givenSquadNumberExists_whenPost_thenReturnsConflict`)
37-
38-
### Annotations
39-
40-
- **Controllers**: @RestController (never @Controller for REST APIs)
41-
- **DTOs**: Lombok @Data, @Builder, @AllArgsConstructor
42-
- **Services**: @Service + @Transactional on mutating methods
43-
- **Repositories**: @Repository (extend JpaRepository)
44-
- **Entities**: @Entity with @Table, @Id, @GeneratedValue
9+
```bash
10+
# Development
11+
./mvnw spring-boot:run # Run with hot reload (port 9000)
12+
./mvnw clean test # Run tests
13+
./mvnw clean test jacoco:report # Test with coverage
4514

46-
### Lombok usage
15+
# Docker
16+
docker compose up # Start in container
17+
docker compose down -v # Reset database
4718

48-
- Reduce boilerplate with @Data, @Builder, @AllArgsConstructor
49-
- Use @RequiredArgsConstructor for constructor injection
50-
- Never use field injection
19+
# Documentation
20+
http://localhost:9000/swagger/index.html # API docs
21+
http://localhost:9001/actuator/health # Health check
22+
```
5123

52-
### REST conventions
24+
## Stack
5325

54-
- Return ResponseEntity<T> from controllers
55-
- Use proper HTTP status codes (201 Created, 204 No Content, 409 Conflict)
56-
- OpenAPI annotations required on all endpoints
26+
- Java 25 (LTS, required)
27+
- Spring Boot 4.0.0 (Spring MVC)
28+
- Spring Data JPA + Hibernate
29+
- SQLite (file-based runtime, in-memory tests)
30+
- Maven 3 (use `./mvnw` wrapper)
31+
- Bean Validation (JSR-380)
32+
- Spring Cache (1-hour TTL)
33+
- JUnit 5 + AssertJ + MockMvc
34+
- JaCoCo (coverage)
35+
- SpringDoc OpenAPI 3 (Swagger)
36+
- Lombok (boilerplate reduction)
5737

58-
### Logging
38+
## Project Patterns
5939

60-
- Use SLF4J (injected via Lombok @Slf4j)
61-
- Never use System.out.println
40+
- **Architecture**: Layered (Controller → Service → Repository → JPA)
41+
- **Dependency Injection**: Constructor injection via Lombok `@RequiredArgsConstructor`
42+
- **Error Handling**: `@ControllerAdvice` for global exception handling
43+
- **Validation**: Bean Validation annotations in DTOs (`@NotNull`, `@Min`, etc.)
44+
- **Caching**: Spring `@Cacheable` on service layer (1-hour TTL)
45+
- **DTO Pattern**: ModelMapper for entity ↔ DTO transformations
46+
- **Repository**: Spring Data JPA with derived queries and custom JPQL
6247

63-
### Configuration
48+
## Code Conventions
6449

65-
- Externalize via @Value or application.yml
66-
- Never hardcode values in code
50+
- **Naming**: camelCase (methods/variables), PascalCase (classes), UPPER_SNAKE_CASE (constants)
51+
- **Files**: Class name matches file name (e.g., `PlayersController.java`)
52+
- **Package Structure**:
53+
- `controllers/` - REST endpoints (`@RestController`)
54+
- `services/` - Business logic (`@Service`)
55+
- `repositories/` - Data access (`@Repository`, extends `JpaRepository`)
56+
- `models/` - Domain entities (`@Entity`) and DTOs
57+
- `converters/` - JPA attribute converters
58+
- **Annotations**:
59+
- Controllers: `@RestController`, `@RequestMapping`, `@Operation` (OpenAPI)
60+
- Services: `@Service`, `@Transactional`, `@Cacheable`
61+
- Repositories: `@Repository` (Spring Data JPA)
62+
- DTOs: `@NotNull`, `@Min`, `@Max` (Bean Validation)
63+
- Entities: `@Entity`, `@Table`, `@Id`, `@GeneratedValue`
64+
- **Lombok**: `@Data`, `@Builder`, `@AllArgsConstructor`, `@RequiredArgsConstructor`
65+
- **Logging**: SLF4J (never `System.out.println`)
6766

6867
## Testing
6968

70-
### Framework
71-
72-
- **Unit tests**: JUnit 5 with AssertJ for fluent assertions
73-
- **Integration tests**: @SpringBootTest with MockMvc
74-
- **Coverage**: JaCoCo reports (must maintain high coverage)
75-
76-
### Test naming
77-
78-
**Pattern**: `givenX_whenY_thenZ()` (BDD Given-When-Then)
79-
80-
**Naming philosophy:**
81-
82-
Use **semantic naming** that describes the business/domain state and behavior, not technical implementation details:
83-
84-
- **Given** (precondition): Describe the state of the system or data
85-
- `givenPlayerExists` - A player is present in the system
86-
- `givenNoExistingPlayer` - No player matching criteria
87-
- `givenInvalidPlayer` - Invalid data provided
88-
- `givenRaceCondition` - Concurrent modification scenario
89-
- `givenNullId` - Missing required identifier
90-
- **When** (action): The method/operation being tested
91-
- `whenCreate` - Creating a new entity
92-
- `whenRetrieveById` - Fetching by identifier
93-
- `whenUpdate` - Updating existing entity
94-
- `whenDelete` - Removing an entity
95-
- `whenSearchByLeague` - Searching/filtering operation
96-
- **Then** (outcome): Expected result or behavior
97-
- `thenReturnsPlayerDTO` - Returns the DTO object
98-
- `thenReturnsNull` - Returns null (not found/conflict)
99-
- `thenReturnsTrue` / `thenReturnsFalse` - Boolean success indicator
100-
- `thenReturns26Players` - Specific count of results
101-
- `thenReturnsEmptyList` - No results found
102-
- `thenReturnsOk` - HTTP 200 response
103-
- `thenReturnsNotFound` - HTTP 404 response
104-
- `thenReturnsCreated` - HTTP 201 response
105-
106-
**Code conventions:**
107-
108-
- Comments: Use **Given/When/Then** (BDD pattern)
109-
- Assertions: Use `then()` from AssertJ BDDAssertions
110-
- Variables:
111-
- Use `actual` for operation results
112-
- Use `expected` for comparison values when verifying equality
113-
- Use `existing` for pre-saved entities in database
114-
115-
**Why BDD Given-When-Then?**
116-
117-
-**Readable**: Natural language flow, accessible to all stakeholders
118-
-**Behavior-focused**: Tests business logic, not implementation details
119-
-**Self-documenting**: Method name clearly states test scenario
120-
-**Framework-aligned**: Matches Cucumber/SpecFlow patterns
121-
122-
```java
123-
// Examples across layers:
124-
givenNoExistingPlayer_whenCreate_thenReturnsPlayerDTO() // Service: success case
125-
givenPlayerAlreadyExists_whenCreate_thenReturnsNull() // Service: conflict case
126-
givenPlayerExists_whenFindById_thenReturnsPlayer() // Repository: found
127-
givenPlayerDoesNotExist_whenFindById_thenReturnsEmpty() // Repository: not found
128-
givenValidPlayer_whenPost_thenReturnsCreated() // Controller: HTTP 201
129-
givenPlayerDoesNotExist_whenGetById_thenReturnsNotFound() // Controller: HTTP 404
130-
```
131-
132-
### Test structure
133-
134-
Use BDD (Given/When/Then) consistently in JavaDoc comments, method names, and code sections:
135-
136-
```java
137-
/**
138-
* Given no existing player with the same squad number
139-
* When create() is called with valid player data
140-
* Then the player is saved and a PlayerDTO is returned
141-
*/
142-
@Test
143-
void givenNoExistingPlayer_whenCreate_thenReturnsPlayerDTO() {
144-
// Given
145-
Player player = PlayerFakes.createOneValid();
146-
PlayerDTO expected = PlayerDTOFakes.createOneValid();
147-
// When
148-
PlayerDTO actual = playersService.create(expected);
149-
// Then
150-
then(actual).isEqualTo(expected);
151-
}
152-
```
153-
154-
### Test requirements
155-
156-
- Unit tests required for all service and repository methods
157-
- Integration tests required for all controller endpoints
158-
- Tests use in-memory SQLite (jdbc:sqlite::memory:)
159-
- All tests must pass before PR (`./mvnw clean install`)
160-
- **Assertion quality**: Verify actual behavior, not just counts (e.g., verify league content, not just `hasSize()`)
69+
- **Structure**: `*Tests.java` in `src/test/java/` (mirrors main package structure)
70+
- **Naming Pattern**: `method_scenario_outcome`
71+
- `method`: Method under test (e.g., `post`, `findById`, `create`)
72+
- `scenario`: Context (e.g., `playerExists`, `invalidData`, `noMatches`)
73+
- `outcome`: Expected result (e.g., `returnsPlayer`, `returnsConflict`, `returnsEmpty`)
74+
- **Examples**:
75+
76+
```java
77+
// Controller
78+
void post_squadNumberExists_returnsConflict()
79+
80+
// Service
81+
void create_noConflict_returnsPlayerDTO()
82+
83+
// Repository
84+
void findById_playerExists_returnsPlayer()
85+
```
86+
87+
- **JavaDoc**: BDD Given/When/Then structure in test comments
88+
89+
```java
90+
/**
91+
* Given a player with squad number 5 already exists in the database
92+
* When POST /players is called with a new player using squad number 5
93+
* Then response status is 409 Conflict
94+
*/
95+
@Test
96+
void post_squadNumberExists_returnsConflict() { ... }
97+
```
98+
99+
- **Annotations**: `@SpringBootTest`, `@AutoConfigureMockMvc`, `@Test`
100+
- **Assertions**: AssertJ (fluent, e.g., `assertThat(result).isNotNull()`)
101+
- **Mocking**: Mockito for service layer tests
102+
- **Database**: Tests use in-memory SQLite (auto-cleared after each test)
103+
- **Coverage**: Target high coverage (JaCoCo reports in `target/site/jacoco/`)
161104

162105
## Avoid
163106

164-
- **Field injection** - Always use constructor injection
165-
- **Using `new` for Spring beans** - Breaks dependency injection
166-
- **Missing @Transactional** - Required on service methods that modify data
167-
- **Exposing entities in controllers** - Always use DTOs
168-
- **System.out.println** - Use SLF4J logging
169-
- **Hardcoded config** - Use @Value or application.yml
170-
- **System Maven** - Always use `./mvnw` wrapper for consistency
171-
- **SQLite in production** - This is a demo/development-only setup
172-
173-
## Folder Structure
174-
175-
```tree
176-
src/main/java/ # Application code
177-
├── Application.java # @SpringBootApplication entry point
178-
├── controllers/ # REST endpoints
179-
├── services/ # Business logic + caching
180-
├── repositories/ # Data access (Spring Data JPA)
181-
├── models/ # Entities (Player) and DTOs (PlayerDTO)
182-
└── converters/ # JPA converters (ISO date handling)
183-
src/test/java/ # Test classes (mirrors main structure)
184-
storage/ # SQLite database file (runtime)
185-
scripts/ # Docker entrypoint and healthcheck
186-
```
187-
188-
## Quick Reference
107+
- Field injection (use constructor injection)
108+
- Using `new` for Spring beans (breaks DI)
109+
- Missing `@Transactional` on service methods that modify data
110+
- Exposing entities directly in controllers (use DTOs)
111+
- `System.out.println` (use SLF4J logging)
112+
- Hardcoded configuration (use `@Value` or `application.properties`)
113+
- Ignoring exceptions (always handle or propagate)
114+
- Testing implementation details (test behavior, not internals)
189115

190-
```bash
191-
# Development
192-
./mvnw spring-boot:run # Run with hot reload
193-
./mvnw clean test jacoco:report # Test with coverage
116+
## Commit Messages
194117

195-
# Docker
196-
docker compose up # Start in container
118+
Follow Conventional Commits format (enforced by commitlint in CI):
197119

198-
# Documentation
199-
http://localhost:8080/swagger-ui.html # API docs
200-
http://localhost:8080/actuator/health # Health check
201-
```
120+
**Format**: `type(scope): description (#issue)` (max 80 chars)
202121

203-
## Commit Messages
122+
**Types**: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `ci`, `perf`, `style`, `build`
204123

205-
Follow Conventional Commits with issue number suffix:
124+
**Examples**:
206125

207-
- **Format**: `type(scope): description (#issue)` (max 80 chars)
208-
- **Types**: feat, fix, chore, docs, test, refactor
209-
- **Example**: `feat(api): add squad number search endpoint (#123)`
126+
- `feat(api): add player stats endpoint (#42)`
127+
- `fix(service): resolve cache invalidation bug (#88)`
128+
- `test: adopt BDD Given-When-Then pattern across all tests (#266)`
210129

211-
## Additional Context
130+
---
212131

213-
For detailed operational procedures, workflows, and troubleshooting, see `AGENTS.md`.
132+
For detailed workflows, troubleshooting, and CI/CD setup, load `#file:AGENTS.md`.

0 commit comments

Comments
 (0)