[AZ-1074] [AZ-1075] Cycle 9 closeout: security, tests, metrics
ci/woodpecker/push/01-test Pipeline failed
ci/woodpecker/push/02-build-push unknown status

Resolve F-AZ1074-1/2 (collection caps, generic gRPC internal errors).
Standalone integration compose stack, docs, security audit, perf and retro.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-06-25 17:32:14 +03:00
parent 7633134a8a
commit 7ed780b063
22 changed files with 618 additions and 40 deletions
+19
View File
@@ -393,3 +393,22 @@ Cycle 8 extends the AZ-795 shared validation infrastructure (FluentValidation +
**AC trace**: AZ-811 AC-1 (5 rules + ProblemDetails shape), AC-2 (happy path), AC-9 (the novel unknown-query-param envelope filter is documented in `_docs/02_document/modules/api_program.md` for reuse).
**Notes**: This is the first endpoint to need a generic **unknown-query-param rejection** layer — ASP.NET's default model binder silently ignores unknown query parameters (parallel to `UnmappedMemberHandling.Disallow` for JSON bodies, but no built-in equivalent exists for query strings). The new `UnknownQueryParameterEndpointFilter` introspects the route's declared parameters and rejects any extra keys. Sub-cases `4b` and `4c` exercise this filter: `4b` proves the pre-AZ-811 wire format (`?Latitude=&Longitude=&ZoomLevel=`) that silently fell back to `lat=0, lon=0, zoom=0` now fails fast with HTTP 400 naming all three unknown keys; `4c` proves the same path catches arbitrary hostile / typo keys. The filter is designed for reuse by any future query-param endpoint (AZ-811 AC-9).
## BT-32: gRPC RouteTileDelivery — Happy Path + Validation + REST Consistency
**Trigger**: `RouteTileDelivery.DeliverRouteTiles(DeliverRouteTilesRequest)` over gRPC (TLS to `https://api:8080` inside docker-compose; metadata `authorization: Bearer <JWT>`). Contract: `_docs/02_document/contracts/c11_tilemanager/tile_provision_grpc.md` + `SatelliteProvider.GrpcContracts/tile_provision.proto`.
**Precondition**: API + Postgres up via `docker-compose.tests.yml`; JWT env vars set; dev TLS cert trusted in integration-tests container.
**Expected stream sequence**: `RouteManifest` → zero or more `TileChunk``DeliveryComplete` (or `DeliveryError` on failure paths).
| # | Scenario | Trigger excerpt | Expected | Test method |
|---|----------|-----------------|----------|-------------|
| pos | Happy path | 2 waypoints (48.276067,37.384458) → (48.270740,37.374029), regionSize=500, zoom=18 | `RouteManifest` with `to_deliver ≥ 0`; at least one `TileChunk` when `to_deliver > 0`; terminal `DeliveryComplete` | `RunHappyPath` |
| 1 | Single waypoint | `waypoints` count = 1 | gRPC `StatusCode.InvalidArgument` before stream completes | `RunInvalidRequests` ("Single waypoint route") |
| 2 | Lat out of range | first waypoint `lat=91` | gRPC `StatusCode.InvalidArgument` | `RunInvalidRequests` ("Latitude out of range") |
| 3 | Zoom out of range | `zoom=99` | gRPC `StatusCode.InvalidArgument` | `RunInvalidRequests` ("Zoom out of allowed range") |
| 4 | Backpressure safe | same as happy path; consumer delays 50 ms between stream events | every `TileChunk.jpeg` starts with `FF D8`; `content_sha256` matches SHA-256 of JPEG bytes | `RunBackpressureSafe` |
| 5 | REST consistency | REST `POST /api/satellite/route` with `requestMaps=true` for same geometry; then gRPC stream for same corridor | gRPC tile keys `(z,x,y)` overlap REST route CSV tile keys by ≥ 1 | `RunRestConsistency` |
**Pass criterion**: All sub-cases pass in full integration mode (`INTEGRATION_TESTS_MODE=full`). No `DeliveryError` on happy path / backpressure path. Invalid requests fail with `InvalidArgument` (not `Internal` / `Unknown`). REST overlap count > 0.
**AC trace**: AZ-1074 AC-1..AC-4; AZ-1075 AC-1..AC-3.
**Notes**: gRPC is additive — REST route endpoints (BT-06..BT-12) remain unchanged. Cache-reuse (AZ-1074 AC-2) is covered structurally by the orchestrator unit tests (`RouteTileDeliveryOrchestratorTests.DeliverAsync_CachedTileOnDisk_EmitsBatchWithoutDownload`) plus the integration happy path reusing tiles seeded by prior REST runs in the same compose volume. Consumer-side tests (gps-denied-onboard AZ-1076) are out of scope.
@@ -263,3 +263,18 @@
- AZ-809 ACs 9 + 10 are **advisory** (surfaced for parent-suite team decision, not implemented or tested this cycle). Matrix marks them `◐ advisory (not tested)`. They're recorded so the next cycle / parent-suite review sees them without having to re-discover them from the task spec. AC-9: `RequestRegionRequest.sizeMeters` vs `CreateRouteRequest.regionSizeMeters` naming inconsistency. AC-10: input `points: [{lat, lon}]` vs output `points: [{latitude, longitude}]` round-trip asymmetry on the route endpoint. Either keep + document, or harmonize in a follow-up MAJOR contract bump for both — parent-suite team's call.
- AZ-810 AC-9 (no AZ-488 regression) has a **process annotation**: cycle 8's batch_04 report originally claimed AC-9 "verified by tracing each AZ-488 test payload's metadata shape against the new rules" without running the integration suite. That verification was a false-PASS — the suite was actually red on the AZ-488 happy path because `UavUploadTests.NextTestCoordinate()` produced lat > 90° (a pre-existing latent bug masked by the absence of any validator before AZ-810). The bug was fixed by clamping the test-data generator to OSM-valid ranges in commit `b763da3` and AC-9 is now bound to the green full-suite re-run, not to source tracing. Process lesson recorded in `_docs/LESSONS.md` (2026-05-23).
- Cycle-update rule check: no NFR conflicts. Range bounds (`lat ∈ [-90, 90]`, `lon ∈ [-180, 180]`, `zoom ∈ [0, 22]`, `tileSizeMeters > 0`) are reaffirmed across all 4 endpoints — they were never previously contested. The error-shape contract (`error-shape.md` v1.0.0 from cycle 7) is reused unchanged.
| AZ-1074 AC-1 | Happy path streams tiles — `RouteManifest` + ≥1 `TileChunk` + `DeliveryComplete` | BT-32 pos (blackbox); `RouteTileDeliveryGrpcTests.RunHappyPath` (integration) | ✓ |
| AZ-1074 AC-2 | Cached tiles served without redundant Google Maps download | `RouteTileDeliveryOrchestratorTests.DeliverAsync_CachedTileOnDisk_EmitsBatchWithoutDownload` (unit); BT-32 pos reuses compose volume cache (integration) | ✓ |
| AZ-1074 AC-3 | Invalid route / coordinates rejected with `INVALID_ARGUMENT` | BT-32 sub-cases 13 (blackbox); `RouteTileDeliveryGrpcTests.RunInvalidRequests` (integration) | ✓ |
| AZ-1074 AC-4 | Slow consumer — tile bytes not corrupted | BT-32 sub-case 4 (blackbox); `RouteTileDeliveryGrpcTests.RunBackpressureSafe` (integration) | ✓ |
| AZ-1075 AC-1 | gRPC happy-path passes in docker-compose full run | Full `scripts/run-tests.sh --full` / `docker-compose.tests.yml` (cycle 9 Step 11 — passed) | ✓ |
| AZ-1075 AC-2 | Each invalid variant returns expected gRPC status | BT-32 sub-cases 13; `RouteTileDeliveryGrpcTests.RunInvalidRequests` | ✓ |
| AZ-1075 AC-3 | REST and gRPC tile metadata consistent for same route | BT-32 sub-case 5; `RouteTileDeliveryGrpcTests.RunRestConsistency` | ✓ |
**Coverage shape notes (Cycle 9 — AZ-1074 + AZ-1075 gRPC RouteTileDelivery):**
- Cycle 9 adds the first gRPC blackbox surface alongside the existing REST suite. BT-32 is the binding blackbox spec; integration coverage lives in `RouteTileDeliveryGrpcTests` wired into both smoke and full suites via `Program.cs`.
- Proto source of truth moved to `SatelliteProvider.GrpcContracts/tile_provision.proto` (GrpcServices=Both); contract doc at `_docs/02_document/contracts/c11_tilemanager/tile_provision_grpc.md`.
- Cycle 9 Step 11 initially failed integration startup due to host port 5433 conflict with sibling project `fleet-viewer-dev-db`. Fixed by making `docker-compose.tests.yml` self-contained (no host port publishing — compose-internal networking only) and pointing `scripts/run-tests.sh` at that file alone for integration runs. Unit count is now 448 (includes orchestrator + gRPC validation tests).
- No perf / security NFRs declared in AZ-1074/1075 task specs beyond existing JWT-on-gRPC-metadata (inherits AZ-487/494 invariants). Load testing explicitly excluded.
- Cycle-update rule check: no NFR conflicts.