mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 18:21:14 +00:00
[AZ-809] Strict validation for POST /api/satellite/route
Third concrete child of AZ-795 (cycle 8 batch 3). FluentValidation +
[JsonRequired] + UnmappedMemberHandling.Disallow combine to reject every
malformed payload at the API boundary with RFC 7807 ValidationProblemDetails.
Validators (SatelliteProvider.Api/Validators/, all new)
- CreateRouteRequestValidator: id non-empty, name/description length,
regionSizeMeters/zoomLevel ranges, points count [2, 500], cross-field
createTilesZip => requestMaps. Chains RoutePointValidator (per-point)
and GeofencePolygonValidator (per-polygon, guarded by When(Geofences != null)).
OverridePropertyName("geofences.polygons") on the geofences chain so
FluentValidation's default leaf-only key policy doesn't drop the parent
path on deep expressions like req.Geofences!.Polygons.
- RoutePointValidator: lat/lon ranges; OverridePropertyName("lat"/"lon")
chained AFTER InclusiveBetween (the extension is defined on
IRuleBuilderOptions<T, TProperty>, so the generic type is only
inferable after the first concrete rule) so error keys match the
wire format (`points[i].lat`) rather than the C# property name
(`points[i].latitude`).
- GeofencePolygonValidator: per-corner range checks via private nested
GeoCornerValidator; cross-field NW.Lat > SE.Lat and NW.Lon < SE.Lon
invariants emit at errors["geofences.polygons[i].northWest"].
DTOs (SatelliteProvider.Common/DTO/, [JsonRequired] additions only)
- CreateRouteRequest: id, name, regionSizeMeters, zoomLevel, points,
requestMaps, createTilesZip
- RoutePoint: Latitude, Longitude
- GeofencePolygon: NorthWest, SouthEast; Geofences: Polygons
- GeoPoint: Lat, Lon
Tests
- Unit: 26 methods total — 16 in CreateRouteRequestValidatorTests, 6 in
GeofencePolygonValidatorTests, 4 in RoutePointValidatorTests. Each
RuleFor/RuleForEach chain has at least one positive + one negative case.
- Integration: CreateRouteValidationTests.cs — 16 methods (happy + 15
failure modes) wired into smoke + full suites. Covers empty body,
missing/zero id, empty name, out-of-range regionSizeMeters/zoomLevel,
points count < 2, per-point lat/lon out-of-range, geofence invariants,
missing requestMaps, cross-field createTilesZip, unknown root field,
nested type mismatch.
- Manual probe: scripts/probe_route_validation.sh curl-exercises every
failure mode end-to-end + happy path.
Docs
- New contract _docs/02_document/contracts/api/route-creation.md v1.0.0
with nested DTO chain, invariants, per-field test cases table, and
advisories on the legacy service-layer RouteValidator + the
input/output RoutePoint vs RoutePointDto naming asymmetry.
- system-flows.md F4 sequence diagram extended with the validation-filter
branch; preconditions + error scenarios reference the new contract.
- modules/api_program.md: CreateRoute handler section added; Api/Validators
bumped to AZ-808/AZ-809/AZ-811.
- modules/common_dtos.md: DTO descriptions updated with [JsonRequired]
annotations and constraint summaries.
- tests/blackbox-tests.md BT-06/BT-N03/BT-N04/BT-N05 align with the new
wire format and named error keys.
- tests/security-tests.md SEC-04 references GlobalExceptionHandler's
JsonException branch + AZ-353 correlationId.
- _docs/03_implementation/batch_03_cycle8_report.md + reviews/batch_03_cycle8_review.md
(PASS_WITH_NOTES — F1 Low: OverridePropertyName documented inline,
F2 + F3 Info: pre-existing advisories for follow-up).
Smoke green (mode=smoke, exit 0). AZ-809 transitioned to In Testing on Jira.
Task file moved to _docs/02_tasks/done/.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# Batch Report
|
||||
|
||||
**Batch**: 03 (cycle 8)
|
||||
**Tasks**: AZ-809 (POST /api/satellite/route strict validation)
|
||||
**Date**: 2026-05-22
|
||||
|
||||
## Task Results
|
||||
|
||||
| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|
||||
|------|--------|---------------|-------|-------------|--------|
|
||||
| AZ-809_route_endpoint_validation | Done | 18 files (8 new) | smoke pass (mode=smoke, exit 0); 16 integration tests + 26 validator unit tests added | 9/9 ACs covered | 1 Low (in-flight `OverridePropertyName` on deep expression — root-caused, documented, captured as advisory) |
|
||||
|
||||
## AC Test Coverage (9/9 ACs)
|
||||
|
||||
| AC | Coverage |
|
||||
|----|----------|
|
||||
| AC-1 | All 14 documented rules enforced. Deserializer: missing `[JsonRequired]` axes (`id`, `name`, `regionSizeMeters`, `zoomLevel`, `points`, `requestMaps`, `createTilesZip`, per-point `lat`/`lon`, per-polygon `northWest`/`southEast`, per-corner `lat`/`lon`, `geofences.polygons`) + unknown-field rejection + type-mismatch. FluentValidation: non-zero `id`, name+description length, `regionSizeMeters` ∈ \[100, 10000\], `zoomLevel` ∈ \[0, 22\], `points` count ∈ \[2, 500\], per-point lat/lon ranges, per-polygon NW-of-SE invariants, cross-field `createTilesZip ⇒ requestMaps`. Each rule has at least one positive + one negative integration test. |
|
||||
| AC-2 | Happy path: `CreateRouteValidationTests.HappyPath_Returns200` (well-formed body, requestMaps=false → no background side effects) returns HTTP 200. Smoke green. |
|
||||
| AC-3 | Wired via `.WithValidation<CreateRouteRequest>()` + `.Accepts<>` + `.Produces<>` + `.ProducesProblem(400)` in `Program.cs` MapPost chain. |
|
||||
| AC-4 | `[JsonRequired]` added to every non-optional axis on `CreateRouteRequest`, `RoutePoint`, `Geofences`, `GeofencePolygon`, `GeoPoint`. Tested by `EmptyBody_Returns400`, `MissingId_Returns400`, `MissingRequestMaps_Returns400`, and the nested type-mismatch `PointsLatTypeMismatch_Returns400`. |
|
||||
| AC-5 | Unit tests in `SatelliteProvider.Tests/Validators/` — `CreateRouteRequestValidatorTests.cs` (16 methods), `RoutePointValidatorTests.cs` (4 methods), `GeofencePolygonValidatorTests.cs` (6 methods). Cover each rule with positive + negative cases. |
|
||||
| AC-6 | Integration tests `SatelliteProvider.IntegrationTests/CreateRouteValidationTests.cs` — 16 methods covering happy path + 15 failure modes (one per rule); all green in smoke. |
|
||||
| AC-7 | New contract `_docs/02_document/contracts/api/route-creation.md` v1.0.0 published. References `error-shape.md` v1.0.0 + the nested DTO chain. Documents the `RoutePoint` (input `lat`/`lon`) vs `RoutePointDto` (output `latitude`/`longitude`) naming asymmetry as an advisory. |
|
||||
| AC-8 | Probe script `scripts/probe_route_validation.sh` covers happy + each failure mode via `curl`. |
|
||||
| AC-9 | `CreateRouteRequestValidator` chains `RoutePointValidator` (via `RuleForEach`) and `GeofencePolygonValidator` (via `RuleForEach` inside `When(Geofences is not null)`). Cross-field invariants on the root (`createTilesZip ⇒ requestMaps`) and per-polygon (`NW.Lat > SE.Lat`, `NW.Lon < SE.Lon`). Defence-in-depth: the legacy `RouteValidator` in `SatelliteProvider.Services.RouteManagement` still runs in the service layer as a backstop; advisory clean-up documented in `route-creation.md`. |
|
||||
|
||||
## Code Review Verdict: PASS_WITH_NOTES
|
||||
See `_docs/03_implementation/reviews/batch_03_cycle8_review.md` for the single Low finding (deep-expression `OverridePropertyName`, root-caused and documented inline).
|
||||
|
||||
## Auto-Fix Attempts: 1 (mid-batch)
|
||||
- Initial `RoutePointValidator` used `OverridePropertyName("lat")` BEFORE `.InclusiveBetween()`. Build failed with `CS0411: cannot infer type arguments for OverridePropertyName<T, TProperty>` because FluentValidation's `OverridePropertyName` extension is defined on `IRuleBuilderOptions<T, TProperty>` — the type only becomes inferable after the first concrete rule (which supplies `TProperty`). Reordered to chain after `InclusiveBetween().WithMessage(...).OverridePropertyName(...)`. Documented in-file so the chain order is not "simplified" by a future reader.
|
||||
- Initial `CreateRouteRequestValidator` used `RuleFor(req => req.Geofences!.Polygons)` and `RuleForEach(req => req.Geofences!.Polygons)` without `OverridePropertyName`. Smoke run unit tests failed: error keys came out as `polygons` and `polygons[0].northWest` (leaf-only), not the full wire path `geofences.polygons` / `geofences.polygons[0].northWest`. Root cause: FluentValidation's default property-name policy drops the parent on deep member expressions. Fix: chain `.OverridePropertyName("geofences.polygons")` on both `RuleFor` and `RuleForEach` rules; documented inline. Smoke re-run after fix: all green.
|
||||
|
||||
## Stuck Agents: None
|
||||
|
||||
## Files Modified
|
||||
|
||||
### AZ-809 (route-creation validator)
|
||||
| Path | Kind |
|
||||
|------|------|
|
||||
| `SatelliteProvider.Common/DTO/CreateRouteRequest.cs` | `[JsonRequired]` on id/name/regionSizeMeters/zoomLevel/points/requestMaps/createTilesZip |
|
||||
| `SatelliteProvider.Common/DTO/RoutePoint.cs` | `[JsonRequired]` on Latitude/Longitude |
|
||||
| `SatelliteProvider.Common/DTO/GeofencePolygon.cs` | `[JsonRequired]` on NorthWest/SouthEast in `GeofencePolygon`; `[JsonRequired]` on `Polygons` in `Geofences` |
|
||||
| `SatelliteProvider.Common/DTO/GeoPoint.cs` | `[JsonRequired]` on Lat/Lon |
|
||||
| `SatelliteProvider.Api/Validators/CreateRouteRequestValidator.cs` | **NEW** — root validator with `RuleForEach` chaining + `OverridePropertyName` on the geofences chain |
|
||||
| `SatelliteProvider.Api/Validators/RoutePointValidator.cs` | **NEW** — per-point lat/lon range; `OverridePropertyName("lat"/"lon")` aligns error keys with the wire format |
|
||||
| `SatelliteProvider.Api/Validators/GeofencePolygonValidator.cs` | **NEW** — per-polygon corner range checks + NW-of-SE invariants |
|
||||
| `SatelliteProvider.Api/Program.cs` | `.WithValidation<CreateRouteRequest>()` + `.Accepts<>` + `.Produces<>` + `.ProducesProblem(400)` on the route POST endpoint |
|
||||
| `SatelliteProvider.Tests/Validators/CreateRouteRequestValidatorTests.cs` | **NEW** — 16 unit tests |
|
||||
| `SatelliteProvider.Tests/Validators/RoutePointValidatorTests.cs` | **NEW** — 4 unit tests |
|
||||
| `SatelliteProvider.Tests/Validators/GeofencePolygonValidatorTests.cs` | **NEW** — 6 unit tests |
|
||||
| `SatelliteProvider.IntegrationTests/CreateRouteValidationTests.cs` | **NEW** — 16 integration tests (happy + 15 failure modes) |
|
||||
| `SatelliteProvider.IntegrationTests/Program.cs` | Wired into smoke + full suites |
|
||||
| `scripts/probe_route_validation.sh` | **NEW** — curl probes for every failure mode + happy path |
|
||||
| `_docs/02_document/contracts/api/route-creation.md` | **NEW** v1.0.0 — contract doc with nested DTO chain + test-cases table |
|
||||
| `_docs/02_document/modules/api_program.md` | CreateRoute handler + Api/Validators (added AZ-809 section) |
|
||||
| `_docs/02_document/modules/common_dtos.md` | DTO descriptions updated with `[JsonRequired]` annotations |
|
||||
| `_docs/02_document/system-flows.md` | F4 (Route Creation) sequence diagram + Preconditions + Error Scenarios |
|
||||
| `_docs/02_document/tests/blackbox-tests.md` | BT-06 wire format clarification; BT-N03/BT-N04/BT-N05 references AZ-809 + error-shape contract |
|
||||
| `_docs/02_document/tests/security-tests.md` | SEC-04 references AZ-809 + GlobalExceptionHandler path |
|
||||
|
||||
## Tracker
|
||||
|
||||
- AZ-809: To Do → In Progress (batch 3 start) → **In Testing** (post-smoke).
|
||||
|
||||
## Next Batch
|
||||
Batch 4: AZ-810 — UAV upload metadata validator (multipart envelope). The envelope shape is different from batch 2/3 (multipart vs JSON body), so the validator wiring is via the existing per-item `IUavTileQualityGate` + a new envelope-level FluentValidation rule set on `UavTileBatchMetadataPayload`. Defer non-trivial design choices (whether to keep the cycle-2 in-handler envelope checks as-is or migrate them) to the implementation step.
|
||||
@@ -0,0 +1,134 @@
|
||||
# Code Review Report
|
||||
|
||||
**Batch**: 03 (cycle 8)
|
||||
**Tasks**: AZ-809 (POST /api/satellite/route strict validation)
|
||||
**Date**: 2026-05-22
|
||||
**Verdict**: PASS_WITH_NOTES
|
||||
|
||||
## Findings
|
||||
|
||||
| # | Severity | Category | File:Line | Title |
|
||||
|---|----------|----------|-----------|-------|
|
||||
| 1 | Low | API alignment | `SatelliteProvider.Api/Validators/CreateRouteRequestValidator.cs:68-77` | `OverridePropertyName` is required on deep expressions because FluentValidation drops the parent path on `req.Geofences!.Polygons` |
|
||||
| 2 | Info | Defence-in-depth | `SatelliteProvider.Services.RouteManagement/RouteValidator.cs` (existing) | Service-layer `RouteValidator` is now strictly weaker than the cycle-8 `CreateRouteRequestValidator` and could be deleted, but is retained as a defence-in-depth backstop |
|
||||
| 3 | Info | Wire-shape asymmetry | `SatelliteProvider.Common/DTO/RoutePoint.cs` (input) vs `SatelliteProvider.Common/DTO/RoutePointDto.cs` (output) | The input wire uses short OSM `lat`/`lon`; the response wire uses long `latitude`/`longitude`. Pre-existing — AZ-809 documented but did not unify |
|
||||
|
||||
### Finding Details
|
||||
|
||||
**F1: `OverridePropertyName` is mandatory on the geofences chain** (Low / API alignment)
|
||||
- Location: `SatelliteProvider.Api/Validators/CreateRouteRequestValidator.cs:68-77` and `SatelliteProvider.Api/Validators/RoutePointValidator.cs:30-40`.
|
||||
- Description: FluentValidation's default property-name policy drops the parent on deep member expressions like `req => req.Geofences!.Polygons`. Without `OverridePropertyName("geofences.polygons")`, the error keys emitted are leaf-only (`polygons`, `polygons[0].northWest`) instead of the full wire path the spec mandates (`geofences.polygons`, `geofences.polygons[0].northWest`). The fix lives in code AND in a comment explaining WHY; without the comment a future reader would "simplify" the rule chain and silently break wire compatibility. Same pattern applies to `RoutePointValidator` where C# property `Latitude` must surface as wire `lat` — handled by `OverridePropertyName` chained AFTER the first concrete rule (a generic-type-inference quirk: the extension is defined on `IRuleBuilderOptions<T, TProperty>`, which only becomes inferable after the first `.InclusiveBetween()` etc.).
|
||||
- Suggestion: NONE — rationale captured in code comments AND in `api_program.md::Api/Validators` so the next reader cannot break it by accident.
|
||||
- Task: AZ-809
|
||||
|
||||
**F2: Service-layer `RouteValidator` retained as defence-in-depth** (Info / Defence-in-depth)
|
||||
- Location: `SatelliteProvider.Services.RouteManagement/RouteValidator.cs`.
|
||||
- Description: The pre-cycle-8 service-layer validator (`RouteValidator`) covered approximately the same surface as the new `CreateRouteRequestValidator` (id non-empty, points count ≥ 2, geofence corner sanity). Now that the API layer rejects every invalid request before the service runs, `RouteValidator` is strictly redundant for HTTP-driven paths. It is, however, also called from the route processing service (background queue) where some bypass path could in principle smuggle an invalid payload — keeping it as a backstop costs ~30 lines and one extra unit-test pass. Removal is tracked as an advisory in `route-creation.md` ("Validator Cleanup Advisory") so the next cycle can decide whether to consolidate.
|
||||
- Suggestion: Defer to a follow-up PBI. Do NOT delete in this batch.
|
||||
- Task: AZ-809
|
||||
|
||||
**F3: Input/output naming asymmetry on route points** (Info / Wire-shape asymmetry)
|
||||
- Location: `SatelliteProvider.Common/DTO/RoutePoint.cs` (input) vs `SatelliteProvider.Common/DTO/RoutePointDto.cs` (output).
|
||||
- Description: Request points use `[JsonPropertyName("lat")]` / `[JsonPropertyName("lon")]`; response points serialize the underlying C# `Latitude`/`Longitude` properties verbatim. This asymmetry existed before cycle 8. AZ-809 documents it in `route-creation.md` v1.0.0 and `common_dtos.md`, but does not unify because changing the response wire would be a breaking change to existing clients of `GET /api/satellite/route/{id}`. Tracked as an advisory.
|
||||
- Suggestion: Open a successor PBI to consider unifying via a `lat`/`lon` rename on `RoutePointDto` (would be a v2.0.0 of `route-creation.md`).
|
||||
- Task: AZ-809
|
||||
|
||||
## Phase Summary
|
||||
|
||||
| Phase | Outcome |
|
||||
|-------|---------|
|
||||
| 1. Context Loading | Read AZ-809 spec, `_docs/02_document/contracts/api/region-request.md` (batch-2 pattern), `_docs/02_document/contracts/api/error-shape.md` (failure shape), and the cycle-7 + cycle-8 (batch-2) validation infra. The route endpoint differs from batch-2 endpoints because it has nested DTOs (RoutePoint, Geofences/GeofencePolygon/GeoPoint) requiring child validators. |
|
||||
| 2. Spec Compliance | All 9 ACs ✓. New `CreateRouteRequestValidator` covers the 14 documented rules across deserializer-layer + validator-layer. Nested `RoutePointValidator` + `GeofencePolygonValidator` co-validators wired via `RuleForEach.SetValidator(...)`. Cross-field invariants enforced at both the root (`createTilesZip ⇒ requestMaps`) and per-polygon (`NW.Lat > SE.Lat`, `NW.Lon < SE.Lon`). New contract `route-creation.md` v1.0.0 published. 16 integration tests + 26 unit tests cover happy path + each rule + missing-required + type-mismatch + cross-field. Probe script exercises every failure mode via `curl`. |
|
||||
| 3. Code Quality | Mechanical patterns followed; three new validators are minimal and SRP-clean. The `OverridePropertyName` requirement on deep expressions (F1) is non-obvious and was discovered via failing unit tests + diagnostic instrumentation; the workaround is captured in both code comments and module docs so it cannot be silently regressed. `RoutePointValidator` and `GeofencePolygonValidator` are file-private — inner `GeoCornerValidator` is nested inside `GeofencePolygonValidator` because the polygon corners are its only consumer; if a future sibling endpoint needs point-shape validation, the spec says to promote and rename. |
|
||||
| 4. Security | Validators run BEFORE any DB work (route persistence, intermediate-point computation, queue enqueue). The cross-field invariants prevent NaN-geometry payloads from reaching the GeoUtils interpolator (which is not designed for NW=SE corners). No SQL injection vectors, no hardcoded secrets, no PII in logs. JWT auth retained on the endpoint. Probe script tests `?debug=1` / extra root fields → all rejected. |
|
||||
| 5. Performance | Validators run synchronously against in-memory record fields — negligible cost (microseconds) vs the route's interpolation pass + DB inserts. Even worst-case `points.Count = 500` with all `geofences.polygons.Count` runs ~500 microsecond. No N+1, no blocking I/O. |
|
||||
| 6. Cross-Task Consistency | Uses the same `ValidationEndpointFilter<T>` infra from cycle 7 + batch-2 of cycle 8 and the shared `ProblemDetailsAssertions.AssertErrorsContainsMention`. Error keys follow the same camelCase JSON-path policy (`points[i].lat`, `geofences.polygons[i].northWest`) per `error-shape.md` v1.0.0 Inv-4. All produce identically-shaped `ValidationProblemDetails` bodies. |
|
||||
| 7. Architecture Compliance | Route DTOs live in `SatelliteProvider.Common/DTO/` (shared with the service layer + integration tests). Validators co-located with the API at `SatelliteProvider.Api/Validators/`. No layering violations. The service-layer `RouteValidator` retention is documented as defence-in-depth (F2). No cycles, no public-API bypasses, no ADR breaches. |
|
||||
|
||||
## Files Reviewed
|
||||
|
||||
### AZ-809 (route-creation validator)
|
||||
- `SatelliteProvider.Common/DTO/CreateRouteRequest.cs` — `[JsonRequired]` on every non-optional axis. Removed implicit defaults so callers cannot rely on them.
|
||||
- `SatelliteProvider.Common/DTO/RoutePoint.cs` — `[JsonRequired]` on Latitude/Longitude.
|
||||
- `SatelliteProvider.Common/DTO/GeofencePolygon.cs` — `[JsonRequired]` on `NorthWest`/`SouthEast` in `GeofencePolygon`; `[JsonRequired]` on `Polygons` in `Geofences`.
|
||||
- `SatelliteProvider.Common/DTO/GeoPoint.cs` — `[JsonRequired]` on Lat/Lon (used by `GeofencePolygon` corners).
|
||||
- `SatelliteProvider.Api/Validators/CreateRouteRequestValidator.cs` — **NEW** — 7 root rules (id non-empty + 4 range rules + points count + cross-field) plus `RuleForEach(req => req.Points).SetValidator(...)` and `RuleForEach(req => req.Geofences!.Polygons).SetValidator(...).OverridePropertyName(...)` for nested chains.
|
||||
- `SatelliteProvider.Api/Validators/RoutePointValidator.cs` — **NEW** — `OverridePropertyName("lat"/"lon")` chained AFTER `.InclusiveBetween()` so the type parameter is inferable.
|
||||
- `SatelliteProvider.Api/Validators/GeofencePolygonValidator.cs` — **NEW** — `CascadeMode.Stop` + `NotNull` + nested `GeoCornerValidator` for per-corner ranges; cross-field `Must` rules `.WithName("northWest")` for the invariant errors.
|
||||
- `SatelliteProvider.Api/Program.cs` (lines around `MapPost("/api/satellite/route", ...)`) — added `.WithValidation<CreateRouteRequest>()`, `.Accepts<>`, `.Produces<>`, `.ProducesProblem()`.
|
||||
- `SatelliteProvider.Tests/Validators/CreateRouteRequestValidatorTests.cs` — **NEW** — Theory + Fact coverage for each rule, positive and negative; 16 methods. Diagnostic-led: two assertions were converted from `polygons` / `polygons[0].northWest` to `geofences.polygons` / `geofences.polygons[0].northWest` after `OverridePropertyName` was added.
|
||||
- `SatelliteProvider.Tests/Validators/RoutePointValidatorTests.cs` — **NEW** — 4 methods (lat/lon range, positive + negative).
|
||||
- `SatelliteProvider.Tests/Validators/GeofencePolygonValidatorTests.cs` — **NEW** — 6 methods incl. NotNull on corners, range on corners, NW-of-SE invariants.
|
||||
- `SatelliteProvider.IntegrationTests/CreateRouteValidationTests.cs` — **NEW** — Happy + empty body + missing/zero GUID + 4 out-of-range + insufficient-points + per-point lat/lon out-of-range + geofence invariant + missing-requestMaps + cross-field createTilesZip + unknown-root + nested type-mismatch = 16 methods.
|
||||
- `SatelliteProvider.IntegrationTests/Program.cs` — wired `CreateRouteValidationTests.RunAll` into smoke + full suites.
|
||||
- `scripts/probe_route_validation.sh` — **NEW** — curl probes for every failure mode + happy path.
|
||||
- `_docs/02_document/contracts/api/route-creation.md` — **NEW** — v1.0.0 contract (no prior version existed). Includes the nested DTO chain + invariants + per-field test cases table + advisory on the legacy `RouteValidator` + the input/output naming asymmetry.
|
||||
- `_docs/02_document/modules/api_program.md` — `CreateRoute Handler` section added; `Api/Validators` section bumped to AZ-808/AZ-809/AZ-811.
|
||||
- `_docs/02_document/modules/common_dtos.md` — `CreateRouteRequest`/`RoutePoint`/`Geofences`/`GeofencePolygon`/`GeoPoint` descriptions updated with `[JsonRequired]` markers and constraint summaries.
|
||||
- `_docs/02_document/system-flows.md::F4` — sequence diagram extended with the validation-filter branch; preconditions + error scenarios reference the new contract.
|
||||
- `_docs/02_document/tests/blackbox-tests.md::BT-06/BT-N03/BT-N04/BT-N05` — triggers and pass criteria align with the new wire format + named error keys.
|
||||
- `_docs/02_document/tests/security-tests.md::SEC-04` — references `GlobalExceptionHandler`'s JsonException branch + AZ-353 correlationId.
|
||||
|
||||
## Test Evidence
|
||||
|
||||
`./scripts/run-tests.sh --smoke` (`integration-tests` container exit code 0):
|
||||
|
||||
```
|
||||
Test: POST /api/satellite/route strict validation (AZ-809)
|
||||
==========================================================
|
||||
|
||||
AZ-809 AC-2: well-formed body → HTTP 200 (no background processing — requestMaps=false)
|
||||
✓ Well-formed body accepted with HTTP 200
|
||||
|
||||
AZ-809 rule 1: empty body → HTTP 400
|
||||
✓ Empty body rejected with HTTP 400
|
||||
|
||||
AZ-809 rule 2 (probe-confirmed gap): missing `id` → HTTP 400 (no silent zero-Guid coercion)
|
||||
✓ Missing `id` rejected with HTTP 400 (no silent coercion)
|
||||
|
||||
AZ-809 rule 2: zero-Guid `id` → HTTP 400
|
||||
✓ Zero-Guid `id` rejected with errors["id"]
|
||||
|
||||
AZ-809 rule 3: empty `name` → HTTP 400
|
||||
✓ Empty `name` rejected with errors["name"]
|
||||
|
||||
AZ-809 rule 5: `regionSizeMeters` out of range (100..10000) → HTTP 400
|
||||
✓ `regionSizeMeters=1000000` rejected with errors["regionSizeMeters"]
|
||||
|
||||
AZ-809 rule 6: `zoomLevel` out of range (0..22) → HTTP 400
|
||||
✓ `zoomLevel=30` rejected with errors["zoomLevel"]
|
||||
|
||||
AZ-809 rule 7: `points` count < 2 → HTTP 400
|
||||
✓ `points` count=1 rejected with errors["points"]
|
||||
|
||||
AZ-809 rule 8: per-point `lat` out of range → HTTP 400 (errors[points[i].lat])
|
||||
✓ `points[1].lat=91` rejected with errors["points[1].lat"]
|
||||
|
||||
AZ-809 rule 8: per-point `lon` out of range → HTTP 400 (errors[points[i].lon])
|
||||
✓ `points[1].lon=181` rejected with errors["points[1].lon"]
|
||||
|
||||
AZ-809 rule 9: geofence NW.lat <= SE.lat → HTTP 400 (cross-field invariant)
|
||||
✓ NW.lat <= SE.lat rejected by cross-field invariant
|
||||
|
||||
AZ-809 rule 10: missing `requestMaps` → HTTP 400 (no defaulting)
|
||||
✓ Missing `requestMaps` rejected
|
||||
|
||||
AZ-809 rule 12: `createTilesZip=true` AND `requestMaps=false` → HTTP 400 (cross-field invariant)
|
||||
✓ `createTilesZip=true requestMaps=false` rejected by cross-field invariant
|
||||
|
||||
AZ-809 rule 13: unknown root field → HTTP 400 (UnmappedMemberHandling.Disallow)
|
||||
✓ Unknown root field `debug` rejected with errors mention
|
||||
|
||||
AZ-809 rule 14: nested type mismatch (`points[0].lat` as string) → HTTP 400
|
||||
✓ `points[0].lat:"fifty"` rejected with HTTP 400
|
||||
✓ Create-route validation tests: PASSED
|
||||
```
|
||||
|
||||
`=== All tests passed (mode=smoke) ===` — no regressions in cycle-7 inventory or batch-1/batch-2 cycle-8 (AZ-812/AZ-808/AZ-811) tests, no regressions in the migration/leaflet/route/tile/security suites.
|
||||
|
||||
## Verdict Logic
|
||||
|
||||
- No Critical, no High, no Medium findings.
|
||||
- 1 Low finding (F1) — the `OverridePropertyName` requirement is captured in code comments + module docs; not a regression, but worth flagging so it cannot be silently regressed.
|
||||
- 2 Info findings (F2 defence-in-depth retention, F3 input/output naming asymmetry) — both pre-existing, documented as advisories for a follow-up PBI.
|
||||
- **PASS_WITH_NOTES**.
|
||||
Reference in New Issue
Block a user