mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-27 08:31:13 +00:00
[AZ-1124] Cycle 12 closure docs and cycle 13 task slate
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -18,7 +18,7 @@ public static class GrpcTestHelpers
|
||||
var caCertPath = ResolveCaCertPath();
|
||||
if (!string.IsNullOrEmpty(caCertPath))
|
||||
{
|
||||
var caCert = X509Certificate2.CreateFromPemFile(caCertPath);
|
||||
var caCert = X509Certificate2.CreateFromPem(File.ReadAllText(caCertPath));
|
||||
handler.SslOptions = new SslClientAuthenticationOptions
|
||||
{
|
||||
RemoteCertificateValidationCallback = (_, certificate, _, errors) =>
|
||||
|
||||
@@ -24,7 +24,7 @@ Console application that runs end-to-end integration tests against a live API in
|
||||
### Supporting Classes
|
||||
- `Models.cs` — HTTP response DTOs for deserialization
|
||||
- `RouteTestHelpers.cs` — shared utilities (wait-for-completion polling, geofence polygon builders, test data)
|
||||
- `Program.cs` — test runner entry point (handles `--smoke` / `--full` mode selection, `--keep-state` opt-out flag, default-token issuance via `JwtTokenFactory`, the AZ-493 DB-reset hook, and the AZ-492 `--mint-only` / `--gen-uav-fixture` perf-bootstrap subcommands that short-circuit before any HTTP / DB setup)
|
||||
- `Program.cs` — test runner entry point (handles `--smoke` / `--full` mode selection, `--keep-state` opt-out flag, default-token issuance via `JwtTokenFactory`, the AZ-493 DB-reset hook, and the AZ-492 perf-bootstrap subcommands `--mint-only` / `--gen-uav-fixture` plus AZ-1124 `--run-pt10` that short-circuit before any HTTP / DB setup)
|
||||
- `JwtTestHelpers.cs` — runner-side JWT concerns:
|
||||
- `ResolveSecretOrThrow` reads `JWT_SECRET` env var with size validation
|
||||
- `ResolveIssuerOrThrow` / `ResolveAudienceOrThrow` (AZ-494) read `JWT_ISSUER` / `JWT_AUDIENCE` with fail-fast contract
|
||||
@@ -34,7 +34,7 @@ Console application that runs end-to-end integration tests against a live API in
|
||||
- `DefaultSubject = "integration-tests"` is the canonical runner subject value
|
||||
- Token *minting* lives in the shared `SatelliteProvider.TestSupport.JwtTokenFactory` (AZ-491) — runner-side concerns (env reads, HttpClient mutation, the iss/aud-aware mint wrapper) deliberately stay here.
|
||||
- `IntegrationTestDatabaseReset.cs` (AZ-493) — instance class with a single `EnsureCleanStateAsync()` method that truncates the integration-test target tables in FK-safe order. Guarded via `SatelliteProvider.TestSupport.IntegrationTestResetGuard` (env + Host allowlist) so it cannot run against a non-test database.
|
||||
- `PerfBootstrap.cs` (AZ-492) — static helpers for the perf harness bootstrap subcommands. `MintToken()` mints a 4-hour HS256 token with subject `perf-tests` and a `permissions: GPS` claim via the canonical `SatelliteProvider.TestSupport.JwtTokenFactory.Create`; `GenerateUavFixture(args)` writes a 256×256 random-noise JPEG via `SixLabors.ImageSharp` to the path passed on the CLI. Invoked from `scripts/run-performance-tests.sh` via `dotnet <SatelliteProvider.IntegrationTests.dll> --mint-only` and `--gen-uav-fixture <path>`.
|
||||
- `PerfBootstrap.cs` (AZ-492; PT-10 extended by AZ-1124) — static helpers for the perf harness bootstrap subcommands. `MintToken()` mints a 4-hour HS256 token with subject `perf-tests` and a `permissions: GPS` claim via the canonical `SatelliteProvider.TestSupport.JwtTokenFactory.Create`; `GenerateUavFixture(args)` writes a 256×256 random-noise JPEG via `SixLabors.ImageSharp` to the path passed on the CLI; `RunPt10Async()` opens a gRPC channel via `GrpcTestHelpers`, streams `DeliverRouteTiles` for `PERF_REPEAT_COUNT` cold iterations plus one slow-consumer pass (`PERF_PT10_SLOW_MS` delay between events), and prints `PT10_*` metric lines to stdout for `scripts/run-performance-tests.sh` to gate. Invoked via `dotnet <SatelliteProvider.IntegrationTests.dll> --mint-only`, `--gen-uav-fixture <path>`, and `--run-pt10`.
|
||||
- `ProblemDetailsAssertions.cs` (added cycle 7 — AZ-795) — shared static helpers for asserting RFC 7807 ProblemDetails bodies on integration-test responses. `ReadProblemDetailsAsync(HttpResponseMessage, label)` deserialises the response body into a `JsonElement` with helpful failure messages when the content-type / shape doesn't match. `AssertProblemDetails(problem, expectedStatus, label)` asserts the base ProblemDetails shape (`type`, `title`, `status`). `AssertValidationProblem(problem, expectedStatus, label, expectedErrorPath?, expectedErrorContains?)` extends the base assertion to require the `errors` map per `error-shape.md` Inv-2 and optionally checks a specific field path / message substring. Consumed by `TileInventoryValidationTests`; designed to be reused by every future per-endpoint child task under AZ-795.
|
||||
- `GrpcTestHelpers.cs` (added cycle 9 — AZ-1075) — gRPC client factory (`CreateClient` over TLS with dev cert trust), request builders, stream collector, and `ExpectInvalidArgumentAsync` assertion helper. Consumed exclusively by `RouteTileDeliveryGrpcTests`.
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ Existing baseline (pre-cycle-2) test classes cover `TileService`, `RegionService
|
||||
- `UavTileQualityGateTests` — one happy path + ≥ 1 reject path per rule (Rule 1 INVALID_FORMAT × 2, Rule 2 SIZE_OUT_OF_BAND × 2, Rule 3 WRONG_DIMENSIONS × 1, Rule 4 CAPTURED_AT_FUTURE / _TOO_OLD × 2, Rule 5 IMAGE_TOO_UNIFORM × 1) + rule-ordering determinism. Uses a `FixedTimeProvider` for Rule-4 isolation and `UavTileImageFactory` for deterministic JPEG fixtures.
|
||||
- `UavTileUploadHandlerTests` — end-to-end with a mocked `ITileRepository`. Cycle-2 baseline: 1-item happy path, 3-item mixed batch (file written + `InsertAsync` called only for accepted), per-source UPSERT pass-through. AZ-503 additions: `HandleAsync_TwoFlightsSameCell_ProduceDistinctIdsAndPathsButSameLocationHash` (multi-flight coexistence with shared `location_hash`); `HandleAsync_IdenticalUpload_ProducesIdenticalIdAndDeterministicContentSha` (idempotent re-insert preserves deterministic `id` + `content_sha256`). AZ-1113 (cycle 10): `HandleAsync_InvalidMetadataJson_ReturnsEnvelopeError` — defense-in-depth metadata parse returns static envelope error (no `ex.Message` echo).
|
||||
|
||||
### AZ-1124 — PT-10 gRPC stream perf harness (cycle 12)
|
||||
- `PerfBootstrapPt10Tests` — static-review + percentile helper coverage for the perf bootstrap: `Percentile_MatchesHarnessFormula_AZ1124_AC2` (p50/p95 math matches shell gate), `PerfScript_DoesNotInlineJwtMint_AZ1124_AC6` (grep `run-performance-tests.sh` for `--run-pt10` and absence of inline `JwtSecurityToken` mint), `Program_DispatchesRunPt10Subcommand_AZ1124_AC1` (grep `Program.cs` for `--run-pt10` dispatch).
|
||||
|
||||
### AZ-1113 — REST 400 error message sanitization (cycle 10)
|
||||
- `GlobalExceptionHandlerTests` — extends AZ-795/353 coverage: `TryHandleAsync_DeserializationFailure_WritesValidationProblemDetailsWithJsonPath_AZ795` asserts `errors[]` values are `"The field value is invalid."` with no `.NET` type leak; `TryHandleAsync_BadHttpRequestExceptionWithoutJson_UsesStaticDetail` asserts non-JSON bind failures emit `detail: "The request could not be processed."` (existing 5xx sanitization tests unchanged).
|
||||
- `Authentication/PermissionsRequirementTests` — `PermissionsAuthorizationHandler` correctly accepts a `permissions` claim shaped as a single string OR as a JSON array, rejects when the requested permission is absent, and short-circuits when the principal has no `permissions` claim at all.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Ripple Log — Cycle 12
|
||||
|
||||
Tasks: AZ-1124 (PT-10 gRPC stream perf)
|
||||
|
||||
- `_docs/02_document/tests/performance-tests.md` — PT-10 scenario block (changed by AZ-1124)
|
||||
- `_docs/02_document/tests/traceability-matrix.md` — AZ-1124 AC-1..AC-6 + NFR row + cycle-12 coverage notes (test-spec sync)
|
||||
- `scripts/run-performance-tests.sh` — PT-10 section + header PT-01..PT-10 (changed by AZ-1124)
|
||||
- `SatelliteProvider.IntegrationTests/PerfBootstrap.cs` + `Program.cs` — `--run-pt10` bootstrap (changed by AZ-1124)
|
||||
- `SatelliteProvider.Tests/PerfBootstrapPt10Tests.cs` — AC-6 static-review unit tests (changed by AZ-1124)
|
||||
- `_docs/02_document/modules/tests_integration.md` — `--run-pt10` subcommand docs (Step 13)
|
||||
- `_docs/02_document/modules/tests_unit.md` — `PerfBootstrapPt10Tests` entry (Step 13)
|
||||
- `_docs/02_document/tests/environment.md` — `PERF_PT10_SLOW_MS` / PT-10 env vars (Step 13)
|
||||
|
||||
No new blackbox or security scenarios. gRPC functional surface unchanged (BT-32 remains binding).
|
||||
@@ -46,7 +46,17 @@
|
||||
**Hardware dependencies found**: None
|
||||
**Execution method**: `docker-compose -f docker-compose.yml -f docker-compose.tests.yml up --build --abort-on-container-exit`
|
||||
|
||||
**Performance tests** (Step 15 / `scripts/run-performance-tests.sh`): start the API with the perf overlay when host port 5433 is occupied — `docker compose -f docker-compose.yml -f docker-compose.perf.yml up -d --build`. Details: [containerization.md](../deployment/containerization.md#compose-overlays-dev--test--perf).
|
||||
**Performance tests** (Step 15 / `scripts/run-performance-tests.sh`): start the API with the perf overlay when host port 5433 is occupied — `docker compose -f docker-compose.yml -f docker-compose.perf.yml up -d --build`. Details: [containerization.md](../deployment/containerization.md#compose-overlays-dev--test--perf). PT-10 (`DeliverRouteTiles` gRPC stream) runs via `dotnet SatelliteProvider.IntegrationTests --run-pt10` inside the script; host-side default `API_URL=https://localhost:18980` with TLS trust via `./certs/api.crt` (`PERF_CA_CERT` override). Harness knobs:
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
|----------|---------|---------|
|
||||
| `PERF_REPEAT_COUNT` | 20 | Cold iterations for PT-07/PT-08/PT-10 distribution |
|
||||
| `PERF_UAV_BATCH_SIZE` | 10 | Items per PT-08 upload batch |
|
||||
| `PERF_PT10_SLOW_MS` | 50 | Delay between gRPC stream events on the slow-consumer sub-check (PT-10) |
|
||||
| `PERF_JWT_TOKEN` | (minted in-script) | Pre-minted Bearer token; skips `--mint-only` when set |
|
||||
| `PERF_CA_CERT` | `$PROJECT_ROOT/certs/api.crt` | TLS trust anchor for host-side perf probes (REST + gRPC) |
|
||||
|
||||
See [performance-tests.md](performance-tests.md) § PT-10 for pass thresholds.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
|----------|-------------|-------------|---------------------|
|
||||
| Blackbox (positive) | 12 | 19/22 | — |
|
||||
| Blackbox (negative) | 5 | — | — |
|
||||
| Performance | 9 | 5 | 1 |
|
||||
| Performance | 10 | 6 | 1 |
|
||||
| Resilience | 6 | 4 | 3 |
|
||||
| Security | 14 | 9 (AZ-487 AC-1..AC-7, AZ-488 AC-6, leak-hygiene NFR) + 3 (AZ-1113 AC-1..AC-3) | 1 (AZ-487 supersedes "No authentication") |
|
||||
| Resource Limits | 7 | 5 | 4 |
|
||||
@@ -219,7 +219,8 @@
|
||||
| Cycle 9 — AZ-1074 + AZ-1075 gRPC RouteTileDelivery (integration + unit + blackbox) | 1 integration file (`RouteTileDeliveryGrpcTests`) + orchestrator unit tests + 1 blackbox (BT-32 with 6 sub-cases) + `SatelliteProvider.GrpcContracts` | 7/7 (AZ-1074 AC-1..AC-4, AZ-1075 AC-1..AC-3) | — |
|
||||
| Cycle 10 — AZ-1113 REST 400 error message sanitization (integration + unit + blackbox + contract patch) | 3 integration assertion paths (inventory deserializer, latlon bind, UAV metadata) + 3 unit methods (`GlobalExceptionHandlerTests` ×2, `UavTileUploadHandlerTests` ×1) + 1 blackbox (BT-33 with 3 sub-cases) + 3 security (SEC-14..SEC-16) + `error-shape.md` v1.0.1 patch | 5/5 in-scope (AZ-1113 AC-1..AC-5) | — |
|
||||
| Cycle 11 — AZ-1123 perf compose documentation (deployment + test env docs) | doc-only (`containerization.md` compose overlays, `environment.md` perf cross-link) | 3/3 in-scope (AZ-1123 AC-1..AC-3); doc-verified at Step 13 | — |
|
||||
| **Total** | **170** | **124/124 in-scope (100%); 2 AZ-504 ACs gated at Step 15; 10 prior-cycle ACs doc-verified at Step 13 (2 cycle-7 + 8 cycle-8); 2 advisory non-tested (cycle-8 AZ-809 AC-9/AC-10)** | **8/8 (100%)** |
|
||||
| Cycle 12 — AZ-1124 PT-10 gRPC stream perf (perf harness + unit) | 1 perf (PT-10) + 3 unit (`PerfBootstrapPt10Tests`) + integration bootstrap (`SatelliteProvider.IntegrationTests --run-pt10`) | 6/6 in-scope (AZ-1124 AC-1..AC-6); 1 AC gated at Step 15 (AC-3); 1 doc-verified at Step 13 (AC-5) | — |
|
||||
| **Total** | **173** | **130/130 in-scope (100%); 3 ACs gated at Step 15 (2 AZ-504 + 1 AZ-1124 AC-3); 11 prior-cycle ACs doc-verified at Step 13 (2 cycle-7 + 8 cycle-8 + 1 AZ-1124 AC-5 pending); 2 advisory non-tested (cycle-8 AZ-809 AC-9/AC-10)** | **8/8 (100%)** |
|
||||
|
||||
**Coverage shape notes (Cycle 5 — AZ-503 foundation):**
|
||||
- AZ-503 was split mid-cycle (Option C, autodev Step 10 batch 2): 7 of 12 original ACs land here; 5 (AC-5, AC-6, AC-9, AC-10, AC-12) are deferred to AZ-505 with a `Blocks` link in Jira and an entry in `_docs/02_tasks/_dependencies_table.md`. The deferred rows above are marked `◐ deferred → AZ-505` so the matrix surfaces the scope boundary explicitly.
|
||||
@@ -285,11 +286,17 @@
|
||||
| AZ-1123 AC-3 | Integration (`docker-compose.tests.yml` only) vs perf overlay distinction documented | doc-state AC; verified at Step 13 (both deployment + test env docs) | ✓ |
|
||||
| AZ-1124 AC-1 | PT-10 exercises real gRPC `DeliverRouteTiles` stream with Bearer metadata | PT-10 (performance); `SatelliteProvider.IntegrationTests --run-pt10` (integration bootstrap) | ✓ |
|
||||
| AZ-1124 AC-2 | PT-10 reports `first_batch_ms` + `total_stream_ms` p50/p95 | PT-10 stdout metrics (`PT10_*` lines) | ✓ |
|
||||
| AZ-1124 AC-3 | PT-10 threshold gate (`p95(first_batch_ms) ≤ 30000`, `p95(total_stream_ms) ≤ 120000`) | PT-10 shell gate in `scripts/run-performance-tests.sh` | ◐ gate at Step 15 |
|
||||
| AZ-1124 AC-3 | PT-10 threshold gate (`p95(first_batch_ms) ≤ 30000`, `p95(total_stream_ms) ≤ 120000`) | PT-10 shell gate in `scripts/run-performance-tests.sh`; cycle 12 Step 15 measured p95=48ms / 48ms | ✓ |
|
||||
| AZ-1124 AC-4 | PT-10 slow-consumer smoke completes without `DeliveryError` | PT-10 `PT10_SLOW_CONSUMER=PASS` sub-check | ✓ |
|
||||
| AZ-1124 AC-5 | PT-10 documented in `performance-tests.md`; gRPC stream perf no longer Unverified | doc-state AC; verified at Step 13 | ◐ doc-verified at Step 13 |
|
||||
| AZ-1124 AC-5 | PT-10 documented in `performance-tests.md`; gRPC stream perf no longer Unverified | doc-state AC; verified at Step 13 | ✓ |
|
||||
| AZ-1124 AC-6 | PT-10 reuses `PerfBootstrap` / `JwtTokenFactory` / `GrpcTestHelpers` — no third JWT mint in shell | `PerfBootstrapPt10Tests` (unit — static review) | ✓ |
|
||||
|
||||
**Coverage shape notes (Cycle 12 — AZ-1124 PT-10 gRPC stream perf):**
|
||||
- First gRPC **performance** scenario — closes cycle 9–11 retro carry-over where BT-32 / AZ-1074 functional coverage left stream latency `Unverified`. PT-10 reuses `GrpcTestHelpers`, `PerfBootstrap` JWT mint, and the standard 2-waypoint fixture; no new production RPC or proto change.
|
||||
- AC-3 (p95 thresholds) and AC-5 (spec/traceability doc state) follow the established split: threshold gate at Step 15; doc-state AC verified at Step 13 (`performance-tests.md` § PT-10, module docs for `--run-pt10`).
|
||||
- `PerfBootstrapPt10Tests` (3 methods) satisfies AC-6 static-review gate — no third JWT mint in the shell script.
|
||||
- Cycle-update rule check: no NFR conflicts. PT-10 cold-path budgets align with PT-01 family (GM download on fresh volume).
|
||||
|
||||
**Coverage shape notes (Cycle 11 — AZ-1123 perf compose documentation):**
|
||||
- Documentation-only cycle — no new runtime tests, blackbox scenarios, perf thresholds, or security findings. Cycle-update adds traceability rows only; existing Step 11 smoke (450/450) is regression evidence.
|
||||
- Closes cycle 9/10 retro action to document `docker-compose.perf.yml` (file landed cycle 10; playbook landed cycle 11).
|
||||
|
||||
@@ -263,12 +263,13 @@ Step 9 cycle 9: 2 tasks created (AZ-1074 = 5 pts, AZ-1075 = 3 pts) — total 8 p
|
||||
Step 9 cycle 10: 1 task created (AZ-1113 = 2 pts) — REST 400 error message sanitization (F-AZ795-1/2, F-AZ810-1). Child of AZ-795.
|
||||
Step 9 cycle 11: 1 task created (AZ-1123 = 1 pt) — document `docker-compose.perf.yml` host-port conflict playbook (cycle 10 retro action).
|
||||
Step 9 cycle 12: 1 task created (AZ-1124 = 3 pts) — PT-10 gRPC `DeliverRouteTiles` stream perf scenario (cycle 9–11 retro carry-over).
|
||||
Step 9 cycle 13: 1 task created (AZ-1126 = 2 pts) — `DateTime` → `DateTimeOffset` on `UavTileMetadata.capturedAt` (F-AZ810-2). Child of AZ-795.
|
||||
|
||||
### Step 9 cycle 12 (PT-10 gRPC stream perf — AZ-1124)
|
||||
### Step 9 cycle 13 (capturedAt DateTimeOffset — AZ-1126)
|
||||
|
||||
| Task | Depends On | Points | Status |
|
||||
|------|-----------|--------|--------|
|
||||
| AZ-1124 PT-10 gRPC stream perf scenario | AZ-1074, AZ-1075, AZ-492 | 3 | Todo |
|
||||
| AZ-1126 capturedAt DateTimeOffset (F-AZ810-2) | AZ-810, AZ-488 | 2 | Todo |
|
||||
|
||||
## Coverage Verification
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# Migrate UAV upload capturedAt to DateTimeOffset
|
||||
|
||||
**Task**: AZ-1126_captured_at_datetimeoffset
|
||||
**Name**: Migrate UavTileMetadata.capturedAt to DateTimeOffset (F-AZ810-2)
|
||||
**Description**: Close security carry-over F-AZ810-2 by typing `UavTileMetadata.CapturedAt` as `DateTimeOffset` instead of `DateTime`, eliminating ambiguous `DateTimeKind.Unspecified` handling on the UAV upload metadata input path.
|
||||
**Complexity**: 2 points
|
||||
**Dependencies**: AZ-810 (HARD — metadata validation layer); AZ-488 (original upload endpoint)
|
||||
**Component**: SatelliteProvider.Common (UavTileMetadata) + SatelliteProvider.Api (validators) + SatelliteProvider.Services.TileDownloader (quality gate + upload handler)
|
||||
**Tracker**: AZ-1126
|
||||
**Epic**: AZ-795
|
||||
|
||||
## Problem
|
||||
|
||||
Security finding F-AZ810-2 (cycle 8, open through cycle 12) flags that `UavTileMetadata.CapturedAt` is typed `DateTime` rather than `DateTimeOffset`. `DateTime` accepts `DateTimeKind.Unspecified` values that deserialize from offset-less ISO-8601 strings, forcing manual `Kind` normalization in the upload handler and quality gate. This is a time-handling correctness gap that can skew freshness-window checks in non-UTC dev environments.
|
||||
|
||||
## Outcome
|
||||
|
||||
- `capturedAt` on the UAV upload metadata input is unambiguously UTC-aware at the type level
|
||||
- Offset-less or `Unspecified` timestamps are rejected before persistence
|
||||
- F-AZ810-2 is marked resolved in the next security audit cycle
|
||||
- Wire compatibility preserved for clients sending ISO-8601 UTC with explicit offset (`Z` or `+00:00`)
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Change `UavTileMetadata.CapturedAt` from `DateTime` to `DateTimeOffset`
|
||||
- Update FluentValidation rules, quality gate, and upload handler to compare via UTC without manual `Kind` branching
|
||||
- Reject offset-less / ambiguous `capturedAt` values with HTTP 400
|
||||
- Unit tests for the new rejection path and existing freshness-window rules
|
||||
- Integration test proving offset-less `capturedAt` is rejected
|
||||
- Patch `_docs/02_document/contracts/api/uav-tile-upload.md` 1.2.0 → 1.2.1 (change log + clarify offset requirement)
|
||||
|
||||
### Excluded
|
||||
|
||||
- `TileInventoryEntry.CapturedAt` response field (remains `DateTime?` — DB read path)
|
||||
- `TileEntity.CapturedAt` persistence layer type
|
||||
- Changes to gRPC tile delivery or other non-UAV-upload surfaces
|
||||
- MAJOR contract version bump (wire JSON shape unchanged for compliant clients)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Type migration**
|
||||
Given the UAV upload metadata DTO
|
||||
When deserialized from JSON
|
||||
Then `CapturedAt` is `DateTimeOffset` and freshness comparisons use UTC without manual `DateTimeKind` normalization
|
||||
|
||||
**AC-2: Reject ambiguous timestamps**
|
||||
Given a UAV upload batch with `capturedAt` lacking an explicit UTC offset (offset-less ISO string)
|
||||
When POST `/api/satellite/upload`
|
||||
Then HTTP 400 with a validation or deserialization error referencing `capturedAt`
|
||||
|
||||
**AC-3: Backward-compatible UTC clients**
|
||||
Given a UAV upload batch with `capturedAt` as ISO-8601 UTC (`...Z` or `...+00:00`)
|
||||
When POST `/api/satellite/upload` with otherwise valid payload
|
||||
Then HTTP 200 (or the same non-timestamp rejection as before timestamp validation)
|
||||
|
||||
**AC-4: Contract patch**
|
||||
Given the implementation is complete
|
||||
When `uav-tile-upload.md` is reviewed
|
||||
Then version is 1.2.1 with a change-log entry documenting the offset requirement and F-AZ810-2 closure
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
**Compatibility**
|
||||
- Compliant clients already sending `Z`-suffixed timestamps must not break
|
||||
|
||||
**Security**
|
||||
- Closes F-AZ810-2 (Low / informational time-handling finding)
|
||||
|
||||
## Unit Tests
|
||||
|
||||
| AC Ref | What to Test | Required Outcome |
|
||||
|--------|-------------|-----------------|
|
||||
| AC-1 | `UavTileMetadataValidator` freshness window with `DateTimeOffset` | Future/too-old rules still fire |
|
||||
| AC-2 | Deserializer or validator with offset-less `capturedAt` | Rejected |
|
||||
| AC-1 | `UavTileQualityGate` captured-at rules | Still accept/reject correctly |
|
||||
|
||||
## Blackbox Tests
|
||||
|
||||
| AC Ref | Initial Data/Conditions | What to Test | Expected Behavior | NFR References |
|
||||
|--------|------------------------|-------------|-------------------|----------------|
|
||||
| AC-2 | Valid JPEG + metadata with `capturedAt: "2026-06-26T12:00:00"` (no offset) | POST `/api/satellite/upload` | HTTP 400 mentioning `capturedAt` | — |
|
||||
| AC-3 | Valid JPEG + metadata with `capturedAt` as `DateTime.UtcNow.ToString("o")` | POST `/api/satellite/upload` | HTTP 200 (happy path unchanged) | Compatibility |
|
||||
|
||||
## Constraints
|
||||
|
||||
- Must remain a child of epic AZ-795 strict-validation theme
|
||||
- No breaking wire change for offset-aware clients
|
||||
|
||||
## Risks & Mitigation
|
||||
|
||||
**Risk 1: Client sends offset-less timestamps**
|
||||
- *Risk*: Legitimate clients using `"2026-06-26T12:00:00"` without `Z` start failing
|
||||
- *Mitigation*: Contract patch documents requirement; rejection is intentional per F-AZ810-2
|
||||
|
||||
## Contract
|
||||
|
||||
This task patches the producer contract at `_docs/02_document/contracts/api/uav-tile-upload.md` (1.2.0 → 1.2.1).
|
||||
|
||||
Consumers: `gps-denied-onboard`, mission planner UI, any UAV upload client.
|
||||
|
||||
### Document Dependencies
|
||||
|
||||
- `_docs/02_document/contracts/api/uav-tile-upload.md` v1.2.0 (patch to 1.2.1)
|
||||
- `_docs/02_document/contracts/api/error-shape.md` v1.0.0 (unchanged)
|
||||
@@ -0,0 +1,26 @@
|
||||
# Performance Report — Cycle 12
|
||||
|
||||
**Date**: 2026-06-26
|
||||
**Cycle**: 12 (AZ-1124 PT-10 gRPC stream perf)
|
||||
**Runner**: `scripts/run-performance-tests.sh` (default: `PERF_REPEAT_COUNT=20`, `PERF_UAV_BATCH_SIZE=10`, `PERF_PT10_SLOW_MS=50`)
|
||||
**Stack**: `docker compose -f docker-compose.yml -f docker-compose.perf.yml up -d --build`
|
||||
**API_URL**: `https://localhost:18980`
|
||||
**Verdict**: **PASS** (11/11 thresholds; exit 0)
|
||||
|
||||
## PT-10 (new — AZ-1124)
|
||||
|
||||
| Metric | p50 | p95 | Threshold | Verdict |
|
||||
|--------|-----|-----|-----------|---------|
|
||||
| first_batch_ms | 43 ms | 48 ms | ≤ 30000 ms | Pass |
|
||||
| total_stream_ms | 43 ms | 48 ms | ≤ 120000 ms | Pass |
|
||||
| slow-consumer | — | — | completes without DeliveryError | Pass |
|
||||
|
||||
Warm compose volume — iterations 2–20 served cached tiles; iteration 1 cold path 150 ms first batch.
|
||||
|
||||
## REST scenarios (PT-01..PT-08)
|
||||
|
||||
All passed on warm volume. Notable: PT-07 cold p95=106 ms, warm p95=65 ms (warm faster).
|
||||
|
||||
## Step 15 fix applied during gate
|
||||
|
||||
First run failed PT-10: `CreateFromPemFile(api.crt)` rejected cert-only PEM on host-side gRPC channel. Fixed in `GrpcTestHelpers.CreateChannel` — load CA via `X509Certificate2.CreateFromPem(File.ReadAllText(...))`. Re-run green.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Retrospective — Cycle 12 (2026-06-26)
|
||||
|
||||
**Tasks**: AZ-1124 (PT-10 gRPC stream perf, 3 SP). **1 task, 3 SP, 1 batch.**
|
||||
**Mode**: cycle-end. Steps 14 (Security) and 16/16.5 (Deploy/Release) **skipped**.
|
||||
**Previous retro**: `retro_2026-06-25_cycle11.md`
|
||||
|
||||
## Implementation Summary
|
||||
|
||||
| Metric | Cycle 12 | Δ vs cycle 11 |
|
||||
|--------|----------|---------------|
|
||||
| Tasks implemented | **1** | unchanged |
|
||||
| Total complexity delivered | **3 SP** | +2 SP |
|
||||
| Blocked tasks | **0** | unchanged |
|
||||
|
||||
## Quality
|
||||
|
||||
| Gate | Result |
|
||||
|------|--------|
|
||||
| Code review | PASS (batch_01_cycle12) |
|
||||
| Step 11 full suite | **PASS** (exit 0, ~21 min) |
|
||||
| Step 15 perf (PT-01..PT-10) | **PASS** after `GrpcTestHelpers` PEM fix |
|
||||
| Security | Skipped (harness-only; no prod surface) |
|
||||
|
||||
## Cycle 12 delta
|
||||
|
||||
- **PT-10 landed** — first gRPC perf scenario; closes cycle 9–11 retro Action #2.
|
||||
- **Step 15 caught TLS bug** — `CreateFromPemFile(api.crt)` fails on cert-only PEM for host-side gRPC; fixed to `CreateFromPem(text)`.
|
||||
|
||||
## Top 3 Improvement Actions (cycle 13 candidates)
|
||||
|
||||
1. **F-AZ810-2** `DateTime` → `DateTimeOffset` on `capturedAt` (~1 SP) — carry-over
|
||||
2. **Align `environment.md` integration command** with `run-tests.sh` (`docker-compose.tests.yml` only) (~0.5 SP) — carry-over
|
||||
3. **Promote PT-09** inventory inline test into shell harness if production SLO tightens (~2 SP) — optional
|
||||
|
||||
## Cycle 12 Verdict
|
||||
|
||||
**Successful perf-harness cycle** — AZ-1124 delivered PT-10 end-to-end; gRPC stream NFR no longer Unverified. Minor TLS loading fix required at Step 15 gate.
|
||||
@@ -37,6 +37,8 @@ If the enum's wire string happens to match a member name case-insensitively (e.g
|
||||
|
||||
- [2026-06-25] [process] Documentation-only autodev cycles should still run Step 12 traceability rows (doc-verified ACs) and Step 13 ripple logs, while Steps 14–15 are appropriately skipped when no code surface changes — avoids empty cycle artifacts without running meaningless security/perf gates (cycle 11: AZ-1123).
|
||||
Source: _docs/06_metrics/retro_2026-06-25_cycle11.md
|
||||
- [2026-06-26] [tooling] Host-side gRPC perf (`--run-pt10`) must load cert-only PEM CA files via `X509Certificate2.CreateFromPem(text)` — `CreateFromPemFile(api.crt)` throws when no private key is present; REST curl `--cacert` tolerates cert-only but .NET gRPC channel setup does not (cycle 12: PT-10 Step 15 first run failed, one-line fix in `GrpcTestHelpers`).
|
||||
Source: _docs/06_metrics/perf_2026-06-26_cycle12.md
|
||||
## Ring buffer (last 15 entries — newest at top)
|
||||
|
||||
- [2026-06-25] [testing] PT-07 cold-vs-warm region latency is sensitive to outlier cold p95 on a warm compose volume — the perf gate should drain the region queue before the warm pass and accept warm p50 < cold p50 when p95 is within noise (cycle 10: two marginal PT-07 FAILs before harness fix; AZ-1113 did not touch region paths).
|
||||
|
||||
Reference in New Issue
Block a user