diff --git a/SatelliteProvider.IntegrationTests/UavUploadTests.cs b/SatelliteProvider.IntegrationTests/UavUploadTests.cs index 0ec006e..0f80150 100644 --- a/SatelliteProvider.IntegrationTests/UavUploadTests.cs +++ b/SatelliteProvider.IntegrationTests/UavUploadTests.cs @@ -511,9 +511,14 @@ public static class UavUploadTests private static (double Latitude, double Longitude) NextTestCoordinate() { // Spread test coordinates far enough apart to fall into distinct tile cells - // so concurrent runs don't collide on the per-source unique index. + // so concurrent runs don't collide on the per-source unique index. Wrap on + // 40_000-cell axes so the result always stays strictly inside the + // OSM-valid ranges enforced by UavTileMetadataValidator (AZ-810): + // lat in [50.0, 70.0), lon in [10.0, 40.0). var n = Interlocked.Increment(ref _coordinateCounter); - return (60.0 + n * 0.0005, 30.0 + n * 0.0005); + var lat = 50.0 + ((uint)n % 40_000u) * 0.0005; + var lon = 10.0 + ((uint)n % 60_000u) * 0.0005; + return (lat, lon); } private static async Task CountUavRowsAsync(string connectionString, double latitude, double longitude) diff --git a/SatelliteProvider.IntegrationTests/UavUploadValidationTests.cs b/SatelliteProvider.IntegrationTests/UavUploadValidationTests.cs index ef28532..dfac479 100644 --- a/SatelliteProvider.IntegrationTests/UavUploadValidationTests.cs +++ b/SatelliteProvider.IntegrationTests/UavUploadValidationTests.cs @@ -646,13 +646,20 @@ public static class UavUploadValidationTests return stream.ToArray(); } - // Same coordinate-seeding strategy as UavUploadTests so AZ-810 happy-path - // inserts don't collide with the AZ-488 suite when both run back-to-back. - private static int _coordinateCounter = (int)((DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond) % 1_000_000) + 5_000_000; + // Use a southern-hemisphere range that does NOT overlap UavUploadTests' + // northern range ([50,70) x [10,40)). Non-overlap (not counter offset) is + // what guarantees the AZ-488 and AZ-810 suites don't collide on the + // per-source UNIQUE index when both run against the same DB. Wrap on + // 40_000-cell axes so the result always stays strictly inside the + // OSM-valid ranges enforced by UavTileMetadataValidator: + // lat in [-70.0, -50.0), lon in [-40.0, -10.0). + private static int _coordinateCounter = (int)((DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond) % 1_000_000); private static (double Latitude, double Longitude) NextTestCoordinate() { var n = Interlocked.Increment(ref _coordinateCounter); - return (60.0 + n * 0.0005, 30.0 + n * 0.0005); + var lat = -50.0 - ((uint)n % 40_000u) * 0.0005; + var lon = -10.0 - ((uint)n % 60_000u) * 0.0005; + return (lat, lon); } } diff --git a/_docs/03_implementation/batch_04_cycle8_report.md b/_docs/03_implementation/batch_04_cycle8_report.md index dff17fc..6636fd3 100644 --- a/_docs/03_implementation/batch_04_cycle8_report.md +++ b/_docs/03_implementation/batch_04_cycle8_report.md @@ -22,7 +22,7 @@ | AC-6 | `_docs/02_document/modules/api_program.md::POST /api/satellite/upload` endpoint description updated; `Api/Validators` section gained entries for `UavTileBatchMetadataPayloadValidator`, `UavTileMetadataValidator`, `UavUploadValidationFilter`; `Common/DTO (AZ-488)` updated to note `[JsonRequired]` additions; DI Registration list gained the `UavUploadValidationFilter` transient registration. | | AC-7 | `[JsonRequired]` annotations on `UavTileMetadata` + `UavTileBatchMetadataPayload` propagate to Swashbuckle's OpenAPI as `required: [latitude, longitude, tileZoom, tileSizeMeters, capturedAt]` and `required: [items]`. Endpoint chain in `Program.cs` declares `.Accepts("multipart/form-data")` + `.Produces(200)` + `.ProducesProblem(400)`. Explicit OpenAPI range annotations omitted per existing project pattern (FluentValidation messages convey the range to API consumers via `ValidationProblemDetails.errors`). | | AC-8 | Probe script `scripts/probe_upload_validation.sh` — happy + 14 failure modes via `curl`. Reuses `probe_route_validation.sh` structure (JWT mint, status-code assertion, `--exit-on-fail` driver). | -| AC-9 | No regression in AZ-488: validator rules all align with the legal payloads `UavUploadTests` already sends (lat/lon in range, tileZoom = 18, tileSizeMeters = 200.0, capturedAt = `UtcNow` or recent past, items.Count ∈ [1, 100]). The defence-in-depth check (`IUavTileQualityGate` per-item rejects post-validator) is unchanged and still runs in the handler. Verified by tracing each AZ-488 test payload's metadata shape against `UavTileMetadataValidator` + `UavTileBatchMetadataPayloadValidator` rules. Full integration-test pass gating deferred to autodev Step 11. | +| AC-9 | No regression in AZ-488: validator rules align with the field shape AZ-488 tests send (`tileZoom = 18`, `tileSizeMeters = 200.0`, `capturedAt = UtcNow` or recent past, `items.Count ∈ [1, 100]`, no unknown fields). The defence-in-depth check (`IUavTileQualityGate` per-item rejects post-validator) is unchanged and still runs in the handler. **Step 11 caveat (resolved):** the integration test run exposed a latent bug in `UavUploadTests.NextTestCoordinate` — the pre-existing seed `(Ticks/TicksPerSecond) % 1_000_000` produced latitudes far above 90° (e.g. n=200_000 → lat=160), which previously slipped through silently (no validator, no DB constraint) but AZ-810 correctly rejects. Fixed in `UavUploadTests.cs` (clamped to lat ∈ [50,70), lon ∈ [10,40)) and `UavUploadValidationTests.cs` (clamped to lat ∈ [-70,-50), lon ∈ [-40,-10) — non-overlapping range for per-source UNIQUE-index safety). No production code change; AZ-810 validator behaviour unchanged. | ## Code Review Verdict: PASS_WITH_WARNINGS diff --git a/_docs/LESSONS.md b/_docs/LESSONS.md index c1f0935..6bb29cc 100644 --- a/_docs/LESSONS.md +++ b/_docs/LESSONS.md @@ -37,6 +37,8 @@ If the enum's wire string happens to match a member name case-insensitively (e.g ## Ring buffer (last 15 entries — newest at top) +- [2026-05-23] [process] When verifying a "no-regression" AC for an input-validation change ("AZ-NNN does not break existing tests"), the only sound evidence is a green integration-test run — tracing fixture variables back to their generators in source is insufficient because helpers can produce values outside the new bounds and previously slipped through silently when no validator existed; document the standard as "verified by reading source" → unconfirmed, "verified by full test run" → confirmed, and gate the batch report's AC table on the latter before the implement skill closes the batch (cycle 8: AZ-810 batch_04 AC-9 claimed "no AZ-488 regression" based on tracing `latitude = coord.Latitude` in test source, but `NextTestCoordinate` seeded by `(Ticks/TicksPerSecond) % 1_000_000` produced lat far above 90° at runtime; the false-PASS only surfaced at autodev Step 11 when the integration test run returned HTTP 400 from the new validator on the AZ-488 happy path). + Source: _docs/03_implementation/batch_04_cycle8_report.md (AC-9 row) - [2026-05-22] [process] When the implement skill ships a cycle's batch commit without writing `_docs/03_implementation/implementation_report_*_cycle{N}.md`, downstream skills (test-spec cycle-update, document task mode, retrospective Step 1) must fall back to reading the cycle's task specs in `_docs/02_tasks/done/` plus the commit body via `git log --grep='[AZ-...]'` — codify the fallback in those skills' instructions instead of leaving it as per-cycle improvisation, because the implicit contract between Step 10 and Steps 11-17 broke silently this cycle and only succeeded because every downstream skill happened to be robust enough to substitute (cycle 7: AZ-794+AZ-795+AZ-796 shipped as commit `865dfdb` with no report artifact; doc-skill auto-walked the diff, test-spec read the task specs, retrospective wrote from the deploy + security + perf reports — all worked, but the contract was never formal). Source: _docs/06_metrics/retro_2026-05-22_cycle7.md - [2026-05-22] [testing] When a strict-validation layer ships (`JsonSerializerOptions.UnmappedMemberHandling.Disallow`, FluentValidation rules, explicit DTO `[JsonRequired]`), expect the project's own integration tests to surface latent bugs the prior lenient defaults had been masking — silent PascalCase fallback property names, out-of-range fixture coordinates, wrong-cased JSON keys; correct them in the same PR or the test suite goes red and the strict layer looks like a regression instead of the bug-finder it is (cycle 7: `IdempotentPostTests.RoutePoint` had been posting `{"Lat":...}` against a `[JsonPropertyName("lat")]` DTO for months; the new strict deserializer caught it and the 2-line payload fix landed alongside the strict layer). diff --git a/_docs/_autodev_state.md b/_docs/_autodev_state.md index 5372538..54102fd 100644 --- a/_docs/_autodev_state.md +++ b/_docs/_autodev_state.md @@ -4,12 +4,12 @@ flow: existing-code step: 11 name: Run Tests -status: not_started +status: in_progress sub_step: - phase: 0 - name: awaiting-invocation - detail: "" -retry_count: 0 + phase: 2 + name: run-tests + detail: "re-run after coord-clamp fix" +retry_count: 1 cycle: 8 tracker: jira auto_push: true