AZ-491 (3 SP): eliminate the cycle-2 duplicate of JWT-minting logic that existed in both SatelliteProvider.Tests/TestUtilities/ JwtTokenFactory.cs (unit-side) and SatelliteProvider.IntegrationTests/ JwtTestHelpers.cs (integration-side), where the same Expires < NotBefore bug needed parallel fixes in commitsf64d0d7+11b7074. Option A chosen: new SatelliteProvider.TestSupport class library (no test framework) holds the canonical JwtTokenFactory.Create / CreateExpired / TamperSignature. Both Tests and IntegrationTests consume it via ProjectReference; production projects (Api, Common, DataAccess, Services.*) cannot depend on it. The notBefore-shift workaround is preserved with an inline regression-prevention comment back-referencing the cycle-2 fix commits. SatelliteProvider.IntegrationTests/JwtTestHelpers.cs is stripped to runner-only concerns: ResolveSecretOrThrow, AttachDefaultAuthorization, and the DefaultSubject = "integration-tests" constant. Call sites in Program.cs, JwtIntegrationTests.cs, and UavUploadTests.cs (10 sites) switched to JwtTokenFactory.* with JwtTestHelpers.DefaultSubject explicitly passed for the runner subject - behavior parity preserved. Dockerfile for IntegrationTests gets the new TestSupport csproj in its pre-restore COPY layer. Api Dockerfile unchanged (TestSupport is NOT a production dependency). A new code-review SKILL.md Phase 6 checklist row flags near-identical helper logic across test projects as a Medium / Maintainability finding with explicit cycle-2 retro back-reference, so this whole pattern stops at one occurrence. module-layout.md adds a TestSupport Shared/Cross-Cutting entry documenting the production-isolation invariant. tests_unit.md + tests_integration.md updated to describe the consolidated layout. sln updated. Test-suite gate (AC-2 + AC-3) deferred to Step 16 Final Test Run per implement-skill convention. Per-batch review verdict: PASS_WITH_WARNINGS with 1 Low (pre-existing 7.0.3 version pin preserved verbatim from cycle-2 IntegrationTests csproj for parity; not blocking; deferred bump). Co-authored-by: Cursor <cursoragent@cursor.com>
8.3 KiB
Batch Report — Batch 02 cycle 3
Batch: 02 (cycle 3) Tasks: AZ-491 (consolidate JWT test-mint helpers) Date: 2026-05-12
Task Results
| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|---|---|---|---|---|---|
| AZ-491_consolidate_jwt_test_helpers | Done | 2 added + 12 modified + 1 deleted (see below) | Existing 253 unit tests + integration JWT/UAV tests now consume the consolidated factory (Step 16 final gate) | 6/6 ACs covered | 0 blockers |
AC Test Coverage: All covered (6 of 6)
Code Review Verdict: pending (this batch report precedes per-batch review)
Auto-Fix Attempts: 0
Stuck Agents: None
What was implemented
Chose Option A — new SatelliteProvider.TestSupport class library. Rationale: cleaner long-term layering (production code may NOT depend on test-support; integration tests do NOT acquire a dependency on unit tests); pre-stages AZ-492 which needs the same canonical mint surface for the perf harness.
Added
SatelliteProvider.TestSupport/SatelliteProvider.TestSupport.csproj(class library, .NET 8.0, no test framework). PackageReferences:Microsoft.IdentityModel.Tokens7.0.3,System.IdentityModel.Tokens.Jwt7.0.3.SatelliteProvider.TestSupport/JwtTokenFactory.cs— canonical implementation. ExposesCreate(secret, subject=DefaultSubject, lifetime, extraClaims, algorithm),CreateExpired(secret, subject),TamperSignature(token). TheExpires <= NotBeforeworkaround (shiftnotBeforebehindexpiresfor non-positive lifetimes) is preserved with an inline comment back-referencing the cycle-2 commitsf64d0d7+11b7074to prevent regression.
Modified
SatelliteProvider.Tests/SatelliteProvider.Tests.csproj— addedProjectReferenceto TestSupport.SatelliteProvider.Tests/Authentication/JwtTokenFactoryTests.cs—using SatelliteProvider.Tests.TestUtilities;→using SatelliteProvider.TestSupport;. The 4 existing test methods (Create_ProducesTokenValidatedByMatchingParameters,Create_WithExtraClaims_PropagatesClaimsThroughValidation,CreateExpired_TokenFailsValidationWithLifetimeException,TamperSignature_TokenFailsValidationWithSignatureException) now exercise the consolidated factory.SatelliteProvider.Tests/Authentication/AuthenticationServiceCollectionExtensionsTests.cs— same namespace switch for theJwtTokenFactory.Create(envSecret)call site.SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj— addedProjectReferenceto TestSupport; removed the explicitSystem.IdentityModel.Tokens.JwtPackageReference (now flowing transitively through TestSupport).SatelliteProvider.IntegrationTests/JwtTestHelpers.cs— stripped to runner-only concerns:JwtSecretEnvVar,DefaultSubject = "integration-tests",ResolveSecretOrThrow,AttachDefaultAuthorization. RemovedMintValidToken,MintExpiredToken,TamperSignature. Theusing System.IdentityModel.Tokens.Jwt;/using Microsoft.IdentityModel.Tokens;imports were removed since they are no longer needed at this site.SatelliteProvider.IntegrationTests/Program.cs—JwtTestHelpers.MintValidToken(jwtSecret)→JwtTokenFactory.Create(jwtSecret, JwtTestHelpers.DefaultSubject).using SatelliteProvider.TestSupport;added.SatelliteProvider.IntegrationTests/JwtIntegrationTests.cs— 3 call sites switched:MintExpiredToken(secret)→JwtTokenFactory.CreateExpired(secret, JwtTestHelpers.DefaultSubject);MintValidToken(secret)→JwtTokenFactory.Create(secret, JwtTestHelpers.DefaultSubject);TamperSignature→JwtTokenFactory.TamperSignature.using SatelliteProvider.TestSupport;added.SatelliteProvider.IntegrationTests/UavUploadTests.cs— 6 call sites ofJwtTestHelpers.MintValidToken(secret, extraClaims: ...)switched toJwtTokenFactory.Create(secret, JwtTestHelpers.DefaultSubject, extraClaims: ...).using SatelliteProvider.TestSupport;added.SatelliteProvider.IntegrationTests/Dockerfile— addedCOPY ["SatelliteProvider.TestSupport/SatelliteProvider.TestSupport.csproj", "SatelliteProvider.TestSupport/"]before thedotnet restorestep so the build context restores the new ProjectReference correctly. The API Dockerfile is unchanged (TestSupport is NOT a production dependency).SatelliteProvider.sln— new project entry for TestSupport + ProjectConfigurationPlatforms rows..cursor/skills/code-review/SKILL.mdPhase 6 — added a bolded checklist row: "Duplicate test helpers across test projects (AZ-491)" — flags near-identical credential-minting / fixture-generation logic acrossTests+IntegrationTests+ future perf harnesses as a Medium / Maintainability finding with recommended consolidation intoSatelliteProvider.TestSupport. The cycle-2 retrof64d0d7 + 11b7074precedent is cited inline so future agents understand this is a security-relevance maintenance risk, not just a style nit._docs/02_document/module-layout.md— added a new "TestSupport (added by AZ-491)" Shared / Cross-Cutting entry with Public API, PackageReferences, Consumed-by, and an explicit "NOT consumed by production projects" invariant. Documents what stays inJwtTestHelpers(runner-side concerns) vs. what moved._docs/02_document/modules/tests_unit.mdand_docs/02_document/modules/tests_integration.md— updated to reflect the new dependency onSatelliteProvider.TestSupportand the split of mint logic (TestSupport) vs. runner concerns (JwtTestHelpers).
Deleted
SatelliteProvider.Tests/TestUtilities/JwtTokenFactory.cs(82 lines; content migrated verbatim to TestSupport with thenotBeforeregression-prevention comment expanded). TheTestUtilities/directory itself is retained because it still ownsUavTileImageFactory.cs(out of scope per the AZ-491 task spec § Excluded).
AC Verification
| AC | Status | Evidence |
|---|---|---|
AC-1: Single source of truth — only one JwtSecurityToken( constructor call in test code |
✓ | rg "new JwtSecurityToken" returns only SatelliteProvider.TestSupport/JwtTokenFactory.cs:46 |
| AC-2: Integration tests pass unchanged | Deferred to Step 16 | Call-site signatures preserved (subject explicitly passed via JwtTestHelpers.DefaultSubject); semantics identical |
| AC-3: Unit tests pass unchanged | Deferred to Step 16 | Identical method shapes; only the import statement changed |
| AC-4: Runner-side concerns preserved | ✓ | ResolveSecretOrThrow + AttachDefaultAuthorization + DefaultSubject unchanged in JwtTestHelpers.cs |
| AC-5: Cycle-2 IDX12401 fix preserved | ✓ | The notBefore shift branch is verbatim from the cycle-2 fixes, with an additional regression-prevention comment citing commits f64d0d7 + 11b7074 |
| AC-6: Code-review rule lands | ✓ | .cursor/skills/code-review/SKILL.md Phase 6 now carries a duplicate-helper detection rule with severity, finding-category, and project-name guidance |
Open follow-ups (non-blocking)
- AZ-492 dependency: the perf harness PBI can now consume
SatelliteProvider.TestSupport.JwtTokenFactorydirectly (or via a shell-based JWT minter that mirrors the same parameters). The_docs/_process_leftovers/2026-05-11_perf-pt07-harness.mdleftover entry can be closed by AZ-492's implementation. - AZ-494 dependency: the planned
iss/audissuer/audience parameters onJwtTokenFactory.Createwill be added by AZ-494 (still gated on external admin team input for the actual values). The current signature deliberately keepsissuer: null, audience: nullso all current tests continue to pass against the existingValidateIssuer = false / ValidateAudience = falseAPI configuration. - Test-suite gate: AZ-491 AC-2 + AC-3 require
./scripts/run-tests.sh --fullto be green. Deferred to Step 16 (Final Test Run). Risk for AZ-491 is moderate — the consolidation touches every JWT test call site — but every change is a syntactic rewrite, not a semantic one.
Next Batch: AZ-493 (Integration test DB-reset hook)
AZ-493 is 2 SP. It introduces a startup-side database-reset hook in SatelliteProvider.IntegrationTests/Program.cs to truncate FK-safe tables (tiles, regions, routes, ...) before the test suite runs, eliminating the wallclock-seeded _coordinateCounter workaround that UavUploadTests adopted in cycle 2. Independent of AZ-491; can run in parallel but we keep sequential per implement-skill convention.