Skip to content

Commit 31521f9

Browse files
author
aligneddev
committed
feat: Add migration test coverage policy tests to ensure all migrations have corresponding test entries
1 parent 356bd20 commit 31521f9

2 files changed

Lines changed: 82 additions & 1 deletion

File tree

.specify/memory/constitution.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ Modified Sections:
1010
- Compliance Audit Checklist: Added modular boundary and contract compatibility checks
1111
- Guardrails: Added non-negotiable interface/contract boundary rules for cross-module integration
1212
Status: Approved — modular architecture and contract-first parallel delivery are now constitutional requirements
13-
Current Update (v1.12.2): Added mandatory spec-completion gate requiring database migrations to be applied and E2E tests to pass before a spec can be marked done.
13+
Current Update (v1.12.3): Added mandatory per-migration test coverage governance requiring each migration to include a new or updated automated test, enforced by a migration coverage policy test in CI.
14+
Previous Update (v1.12.2): Added mandatory spec-completion gate requiring database migrations to be applied and E2E tests to pass before a spec can be marked done.
1415
Previous Updates:
1516
- v1.11.0: Strengthened TDD mandate with a strict gated red-green-refactor workflow requiring explicit user confirmation of failing tests before implementation.
1617
- v1.10.2: Codified a mandatory post-change verification command matrix so every change runs explicit checks before merge.
@@ -291,6 +292,7 @@ A vertical slice is **production-ready** only when all items are verified:
291292
- [ ] Post-change verification matrix executed for the impacted scope and evidence recorded
292293
- [ ] Feature branch deployed locally via `dotnet run` (entire Aspire stack: frontend, API, database)
293294
- [ ] Integration tests pass; manual E2E test via Playwright (if critical user journey)
295+
- [ ] Every migration introduced by the slice includes a new or updated automated test and an updated migration coverage policy mapping entry
294296
- [ ] All validation layers implemented: client-side (React validation), API (DTO DataAnnotations), database (constraints)
295297
- [ ] Events stored in event table with correct schema; projections materialized and queryable
296298
- [ ] Module boundaries preserved; cross-module interactions occur only via approved interfaces/contracts with compatibility evidence
@@ -362,6 +364,7 @@ Tests suggested by agent must receive explicit user approval before implementati
362364

363365
**Database Tests**
364366
- Migration up/down transitions
367+
- Migration coverage policy test must map every discovered migration to a new or updated automated test action
365368
- Event table constraints (unique EventId, non-null fields)
366369
- Foreign key integrity for aggregates
367370
- DataAnnotations constraints validated at database layer
@@ -394,6 +397,7 @@ Tests suggested by agent must receive explicit user approval before implementati
394397
14. **User Acceptance**: User validates slice meets specification and data validation rules observed
395398
15. **Phase Completion Commit**: Before starting the next phase, create a dedicated phase-completion commit that includes completed tasks and verification evidence for that phase
396399
16. **Spec Completion Gate**: Before marking any specification as done, database migrations for that spec must be applied successfully to the target local runtime database and the spec's end-to-end (Playwright) tests must run green
400+
17. **Migration Test Coverage Gate**: Every migration added or modified in a branch must include a new or updated automated test and must be represented in the migration coverage policy test map before merge
397401

398402
### Compliance Audit Checklist
399403

@@ -410,6 +414,7 @@ Tests suggested by agent must receive explicit user approval before implementati
410414
- [ ] TDD gate commits created: red baseline commit, green commit, and separate refactor commit when applicable
411415
- [ ] Phase completion commit created before moving to the next phase
412416
- [ ] Database migrations for the spec are created and applied successfully to the runtime database used for validation
417+
- [ ] Every migration introduced or modified by the spec has a corresponding new or updated automated test and a migration-coverage policy entry
413418
- [ ] Spec-level E2E (Playwright) suite executed and passing before spec marked complete
414419
- [ ] All SAMPLE_/DEMO_ data removed from code before merge
415420
- [ ] Secrets NOT committed; `.gitignore` verified; pre-commit hook prevents credential leakage
@@ -445,6 +450,7 @@ Breaking these guarantees causes architectural decay and technical debt accrual:
445450
- **TDD cycle is strictly gated and non-negotiable** — implementation code must never be written before failing tests exist, have been run, and the user has reviewed and confirmed the failures. The sequence is always: plan tests → write tests → run and prove failure → get user confirmation → implement → run after each change → verify all pass → consider refactoring. Skipping or reordering any step is prohibited.
446451
- **Commit gates are mandatory for TDD and phase transitions** — every TDD gate transition requires a commit (red, green, and refactor when performed), and every completed phase requires a dedicated phase-completion commit before proceeding.
447452
- **Spec completion requires migration + E2E gates** — a spec cannot be marked done until its database migrations are applied to the runtime database and its Playwright E2E scenarios pass.
453+
- **Every migration requires a test update** — each migration must ship with a new or updated automated test and an updated migration coverage policy entry; changes are blocked when migration coverage is incomplete.
448454
- **Expected-flow C# logic uses Result, not exceptions** — validation, not-found, conflict, and authorization business outcomes must be returned via typed Result objects (including error code/message metadata). Throwing exceptions for these expected outcomes is prohibited; exceptions are only for truly unexpected failures.
449455
- **Cross-module work is contract-first and interface-bound** — teams must integrate through explicit interfaces and versioned contracts only; direct coupling to another module's internal implementation is prohibited.
450456
- **No Entity Framework DbContext in domain layer** — domain must remain infrastructure-agnostic. If domain needs persistence logic, use repository pattern abstracting EF.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using BikeTracking.Api.Infrastructure.Persistence;
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.EntityFrameworkCore.Infrastructure;
4+
using Microsoft.EntityFrameworkCore.Migrations;
5+
6+
namespace BikeTracking.Api.Tests.Infrastructure;
7+
8+
public sealed class MigrationTestCoveragePolicyTests
9+
{
10+
private static readonly IReadOnlyDictionary<string, string> MigrationVerificationLedger =
11+
new Dictionary<string, string>(StringComparer.Ordinal)
12+
{
13+
["202603130001_InitialUserIdentity"] =
14+
"Updated test: user endpoint/auth integration tests validate identity schema behavior.",
15+
["20260323134325_AddRidesTable"] =
16+
"Added test: rides endpoint integration tests validate ride persistence and retrieval.",
17+
["20260327000000_AddRideVersion"] =
18+
"Added test: edit ride endpoint tests validate optimistic concurrency version handling.",
19+
["20260327165005_AddRideMilesUpperBound"] =
20+
"Updated test: rides validation tests enforce miles upper bound behavior.",
21+
["20260327171355_FixRideMilesUpperBoundNumericComparison"] =
22+
"Updated test: rides validation tests verify numeric comparison semantics in SQLite-backed flows.",
23+
["20260330202303_AddUserSettingsTable"] =
24+
"Added test: user settings endpoint integration tests validate persistence and retrieval contract.",
25+
["20260331135119_AddGasPriceToRidesAndLookupCache"] =
26+
"Added test: SQLite endpoint integration tests validate gas price column retrieval after migration.",
27+
};
28+
29+
[Fact]
30+
public void EveryMigrationMustHaveCoverageLedgerEntry()
31+
{
32+
using var context = CreateContext();
33+
var discoveredMigrations = context
34+
.GetService<IMigrationsAssembly>()
35+
.Migrations.Keys.OrderBy(static id => id, StringComparer.Ordinal)
36+
.ToArray();
37+
38+
var missing = discoveredMigrations.Except(MigrationVerificationLedger.Keys).ToArray();
39+
var stale = MigrationVerificationLedger.Keys.Except(discoveredMigrations).ToArray();
40+
41+
Assert.True(
42+
missing.Length == 0,
43+
$"Missing migration coverage entries: {string.Join(", ", missing)}"
44+
);
45+
Assert.True(
46+
stale.Length == 0,
47+
$"Ledger contains non-existent migrations: {string.Join(", ", stale)}"
48+
);
49+
}
50+
51+
[Fact]
52+
public void EveryCoverageEntryMustDeclareAddedOrUpdatedTestAction()
53+
{
54+
var invalid = MigrationVerificationLedger
55+
.Where(static entry =>
56+
!entry.Value.StartsWith("Added test:", StringComparison.Ordinal)
57+
&& !entry.Value.StartsWith("Updated test:", StringComparison.Ordinal)
58+
)
59+
.Select(static entry => entry.Key)
60+
.ToArray();
61+
62+
Assert.True(
63+
invalid.Length == 0,
64+
$"Coverage entries must start with 'Added test:' or 'Updated test:'. Invalid entries: {string.Join(", ", invalid)}"
65+
);
66+
}
67+
68+
private static BikeTrackingDbContext CreateContext()
69+
{
70+
var options = new DbContextOptionsBuilder<BikeTrackingDbContext>()
71+
.UseSqlite("Data Source=:memory:")
72+
.Options;
73+
return new BikeTrackingDbContext(options);
74+
}
75+
}

0 commit comments

Comments
 (0)