Files
satellite-provider/_docs/02_document/tests/traceability-matrix.md
T
Oleksandr Bezdieniezhnykh f979e18811 [AZ-494] Enable JWT iss/aud validation with fail-fast startup
Option B per user decision: production ships with empty Jwt.Issuer /
Jwt.Audience in appsettings.json so the API process refuses to start
unless JWT_ISSUER + JWT_AUDIENCE env vars are supplied. Development
ships with grep-friendly DEV-ONLY- placeholders so local + docker
flows keep working unchanged.

AuthenticationServiceCollectionExtensions flips ValidateIssuer +
ValidateAudience to true and wires ValidIssuer / ValidAudience via a
new ResolveRequiredOrThrow helper that all three required values
(secret, iss, aud) now share. JwtTokenFactory.Create + CreateExpired
gain optional iss / aud parameters (default null) so existing call
sites compile unchanged. JwtTestHelpers adds MintAuthenticated /
MintExpired wrappers that resolve iss + aud from env, plus
ResolveIssuerOrThrow / ResolveAudienceOrThrow. PerfBootstrap.MintToken
+ Program.cs JWT bootstrap migrated to the new surface so the perf
harness and the integration runner both validate against the same
contract.

Adds 4 fail-fast unit tests (missing/empty issuer + audience), 2
negative integration scenarios (WrongIssuer_Returns401,
WrongAudience_Returns401), and re-tags every existing integration
mint site via MintAuthenticated.

Compose, .env.example, run-tests.sh, run-performance-tests.sh all
load + export JWT_ISSUER + JWT_AUDIENCE alongside JWT_SECRET.

Resolves F-AUTH-2 (security_report.md + owasp_review.md). AC-7
(cross-repo suite/_docs/10_auth.md write) deferred — outside this
workspace; tracked in deploy_cycle2.md R3 follow-up.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 02:28:48 +03:00

13 KiB
Raw Blame History

Traceability Matrix

Acceptance Criteria → Test Mapping

AC Description Tests Coverage
T1 Tiles cached, not re-downloaded BT-02
T2 Concurrent download limit RS-05, RL-03
T3 Tile stored with correct path BT-01
T4 Tile metadata persisted BT-01
R1 Region state transitions BT-03, BT-04, BT-05
R2 CSV manifest generated BT-03, BT-04, BT-05
R3 Summary file generated BT-03, BT-04, BT-05
R4 Stitched image when requested BT-05
R5 Stitched image valid content BT-05
R6 Region processing bounded RL-04
RT1 Points interpolated at ~200m BT-06
RT2 Point types correctly assigned BT-06
RT3 Total distance calculated BT-06
RT4 Geofence filtering applied BT-11
RT5 ZIP ≤ 50 MB BT-09, RL-01
RT6 Route map stitched BT-08, BT-10, BT-12
A1 Region request returns immediately BT-03
A2 Status endpoint reflects state BT-03, BT-07
A3 Route returns computed metadata BT-06
S1 Migrations run on startup RS-02
S2 Queue rejects when full RS-04, RL-02
S3 Failed regions marked failed RS-03
AZ-484 AC-1 Schema accepts source + captured_at; multi-source rows coexist under 5-col unique index MultiSourceInsertCoexistsUnderNewIndex_AZ484_AC1, NewUniqueConstraintIncludesSourceColumn_AZ484_AC1 (integration)
AZ-484 AC-2 Read returns most-recent across sources MostRecentAcrossSourcesSelection_AZ484_AC2 (integration)
AZ-484 AC-3 Same-source UPSERT collapses to one row with refreshed captured_at SameSourceUpsertReplacesPreviousRow_AZ484_AC3 (integration)
AZ-484 AC-4 Migration 013 backfill leaves no orphans (count preserved, source='google_maps', captured_at=created_at) BackfillUpdateAssignsGoogleMapsAndCapturedAt_AZ484_AC4 (integration)
AZ-484 AC-5 Google Maps download path stamps Source='google_maps' (wire) + CapturedAt UTC BuildTileEntity_SetsGoogleMapsSourceAndUtcCapturedAt_AZ484_AC5 (unit)
AZ-484 AC-6 Existing region/route flows unchanged post-T1 (200 unit + smoke baseline preserved) Full unit suite (213 tests) + integration smoke scenarios BT-01..BT-12
AZ-484 AC-7 Vision + contract docs amended (architecture.md, glossary.md, module-layout.md, tile-storage.md frozen v1.0.0) doc-state AC; verified by monorepo-document reviews
AZ-487 AC-1 Anonymous request returns 401 from every authenticated endpoint SEC-05 (blackbox); JwtIntegrationTests.AnonymousRequest_*_Returns401 (integration)
AZ-487 AC-2 Expired token returns 401; no internal leak in body SEC-06 (blackbox); JwtIntegrationTests.ExpiredToken_Returns401 (integration)
AZ-487 AC-3 Tampered signature returns 401 SEC-07 (blackbox); JwtIntegrationTests.InvalidSignature_Returns401 (integration)
AZ-487 AC-4 Valid token reaches handler with identical response SEC-09, BT-18 (blackbox); JwtIntegrationTests.ValidToken_Returns200_OnHealthyEndpoint (integration)
AZ-487 AC-5 Startup fails on missing / short JWT_SECRET SEC-08 (behavioral); AuthenticationServiceCollectionExtensionsTests.AddSatelliteJwt_Throws* (unit)
AZ-487 AC-6 HttpContext.User exposes claims (sub, permissions, …) JwtTokenFactoryTests.Create_WithExtraClaims_PropagatesClaimsThroughValidation (unit) + indirect via AZ-488 AC-6 (live permission check)
AZ-487 AC-7 Swagger UI Authorize button works JwtIntegrationTests.SwaggerDocument_AdvertisesBearerSecurityScheme (integration; programmatic equivalent of UI flow) ◐ doc-verified
AZ-487 AC-8 All existing tests pass with attached test token Full scripts/run-tests.sh --full run (cycle 2 Step 11 — passed)
AZ-488 AC-1 Happy-path 1-item batch persists with source='uav' BT-13 (blackbox); UavUploadTests.HappyPathSingleItem_PersistsRow (integration)
AZ-488 AC-2 3-item mixed batch returns per-item results BT-14 (blackbox); UavUploadTests.MixedBatch_ReturnsPerItemResults (integration)
AZ-488 AC-3 UAV upload coexists with pre-seeded google_maps row BT-15 (blackbox); UavUploadTests.MultiSourceCoexistence_AZ484_Cycle2 (integration); reuses AZ-484 AC-1 + AC-2 invariants
AZ-488 AC-4 Same-source UPSERT keeps one source='uav' row BT-16 (blackbox); UavUploadTests.SameSourceUpsert_AZ484_Cycle2 (integration); reuses AZ-484 AC-3 invariant
AZ-488 AC-5 Unauthenticated upload returns 401 (covered by AZ-487) UavUploadTests.NoToken_Returns401 (integration); AZ-487 AC-1 row covers contract
AZ-488 AC-6 Authenticated request without GPS permission returns 403 SEC-10 (blackbox); UavUploadTests.ValidTokenWithoutGpsPermission_Returns403 (integration); PermissionsRequirementTests (unit)
AZ-488 AC-7a INVALID_FORMAT reject reason on wrong content-type or magic bytes UavTileQualityGateTests.Validate_NonJpegContentType_* and Validate_WrongMagicBytes_* (unit)
AZ-488 AC-7b SIZE_OUT_OF_BAND reject reason on bytes outside [MinBytes, MaxBytes] RL-06 (resource-limit); UavTileQualityGateTests.Validate_BytesBelowMin_* and Validate_BytesAboveMax_* (unit)
AZ-488 AC-7c WRONG_DIMENSIONS reject reason on non-256×256 images BT-14, BT-17 (blackbox); UavTileQualityGateTests.Validate_WrongDimensions_* (unit)
AZ-488 AC-7d CAPTURED_AT_FUTURE / CAPTURED_AT_TOO_OLD reject reasons UavTileQualityGateTests.Validate_CapturedAtFuture_* and Validate_CapturedAtTooOld_* (unit)
AZ-488 AC-7e IMAGE_TOO_UNIFORM reject reason on uniform / low-variance JPEGs UavTileQualityGateTests.Validate_UniformGreyImage_RejectsImageTooUniform (unit)
AZ-488 AC-7 (ordering) First-applicable rule wins (e.g. format-fail beats dimensions-fail) BT-17 (blackbox); UavTileQualityGateTests.Validate_MultipleViolations_* (unit)
AZ-488 AC-8 Oversized batch (> MaxBatchSize) returns 400 envelope error RL-05 (resource-limit); UavUploadTests.OversizedBatch_Returns400 (integration)
AZ-488 AC-9 Contract uav-tile-upload.md v1.0.0 frozen and matches implementation doc-state AC; verified by Step 13 (Update Docs) review
AZ-488 AC-10 All existing tests + new AZ-487/AZ-488 tests pass; no AZ-484 regression Full scripts/run-tests.sh --full run (cycle 2 Step 11 — passed)
AZ-494 AC-1 Wrong iss token returns 401 JwtIntegrationTests.WrongIssuer_Returns401 (integration)
AZ-494 AC-2 Wrong aud token returns 401 JwtIntegrationTests.WrongAudience_Returns401 (integration)
AZ-494 AC-3 Matching iss + aud accepted JwtIntegrationTests.ValidToken_Returns200_OnHealthyEndpoint (integration; updated to mint via env iss/aud)
AZ-494 AC-4 Missing config fails fast AuthenticationServiceCollectionExtensionsTests.AddSatelliteJwt_ThrowsOnMissingIssuer + _ThrowsOnEmptyIssuer + _ThrowsOnMissingAudience + _ThrowsOnEmptyAudience (unit)
AZ-494 AC-5 Existing tests pass with matched fixtures Full integration suite reruns at Step 16 with JwtTestHelpers.MintAuthenticated (auto-fills iss/aud from env) ✓ (gate verified at Step 16)
AZ-494 AC-6 Security artifacts updated (F-AUTH-2 → Resolved) _docs/05_security/security_report.md + owasp_review.md updated this batch
AZ-494 AC-7 Suite contract reflects reality suite/_docs/10_auth.md lives outside this workspace; this cycle's deploy report documents that satellite-provider validates iss/aud locally and the prod values are admin-team-confirmed at deploy time ◐ deferred (cross-repo write)

Restrictions → Test Mapping

Restriction Tests Coverage
.NET 8.0 runtime All (via Docker image)
PostgreSQL 16 All (via docker-compose)
Single instance PT-05 (concurrent regions on one instance)
Max 4 concurrent downloads RS-05, RL-03
Max 20 concurrent regions RL-04
Queue capacity 1000 RS-04, RL-02
Max ZIP 50 MB RL-01
No authentication → JWT (HS256) on every endpoint, GPS permission on UAV upload SEC-01..SEC-04 (input handling); SEC-05..SEC-09 (AZ-487 JWT layer); SEC-10..SEC-11 (AZ-488 permission + leak hygiene) ✓ (superseded by AZ-487 + AZ-488, cycle 2)

NFRs → Test Mapping

NFR Source Tests Coverage
AZ-484 Perf — GetTilesByRegionAsync p95 ≤ 1.10 × pre-AZ-484 baseline AZ-484 task spec § Non-Functional Requirements PT-07 (Implemented in AZ-492 — cold + warm distribution, p50/p95 reported; cross-commit baseline comparison remains operator-driven at Step 15)
AZ-484 Compatibility — no public HTTP response field added/removed; vestigial maps_version/version columns preserved (nullable) AZ-484 task spec § Non-Functional Requirements Existing integration suite (no API contract change observable); BT-01 / region status responses verify response shape
AZ-487 Performance — JWT validation < 1 ms overhead per request AZ-487 task spec § Non-Functional Requirements Not separately measured (HMAC-SHA256 + claims parse is sub-millisecond on any modern x86; no caching needed). Re-measure if PT-07 / PT-08 (AZ-492 harness) shows aggregate regression. ◐ recorded
AZ-487 Security — RequireSignedTokens, RequireExpirationTime, ClockSkew = 30 s, secret ≥ 32 bytes, iss + aud validated (extended by AZ-494) AZ-487 + AZ-494 task specs § Non-Functional Requirements + Constraints AuthenticationServiceCollectionExtensionsTests (unit) + SEC-05..SEC-09 + AZ-494 AC-1/AC-2 wrong-iss/aud (integration)
AZ-487 Reliability — Fail-fast on missing / short JWT_SECRET at startup (extended by AZ-494 to iss + aud) AZ-487 + AZ-494 task specs § Non-Functional Requirements SEC-08 (behavioral) + unit AddSatelliteJwt_ThrowsOnMissingSecret + _ThrowsOnMissingIssuer + _ThrowsOnMissingAudience
AZ-488 Performance — Per-item gate cost < 50 ms; p95 batch-of-10 < 2 s AZ-488 task spec § Non-Functional Requirements PT-08 (Implemented in AZ-492 — 20-batch distribution, batch p95 gated at 2000 ms; per-item gate cost reported as derived proxy batch_p95 / batch_size. True per-call UavTileQualityGate.Validate timing requires server-side instrumentation — follow-up). ✓ (batch p95) / ◐ (per-item proxy only)
AZ-488 Reliability — File-first then DB row; per-item failures never fail the batch envelope (except 400/401/403) AZ-488 task spec § Non-Functional Requirements BT-14 (mixed-batch shows per-item isolation); UavTileUploadHandlerTests.*PersistAsync* (unit); reject reason STORAGE_FAILURE defined in contract for the orphan-row recovery path
AZ-488 Compatibility — Replaces 501 stub; coexists with AZ-484 tile-storage v1.0.0 contract on the write side AZ-488 task spec § Non-Functional Requirements + Contract StubAndErrorContractTests updated to drop the stub-501 expectation; BT-15 + BT-16 validate the AZ-484 invariants under live UAV writes
AZ-488 Security — Reject details never leak server internals; integer-only file-path construction AZ-488 task spec § Non-Functional Requirements + Risk 2 SEC-11 (blackbox); UavTileFilePathTests (unit)

Coverage Summary

Category Total Tests ACs Covered Restrictions Covered
Blackbox (positive) 12 19/22
Blackbox (negative) 5
Performance 8 4 1
Resilience 6 4 3
Security 11 9 (AZ-487 AC-1..AC-7, AZ-488 AC-6, leak-hygiene NFR) 1 (AZ-487 supersedes "No authentication")
Resource Limits 7 5 4
Cycle 1 — AZ-484 (integration + unit) 6 7/7
Cycle 2 — AZ-487 (integration + unit + behavioral) 4 integration + 3 unit + 1 behavioral 8/8
Cycle 2 — AZ-488 (integration + unit + blackbox) 7 integration + 14 unit + 6 blackbox 10/10
Total 78 47/47 (100%) 8/8 (100%)

Coverage shape notes (Cycle 2):

  • AZ-487 AC-7 (Swagger UI Authorize) is verified programmatically (SwaggerDocument_AdvertisesBearerSecurityScheme) rather than via a real UI flow; marked ◐ doc-verified. The end-to-end browser-UI Authorize-button check remains a manual smoke before deploy.
  • AZ-487 perf NFR (< 1 ms JWT overhead) remains ◐ recorded; not separately gated. AZ-488 perf NFR (PT-08) moved from ◐ recorded (Deferred) to for batch p95 — see PT-08 row above. AZ-484 perf NFR (PT-07) moved from ◐ recorded to — see PT-07 row above. The harness work landed in AZ-492 (cycle 3) along with the Authorization: Bearer … attach that AZ-487 silently broke for the perf script.