mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 14:21:14 +00:00
98cdcd17c1
Append cycle 2 entries to test-spec artifacts (cycle-update mode): * security-tests.md: SEC-05..SEC-09 (AZ-487 JWT 401/403/parity) + SEC-10..SEC-11 (AZ-488 permission + reject-detail leak hygiene). * blackbox-tests.md: BT-13..BT-17 (UAV happy / mixed / multi-source coexistence / same-source UPSERT / rule-ordering) + BT-18 (existing endpoints parity with Bearer token). * resource-limit-tests.md: RL-05..RL-07 (MaxBatchSize, per-item MaxBytes, Kestrel/Form envelope cap). * performance-tests.md: untouched (PT-08 already landed with AZ-488 as Deferred — see _docs/_process_leftovers/2026-05-11_perf-pt07-harness). * traceability-matrix.md: append AC rows for AZ-487 AC-1..AC-8 and AZ-488 AC-1..AC-10 + AC-7a..AC-7e; annotate "No authentication" restriction as superseded by AZ-487+AZ-488; add NFR rows (perf, security, reliability, compatibility) for both tasks; refresh totals (78 tests; 47/47 ACs; 8/8 restrictions). Coverage shape: AZ-487 AC-7 (Swagger Authorize) and the perf NFRs are recorded but not actively measured this commit (manual UI smoke + deferred PT-08 harness, respectively). Co-authored-by: Cursor <cursoragent@cursor.com>
11 KiB
11 KiB
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) |
✓ |
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 | ✓ |
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 (recorded; active perf comparison deferred to Step 15) | ◐ recorded |
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 harness shows aggregate regression. | ◐ recorded |
AZ-487 Security — RequireSignedTokens, RequireExpirationTime, ClockSkew = 30 s, secret ≥ 32 bytes |
AZ-487 task spec § Non-Functional Requirements + Constraints | AuthenticationServiceCollectionExtensionsTests.AddSatelliteJwt_ThrowsOnShortSecret (unit) + SEC-06/SEC-07 (blackbox) |
✓ |
AZ-487 Reliability — Fail-fast on missing / short JWT_SECRET at startup |
AZ-487 task spec § Non-Functional Requirements | SEC-08 (behavioral) + unit AddSatelliteJwt_ThrowsOnMissingSecret |
✓ |
| AZ-488 Performance — Per-item gate cost < 50 ms; p95 batch-of-10 < 2 s | AZ-488 task spec § Non-Functional Requirements | PT-08 (Deferred — harness reuses PT-07 work; tracked in _docs/_process_leftovers/2026-05-11_perf-pt07-harness.md). Active enforcement starts at cycle 2 Step 15. |
◐ recorded (Deferred) |
| 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) and AZ-488 perf NFR (PT-08) are
◐ recorded; active enforcement deferred to cycle 2 Step 15 (Performance Test). Both depend on the shared PT-07 harness expansion (_docs/_process_leftovers/2026-05-11_perf-pt07-harness.md).