aw-spec-cli 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/.appweaver/ORCH-DIALECT.md +17 -5
- package/assets/.appweaver/orchestrator/README.md +1 -1
- package/assets/.appweaver/orchestrator/adapters/claude_agent_sdk.py +2 -2
- package/assets/.appweaver/orchestrator/adapters/google_antigravity.py +28 -6
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +13 -1
- package/assets/.appweaver/orchestrator/orchestrator.py +126 -39
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +5 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +1 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +1 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +888 -141
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +826 -16
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +780 -12
- package/assets/.appweaver/stack-profiles/java-spring.json +3 -3
- package/assets/.appweaver/templates/spec-template.md +73 -62
- package/assets/.appweaver/templates/story-template.md +107 -0
- package/assets/guardrails/technology/java-spring/ai-rules.md +505 -0
- package/assets/guardrails/technology/java-spring/api-versioning.md +782 -0
- package/assets/guardrails/technology/java-spring/authorization.md +618 -0
- package/assets/guardrails/technology/java-spring/clean-architecture.md +960 -0
- package/assets/guardrails/technology/java-spring/coding-standards.md +821 -0
- package/assets/guardrails/technology/java-spring/dependency-policy.md +91 -0
- package/assets/guardrails/technology/java-spring/domain-driven-design.md +467 -0
- package/assets/guardrails/technology/java-spring/domain_abstraction.md +424 -0
- package/assets/guardrails/technology/java-spring/folder-structure.md +1537 -0
- package/assets/guardrails/technology/java-spring/global_exception_handler.md +247 -0
- package/assets/guardrails/technology/java-spring/logging.md +215 -0
- package/assets/guardrails/technology/java-spring/modular-monolith.md +77 -0
- package/assets/guardrails/technology/java-spring/persistence.md +316 -0
- package/assets/guardrails/technology/java-spring/problem_details.md +98 -0
- package/assets/guardrails/technology/java-spring/result_mapping.md +48 -0
- package/assets/guardrails/technology/java-spring/technology-constitution.md +168 -0
- package/assets/guardrails/technology/java-spring/testing.md +601 -0
- package/assets/guardrails/technology/java-spring/zero-trust.md +210 -0
- package/dist/index.js +0 -0
- package/dist/utils/config.d.ts +22 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +76 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/copy-assets.d.ts.map +1 -1
- package/dist/utils/copy-assets.js +4 -11
- package/dist/utils/copy-assets.js.map +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +11 -7
- package/dist/utils/paths.js.map +1 -1
- package/package.json +2 -1
- package/assets/guardrails/technology/java-spring/README.md +0 -62
- package/dist/commands/update.d.ts +0 -3
- package/dist/commands/update.d.ts.map +0 -1
- package/dist/commands/update.js +0 -142
- package/dist/commands/update.js.map +0 -1
- package/dist/utils/manifest.d.ts +0 -31
- package/dist/utils/manifest.d.ts.map +0 -1
- package/dist/utils/manifest.js +0 -91
- package/dist/utils/manifest.js.map +0 -1
- package/dist/utils/update-engine.d.ts +0 -52
- package/dist/utils/update-engine.d.ts.map +0 -1
- package/dist/utils/update-engine.js +0 -203
- package/dist/utils/update-engine.js.map +0 -1
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
# Testing Strategy — Java · DDD · Hexagonal Architecture (Ports & Adapters)
|
|
2
|
+
|
|
3
|
+
> **Domain-agnostic.** This document defines testing standards applicable to any Java project
|
|
4
|
+
> structured around Domain-Driven Design and Hexagonal Architecture.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Core Philosophy
|
|
9
|
+
|
|
10
|
+
| Principle | Rule |
|
|
11
|
+
|-----------|------|
|
|
12
|
+
| **Test-First Engineering** | Tests MUST be written and verified to fail before the production change is in place. |
|
|
13
|
+
| **Right test at the right level** | Domain logic → pure unit tests. Application orchestration → unit tests. Adapter logic → integration/slice tests. No cross-layer leakage. |
|
|
14
|
+
| **Tests as executable specification** | Test names describe business behaviour or externally observable technical behaviour, not implementation details. |
|
|
15
|
+
| **Determinism** | Time, randomness, and identity generation are always injected. Production code MUST NOT directly create non-deterministic values inside business/application logic. |
|
|
16
|
+
| **Same-commit discipline** | Any change to an aggregate invariant, port contract, Result contract, or error representation MUST include the corresponding test update in the same commit. |
|
|
17
|
+
| **No production data in tests** | Tests use synthetic fixture data only. Production data MUST NOT appear in development, test, demo, or CI environments. |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. Architecture Layers and Their Test Owners
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
┌──────────────────────────────────────────────────┐
|
|
25
|
+
│ Driving Adapters (Primary) │
|
|
26
|
+
│ [ Controller Slice Tests / E2E ] │
|
|
27
|
+
├──────────────────────────────────────────────────┤
|
|
28
|
+
│ Application Layer (Use Cases) │
|
|
29
|
+
│ [ Application Service Unit Tests ] │
|
|
30
|
+
├──────────────────────────────────────────────────┤
|
|
31
|
+
│ Domain Layer (Core) │
|
|
32
|
+
│ [ Pure Domain Unit Tests ] │
|
|
33
|
+
├──────────────────────────────────────────────────┤
|
|
34
|
+
│ Driven Adapters (Secondary) │
|
|
35
|
+
│ [ Persistence / Adapter Integration ] │
|
|
36
|
+
└──────────────────────────────────────────────────┘
|
|
37
|
+
````
|
|
38
|
+
|
|
39
|
+
Domain unit tests MUST remain framework-independent.
|
|
40
|
+
|
|
41
|
+
Application unit tests SHOULD remain independent of Spring infrastructure wherever the
|
|
42
|
+
application implementation permits it. Spring-specific wiring is tested separately through
|
|
43
|
+
appropriate integration or slice tests.
|
|
44
|
+
|
|
45
|
+
Driven adapters are tested against real infrastructure through Testcontainers where
|
|
46
|
+
infrastructure behaviour is relevant. H2 MUST NOT be used as a substitute for PostgreSQL
|
|
47
|
+
integration testing.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 3. Test Types Reference
|
|
52
|
+
|
|
53
|
+
| Test Type | Layer | Framework | Spring Context | I/O |
|
|
54
|
+
| ------------------------ | ------------------- | -------------------------------------------- | -------------- | ------------------- |
|
|
55
|
+
| Domain unit | Domain | JUnit 5 | ❌ None | ❌ None |
|
|
56
|
+
| Application service unit | Application | JUnit 5 + Mockito | ❌ None | ❌ Mocked ports only |
|
|
57
|
+
| Controller slice | Primary Adapter | `@WebMvcTest` + MockMvc | ✅ Slice | ❌ Use case mocked |
|
|
58
|
+
| Persistence integration | Secondary Adapter | `@DataJpaTest` + Testcontainers | ✅ Slice | ✅ Real DB |
|
|
59
|
+
| Full integration | All layers | `@SpringBootTest` + Testcontainers | ✅ Full | ✅ Real DB |
|
|
60
|
+
| Architecture | Cross-cutting | ArchUnit | ❌ None | ❌ None |
|
|
61
|
+
| Result Mapping | HTTP/Infrastructure | JUnit 5 + Spring test support where required | As required | Usually ❌ |
|
|
62
|
+
| Problem Details | HTTP/Infrastructure | JUnit 5 + MockMvc where required | As required | Usually ❌ |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 4. Domain Unit Tests
|
|
67
|
+
|
|
68
|
+
### Rules
|
|
69
|
+
|
|
70
|
+
* No Spring context. No Mockito. No I/O.
|
|
71
|
+
* Test every aggregate **state transition**, **invariant enforcement**, and **domain event emission**.
|
|
72
|
+
* Test every **value object**: construction, equality, and rejection of invalid values.
|
|
73
|
+
* Test every **domain specification / policy**.
|
|
74
|
+
* Use only plain Java and the domain model itself.
|
|
75
|
+
* Domain tests MUST NOT depend on Result Mapping, Problem Details, HTTP, Spring Web, SLF4J, Logback, or other infrastructure concerns.
|
|
76
|
+
|
|
77
|
+
### Mandatory Coverage
|
|
78
|
+
|
|
79
|
+
* Every aggregate command method → at least one positive and one negative test.
|
|
80
|
+
* Every value object constructor with invalid input → rejection verified.
|
|
81
|
+
* Every invariant violation → appropriate domain failure behaviour verified.
|
|
82
|
+
* Every domain event-producing behaviour → event emission verified.
|
|
83
|
+
|
|
84
|
+
The exact failure representation MUST follow the application's established domain error model.
|
|
85
|
+
Tests MUST NOT require exceptions merely because a scenario represents failure if the application
|
|
86
|
+
uses explicit Result-based failure semantics.
|
|
87
|
+
|
|
88
|
+
### Naming Convention
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
should_[expected outcome]_when_[condition]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Determinism Rule
|
|
95
|
+
|
|
96
|
+
Time and identity generation MUST be injectable when required by the system under test.
|
|
97
|
+
|
|
98
|
+
Use `java.time.Clock` directly where appropriate, or an application-defined time abstraction when
|
|
99
|
+
additional control is required.
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
|
|
103
|
+
```java
|
|
104
|
+
Clock fixedClock =
|
|
105
|
+
Clock.fixed(
|
|
106
|
+
Instant.parse("2025-01-01T00:00:00Z"),
|
|
107
|
+
ZoneOffset.UTC
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
IdGenerator fixedIds =
|
|
111
|
+
() -> UUID.fromString("00000000-0000-0000-0000-000000000001");
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Production domain/application code MUST NOT directly call:
|
|
115
|
+
|
|
116
|
+
```java
|
|
117
|
+
LocalDateTime.now();
|
|
118
|
+
Instant.now();
|
|
119
|
+
UUID.randomUUID();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
when doing so would make business/application behaviour non-deterministic.
|
|
123
|
+
|
|
124
|
+
### Structure Example
|
|
125
|
+
|
|
126
|
+
```java
|
|
127
|
+
@DisplayName("Registration Aggregate")
|
|
128
|
+
class RegistrationTest {
|
|
129
|
+
|
|
130
|
+
@Test
|
|
131
|
+
void should_reject_registration_when_capacity_is_full() {
|
|
132
|
+
|
|
133
|
+
// Arrange
|
|
134
|
+
var event =
|
|
135
|
+
EventBuilder.anEventWithCapacity(1)
|
|
136
|
+
.withOneExistingRegistration()
|
|
137
|
+
.build();
|
|
138
|
+
|
|
139
|
+
// Act
|
|
140
|
+
var result = event.register(aParticipant(), fixedClock);
|
|
141
|
+
|
|
142
|
+
// Assert
|
|
143
|
+
assertThat(result.isFailure()).isTrue();
|
|
144
|
+
assertThat(result.error()).isEqualTo(CapacityExceededError.INSTANCE);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@Test
|
|
148
|
+
void should_emit_domain_event_when_registration_succeeds() {
|
|
149
|
+
|
|
150
|
+
// Arrange
|
|
151
|
+
var event = EventBuilder.anEventWithCapacity(10).build();
|
|
152
|
+
|
|
153
|
+
// Act
|
|
154
|
+
var result = event.register(aParticipant(), fixedClock);
|
|
155
|
+
|
|
156
|
+
// Assert
|
|
157
|
+
assertThat(result.isSuccess()).isTrue();
|
|
158
|
+
assertThat(result.domainEvents()).hasSize(1)
|
|
159
|
+
.first()
|
|
160
|
+
.isInstanceOf(ParticipantRegisteredEvent.class);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The exact `Result` API is implementation-specific and MUST follow the application's established
|
|
166
|
+
Result model.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 5. Application Service / Use Case Unit Tests
|
|
171
|
+
|
|
172
|
+
### Rules
|
|
173
|
+
|
|
174
|
+
* No Spring context unless the behaviour being tested specifically requires framework wiring.
|
|
175
|
+
* Use JUnit 5 and Mockito where mocking output ports is appropriate.
|
|
176
|
+
* Mock output ports such as persistence, messaging, external service, and event publication ports.
|
|
177
|
+
* Do NOT mock domain objects.
|
|
178
|
+
* Test orchestration logic only.
|
|
179
|
+
* Do NOT re-test domain invariants here.
|
|
180
|
+
* Tests MUST exercise the use case through its public application-facing contract.
|
|
181
|
+
* Application tests MUST verify the established application Result semantics where Result Mapping
|
|
182
|
+
is the standard failure mechanism.
|
|
183
|
+
|
|
184
|
+
### Mandatory Scenarios per Handler
|
|
185
|
+
|
|
186
|
+
Every Command Handler and Query Handler SHOULD have tests covering applicable scenarios:
|
|
187
|
+
|
|
188
|
+
| Scenario | Assertion |
|
|
189
|
+
| ---------------------- | ------------------------------------------------------------------------- |
|
|
190
|
+
| ✅ Happy path | Correct output port called; correct successful Result returned |
|
|
191
|
+
| ❌ Validation failure | Correct failed Result or established application error behaviour returned |
|
|
192
|
+
| ❌ Not found | Correct failed Result or established application error behaviour returned |
|
|
193
|
+
| ⚡ Concurrency conflict | Correct concurrency failure Result/error behaviour returned |
|
|
194
|
+
| ❌ Dependency failure | Correct failure behaviour returned without corrupting application state |
|
|
195
|
+
|
|
196
|
+
The test MUST NOT require a specific exception type when the established application Result model
|
|
197
|
+
represents that failure explicitly.
|
|
198
|
+
|
|
199
|
+
### Port Argument Verification
|
|
200
|
+
|
|
201
|
+
Use `@Captor` to assert the exact argument passed to output ports where the argument itself is
|
|
202
|
+
important to the use-case behaviour.
|
|
203
|
+
|
|
204
|
+
```java
|
|
205
|
+
@Captor
|
|
206
|
+
ArgumentCaptor<Registration> registrationCaptor;
|
|
207
|
+
|
|
208
|
+
verify(saveRegistrationPort).save(registrationCaptor.capture());
|
|
209
|
+
|
|
210
|
+
assertThat(registrationCaptor.getValue().status())
|
|
211
|
+
.isEqualTo(RegistrationStatus.CONFIRMED);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 6. Controller / Primary Adapter Slice Tests
|
|
217
|
+
|
|
218
|
+
### Rules
|
|
219
|
+
|
|
220
|
+
* Use `@WebMvcTest(XxxController.class)` where Spring MVC slice testing is appropriate.
|
|
221
|
+
* Mock the application input/use-case port.
|
|
222
|
+
* Do NOT load the full application context for ordinary controller slice tests.
|
|
223
|
+
* Do NOT test business logic — that belongs in application/domain tests.
|
|
224
|
+
* Tests exercise only the public HTTP surface.
|
|
225
|
+
* HTTP error responses MUST follow the application's Problem Details standard.
|
|
226
|
+
|
|
227
|
+
### Mandatory Scenarios per Endpoint
|
|
228
|
+
|
|
229
|
+
| Scenario | Assertion |
|
|
230
|
+
| -------------------------- | -------------------------------------------------------------------- |
|
|
231
|
+
| ✅ Happy path | Correct HTTP status (200/201/204) and response body shape |
|
|
232
|
+
| ❌ Invalid request body | HTTP 400 with standardized validation/Problem Details representation |
|
|
233
|
+
| 🔒 Unauthenticated | HTTP 401 where authentication is required |
|
|
234
|
+
| 🔒 Insufficient permission | HTTP 403 where authorization is required |
|
|
235
|
+
| ❌ Resource not found | Standardized HTTP 404 Problem Details response |
|
|
236
|
+
| ❌ Application failure | Appropriate HTTP status and Problem Details representation |
|
|
237
|
+
| ⚡ Concurrency conflict | Standardized HTTP 409 response where applicable |
|
|
238
|
+
|
|
239
|
+
Controller tests MUST NOT duplicate the internal logic of Result Mapping or Problem Details.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 7. Persistence / Secondary Adapter Integration Tests
|
|
244
|
+
|
|
245
|
+
### Rules
|
|
246
|
+
|
|
247
|
+
* Use `@DataJpaTest` with Testcontainers PostgreSQL where persistence integration behaviour is
|
|
248
|
+
being tested.
|
|
249
|
+
* `@AutoConfigureTestDatabase(replace = NONE)` SHOULD be used when required to prevent replacement
|
|
250
|
+
with an embedded database.
|
|
251
|
+
* **Never use H2 or another in-memory substitute for PostgreSQL integration tests.**
|
|
252
|
+
* Testcontainers configuration MUST use an approved PostgreSQL version managed by the project.
|
|
253
|
+
* Flyway migrations MUST be applied before persistence integration tests where migrations are part
|
|
254
|
+
of the application's persistence configuration.
|
|
255
|
+
* Tests SHOULD validate the migrated schema rather than relying on Hibernate schema generation.
|
|
256
|
+
|
|
257
|
+
### Mandatory Scenarios
|
|
258
|
+
|
|
259
|
+
Where applicable, persistence tests MUST verify:
|
|
260
|
+
|
|
261
|
+
* Entity mapping is correct.
|
|
262
|
+
* Column types, nullability, constraints, and indexes behave as expected.
|
|
263
|
+
* Repository adapter behaviour is correct.
|
|
264
|
+
* Specifications produce the expected query results.
|
|
265
|
+
* Pagination and ordering behave correctly.
|
|
266
|
+
* Optimistic concurrency behaviour is correctly persisted and detected.
|
|
267
|
+
* Soft-delete restrictions behave correctly.
|
|
268
|
+
* Flyway migrations apply cleanly from a blank schema.
|
|
269
|
+
* Unique and other database constraints produce the expected persistence failure behaviour.
|
|
270
|
+
|
|
271
|
+
Custom JPQL or native queries SHOULD only be tested where such queries are explicitly required by
|
|
272
|
+
the persistence design.
|
|
273
|
+
|
|
274
|
+
### Shared Base Class Pattern
|
|
275
|
+
|
|
276
|
+
```java
|
|
277
|
+
@Testcontainers
|
|
278
|
+
public abstract class AbstractPersistenceTest {
|
|
279
|
+
|
|
280
|
+
@Container
|
|
281
|
+
static final PostgreSQLContainer<?> POSTGRES =
|
|
282
|
+
new PostgreSQLContainer<>("postgres:<approved-version>")
|
|
283
|
+
.withDatabaseName("app_test")
|
|
284
|
+
.withUsername("app_test")
|
|
285
|
+
.withPassword("app_test");
|
|
286
|
+
|
|
287
|
+
@DynamicPropertySource
|
|
288
|
+
static void configureDataSource(DynamicPropertyRegistry registry) {
|
|
289
|
+
|
|
290
|
+
registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
|
|
291
|
+
registry.add("spring.datasource.username", POSTGRES::getUsername);
|
|
292
|
+
registry.add("spring.datasource.password", POSTGRES::getPassword);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The PostgreSQL version MUST be supplied through the project's dependency/version-management policy
|
|
298
|
+
rather than being prescribed by this rule.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 8. Full Integration Tests
|
|
303
|
+
|
|
304
|
+
### Rules
|
|
305
|
+
|
|
306
|
+
* Use `@SpringBootTest(webEnvironment = RANDOM_PORT)` + Testcontainers.
|
|
307
|
+
* Use `WebTestClient` or `RestAssured` for HTTP calls against the running application.
|
|
308
|
+
* Reserve full integration tests for **critical end-to-end flows** only.
|
|
309
|
+
* Full integration tests are not a substitute for unit or slice tests.
|
|
310
|
+
* Keep the total count intentionally small to protect CI execution time.
|
|
311
|
+
|
|
312
|
+
### Candidate Flows
|
|
313
|
+
|
|
314
|
+
* Core happy-path user journey.
|
|
315
|
+
* Authentication and authorization flow.
|
|
316
|
+
* Cross-module application-event flow.
|
|
317
|
+
* Critical persistence transaction flow.
|
|
318
|
+
* Critical error-response flow involving Result Mapping and Problem Details.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## 9. Architecture Tests (ArchUnit)
|
|
323
|
+
|
|
324
|
+
Architecture tests run on every build and enforce Hexagonal Architecture and module boundaries.
|
|
325
|
+
|
|
326
|
+
### Mandatory Rules
|
|
327
|
+
|
|
328
|
+
```java
|
|
329
|
+
@AnalyzeClasses(packages = "com.yourproject")
|
|
330
|
+
class HexagonalArchitectureTest {
|
|
331
|
+
|
|
332
|
+
@ArchTest
|
|
333
|
+
ArchRule domainIsFrameworkFree =
|
|
334
|
+
noClasses()
|
|
335
|
+
.that().resideInAPackage("..domain..")
|
|
336
|
+
.should().dependOnClassesThat()
|
|
337
|
+
.resideInAnyPackage(
|
|
338
|
+
"org.springframework..",
|
|
339
|
+
"jakarta.persistence..",
|
|
340
|
+
"javax.persistence.."
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
@ArchTest
|
|
344
|
+
ArchRule applicationDoesNotDependOnAdapters =
|
|
345
|
+
noClasses()
|
|
346
|
+
.that().resideInAPackage("..application..")
|
|
347
|
+
.should().dependOnClassesThat()
|
|
348
|
+
.resideInAPackage("..adapter..");
|
|
349
|
+
|
|
350
|
+
@ArchTest
|
|
351
|
+
ArchRule noDirectCrossModuleDomainImports =
|
|
352
|
+
noClasses()
|
|
353
|
+
.that().resideInAPackage("..moduleA..")
|
|
354
|
+
.should().dependOnClassesThat()
|
|
355
|
+
.resideInAPackage("..moduleB..domain..");
|
|
356
|
+
|
|
357
|
+
@ArchTest
|
|
358
|
+
ArchRule portsMustBeInterfaces =
|
|
359
|
+
classes()
|
|
360
|
+
.that().haveNameMatching(".*Port")
|
|
361
|
+
.should().beInterfaces();
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Architecture tests SHOULD additionally verify that:
|
|
366
|
+
|
|
367
|
+
* Domain does not depend on HTTP or Problem Details types.
|
|
368
|
+
* Domain does not depend on Logging or logging implementations.
|
|
369
|
+
* Application does not depend directly on infrastructure adapters.
|
|
370
|
+
* Modules do not access another module's repositories or domain internals.
|
|
371
|
+
* Result Mapping and Problem Details remain at the HTTP/infrastructure boundary.
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## 10. Result Mapping and Problem Details Tests
|
|
376
|
+
|
|
377
|
+
Because Result Mapping and Problem Details are explicit technical capabilities, their reusable
|
|
378
|
+
implementations MUST have focused tests.
|
|
379
|
+
|
|
380
|
+
### Result Mapping
|
|
381
|
+
|
|
382
|
+
Tests SHOULD verify:
|
|
383
|
+
|
|
384
|
+
* Successful generic Result → successful HTTP response containing the Result value.
|
|
385
|
+
* Successful non-generic Result → successful HTTP response without a body.
|
|
386
|
+
* Failed Result → delegation to Problem Details.
|
|
387
|
+
* Result Mapping does not construct a second/custom Problem Details representation.
|
|
388
|
+
* Result Mapping does not bypass the established Problem Details mechanism.
|
|
389
|
+
|
|
390
|
+
### Problem Details
|
|
391
|
+
|
|
392
|
+
Tests SHOULD verify:
|
|
393
|
+
|
|
394
|
+
* Standard Problem Details fields are populated correctly.
|
|
395
|
+
* Application errors can be represented as Problem Details.
|
|
396
|
+
* Appropriate exceptions can be mapped.
|
|
397
|
+
* Validation errors can be represented without performing validation.
|
|
398
|
+
* Diagnostic metadata is included only when configured and available.
|
|
399
|
+
* Sensitive exception information is not exposed.
|
|
400
|
+
* Stack traces are disabled by default for production configuration.
|
|
401
|
+
|
|
402
|
+
These tests MUST NOT move Problem Details or HTTP-specific types into Domain or Application tests.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## 11. Test Data and Fixtures
|
|
407
|
+
|
|
408
|
+
### Rules
|
|
409
|
+
|
|
410
|
+
* Use a **shared fixture/builder library** per project where repeated fixtures provide value.
|
|
411
|
+
* Builders use the Builder pattern or static factory methods.
|
|
412
|
+
* Builders provide sensible defaults.
|
|
413
|
+
* Tests override only values relevant to the scenario.
|
|
414
|
+
* Domain-specific sentinel values belong in the project's domain testing rules.
|
|
415
|
+
|
|
416
|
+
### Builder Pattern
|
|
417
|
+
|
|
418
|
+
```java
|
|
419
|
+
public class RegistrationBuilder {
|
|
420
|
+
|
|
421
|
+
private ParticipantId participantId =
|
|
422
|
+
ParticipantId.of(
|
|
423
|
+
UUID.fromString("00000000-0000-0000-0000-000000000001")
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
private EventId eventId =
|
|
427
|
+
EventId.of(
|
|
428
|
+
UUID.fromString("00000000-0000-0000-0000-000000000002")
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
private RegistrationStatus status =
|
|
432
|
+
RegistrationStatus.CONFIRMED;
|
|
433
|
+
|
|
434
|
+
public static RegistrationBuilder aRegistration() {
|
|
435
|
+
return new RegistrationBuilder();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
public RegistrationBuilder withStatus(RegistrationStatus status) {
|
|
439
|
+
this.status = status;
|
|
440
|
+
return this;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
public Registration build() {
|
|
444
|
+
return Registration.reconstitute(
|
|
445
|
+
participantId,
|
|
446
|
+
eventId,
|
|
447
|
+
status
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## 12. Coverage Targets
|
|
456
|
+
|
|
457
|
+
| Layer | Target | Tool | CI Gate |
|
|
458
|
+
| ----------------------------------------------- | ------------------------------------ | ------ | ------------- |
|
|
459
|
+
| Domain — aggregate behaviours and value objects | **100%** line + branch | JaCoCo | ✅ Fails build |
|
|
460
|
+
| Domain — all other domain code | **85%** line | JaCoCo | ✅ Fails build |
|
|
461
|
+
| Application — use case handlers | **85%** line | JaCoCo | ✅ Fails build |
|
|
462
|
+
| Primary Adapters | No fixed target — slice-tested | — | — |
|
|
463
|
+
| Secondary Adapters | No fixed target — integration-tested | — | — |
|
|
464
|
+
|
|
465
|
+
Coverage MUST be considered together with test quality and meaningful assertions.
|
|
466
|
+
|
|
467
|
+
Coverage MUST be measured on the changed-files delta per PR in addition to overall package
|
|
468
|
+
thresholds where supported by the CI tooling.
|
|
469
|
+
|
|
470
|
+
A PR that drops overall coverage below the approved threshold MUST be rejected by CI.
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## 13. CI Pipeline Test Gates
|
|
475
|
+
|
|
476
|
+
All of the following MUST pass before a PR can merge to `main`:
|
|
477
|
+
|
|
478
|
+
| Gate | Failure Action |
|
|
479
|
+
| -------------------------------------------------------- | -------------- |
|
|
480
|
+
| All unit tests pass | ❌ Block merge |
|
|
481
|
+
| All integration tests pass | ❌ Block merge |
|
|
482
|
+
| All ArchUnit architecture tests pass | ❌ Block merge |
|
|
483
|
+
| Domain + Application layer coverage ≥ approved threshold | ❌ Block merge |
|
|
484
|
+
| Required aggregate/value-object coverage achieved | ❌ Block merge |
|
|
485
|
+
| Test count report published | Informational |
|
|
486
|
+
|
|
487
|
+
Integration test execution time SHOULD be monitored and optimized where necessary.
|
|
488
|
+
|
|
489
|
+
### Security Hygiene in CI
|
|
490
|
+
|
|
491
|
+
* Test credentials MUST use non-production placeholder values.
|
|
492
|
+
* Integration tests MUST NOT use production environment variables.
|
|
493
|
+
* Tests MUST never hit production endpoints or production databases.
|
|
494
|
+
* Testcontainers MUST use isolated test infrastructure.
|
|
495
|
+
* Sensitive test data MUST NOT be committed to source control.
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## 14. Test-First Workflow
|
|
500
|
+
|
|
501
|
+
```text
|
|
502
|
+
1. Write the failing test → confirm it fails (RED)
|
|
503
|
+
|
|
504
|
+
2. Write minimum production code → confirm it passes (GREEN)
|
|
505
|
+
|
|
506
|
+
3. Refactor → confirm tests still pass (REFACTOR)
|
|
507
|
+
|
|
508
|
+
4. Commit test + production code together
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
This workflow is **non-negotiable**.
|
|
512
|
+
|
|
513
|
+
A PR that adds production behaviour without appropriate accompanying tests MUST be rejected during
|
|
514
|
+
code review, unless the change is explicitly classified as a non-behavioural configuration,
|
|
515
|
+
documentation, or infrastructure-only change where tests are not applicable.
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
## 15. Frontend Test Stack (if applicable)
|
|
520
|
+
|
|
521
|
+
Frontend testing is governed by the frontend/UI lane where one is present.
|
|
522
|
+
|
|
523
|
+
| Test Type | Framework | Scope |
|
|
524
|
+
| ---------------------- | -------------------------------------------- | --------------------------------- |
|
|
525
|
+
| Component unit | Project-approved frontend test framework | Rendering, user interactions |
|
|
526
|
+
| Hook / composable unit | Project-approved frontend test framework | Custom hooks and state logic |
|
|
527
|
+
| API client unit | Project-approved frontend test framework | Request format and error handling |
|
|
528
|
+
| E2E | Playwright or project-approved E2E framework | Critical user journeys |
|
|
529
|
+
|
|
530
|
+
Frontend-specific coverage targets SHOULD be defined by the frontend lane rather than this
|
|
531
|
+
backend technology rule.
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## 16. Required Dependencies
|
|
536
|
+
|
|
537
|
+
The exact versions MUST be controlled by the project's centralized dependency management or
|
|
538
|
+
approved BOM.
|
|
539
|
+
|
|
540
|
+
Typical testing dependencies include:
|
|
541
|
+
|
|
542
|
+
```xml
|
|
543
|
+
<!-- Testing stack -->
|
|
544
|
+
<dependency>
|
|
545
|
+
<groupId>org.springframework.boot</groupId>
|
|
546
|
+
<artifactId>spring-boot-starter-test</artifactId>
|
|
547
|
+
<scope>test</scope>
|
|
548
|
+
</dependency>
|
|
549
|
+
|
|
550
|
+
<dependency>
|
|
551
|
+
<groupId>org.testcontainers</groupId>
|
|
552
|
+
<artifactId>postgresql</artifactId>
|
|
553
|
+
<scope>test</scope>
|
|
554
|
+
</dependency>
|
|
555
|
+
|
|
556
|
+
<dependency>
|
|
557
|
+
<groupId>org.testcontainers</groupId>
|
|
558
|
+
<artifactId>junit-jupiter</artifactId>
|
|
559
|
+
<scope>test</scope>
|
|
560
|
+
</dependency>
|
|
561
|
+
|
|
562
|
+
<dependency>
|
|
563
|
+
<groupId>com.tngtech.archunit</groupId>
|
|
564
|
+
<artifactId>archunit-junit5</artifactId>
|
|
565
|
+
<scope>test</scope>
|
|
566
|
+
</dependency>
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
The project MAY use additional testing libraries where justified. This rule MUST NOT hardcode
|
|
570
|
+
library versions.
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## 17. Risks and Mitigations
|
|
575
|
+
|
|
576
|
+
| Risk | Mitigation |
|
|
577
|
+
| ------------------------------------------ | --------------------------------------------------------------------------------------------------- |
|
|
578
|
+
| Slow integration tests blocking CI | Share Testcontainers infrastructure where appropriate; reserve `@SpringBootTest` for critical flows |
|
|
579
|
+
| Coverage gamed by trivial tests | Code review policy requires meaningful assertions and behaviour-focused tests |
|
|
580
|
+
| Testcontainers unavailable in CI | CI runners MUST provide supported container execution |
|
|
581
|
+
| Non-deterministic test failures | Inject `Clock` and identity generation where required |
|
|
582
|
+
| Cross-module coupling introduced silently | ArchUnit cross-module boundary rules fail the build |
|
|
583
|
+
| Tests written after production code | PR review requires tests for applicable behavioural changes |
|
|
584
|
+
| Error behaviour diverges from API standard | Controller and Problem Details integration tests verify standardized error responses |
|
|
585
|
+
| Result Mapping duplicates error handling | Focused Result Mapping tests verify delegation to Problem Details |
|
|
586
|
+
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### One important point
|
|
590
|
+
|
|
591
|
+
I would **not change the testing file beyond this level**. In particular, I would not add detailed package structures, exact Spring annotations, or exact class names because your other guardrails correctly state that implementation details should remain configurable.
|
|
592
|
+
|
|
593
|
+
Also, your **`technology-constitution.md` itself has one inconsistency** that should eventually be corrected:
|
|
594
|
+
|
|
595
|
+
> Principle X says: **"Structured logging using SLF4J/Logback"**
|
|
596
|
+
|
|
597
|
+
But `logging.md` says the **backend/provider is pluggable** and only SLF4J is the default API/facade. Therefore the constitution should say something like:
|
|
598
|
+
|
|
599
|
+
> **Structured logging using the approved SLF4J API/facade and configured approved implementation; provider substitution must remain possible.**
|
|
600
|
+
|
|
601
|
+
That is a **separate small change** I recommend making to `technology-constitution.md`.
|