Files
Oleksandr Bezdieniezhnykh bc04ba7f99 [AZ-794] [AZ-795] [AZ-796] Cycle 7 Steps 12-15 sync (test-spec / docs / security / perf)
Step 12 (Test-Spec Sync): adds BT-27 for the AZ-796 9-rule
validation surface and 12 cycle-7 AC rows + Coverage Summary
update to traceability-matrix.md.

Step 13 (Update Docs): module-layout + module docs for the new
SatelliteProvider.Api/Validators namespace + GlobalExceptionHandler
+ updated TileInventory DTO; tests_unit + tests_integration
document the new InventoryRequestValidatorTests (16 unit tests
covering all 9 rules) + TileInventoryValidationTests (16
integration tests) + ProblemDetailsAssertions support;
glossary entries for Validation Problem Details / FluentValidation
/ Unmapped Member Handling; system-flows F8 (Tile Inventory Bulk
Lookup) expanded with deserializer + validator gates and a 13-row
Validation Surface table; data_parameters § Tile Inventory
documents the v2 input schema + constraints; ripple_log_cycle7
captures the doc-side ripple decisions.

Step 14 (Security Audit): 5-phase audit ran; verdict
PASS_WITH_WARNINGS (3 Low findings — D-AZ795-1 FluentValidation
12.0.0 -> 12.1.1 recommended bump, F-AZ795-1 JsonException.Message
leak in 400 detail, F-AZ795-2 BadHttpRequestException.Message leak).
No Critical / High; auth runs before validation (confirmed in
Program.cs); two NuGet additions (FluentValidation 12.0.0 +
.DependencyInjectionExtensions 12.0.0) both CVE-clean. Per-phase
reports plus consolidated security_report_cycle7.md.

Step 15 (Performance Test): docker compose stack used for perf
run, scripts/run-performance-tests.sh exited 0 with 8/8 scenarios
PASS (second consecutive clean exit-0); added PT-09 cycle-7 smoke
probe (v2 z/x/y schema, 2500-tile all-miss batch) measuring
min=27ms median=44ms p95=73ms max=86ms (13.7x under AZ-505 AC-4
1000ms budget). PT-07/08 improvements traced to the cycle-6 TLS
handshake-overhead identification, not application-side change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 11:24:27 +03:00

8.1 KiB
Raw Permalink Blame History

Module: Tests/SatelliteProvider.Tests

Purpose

Unit test project for component-internal logic. Original AZ-2/AZ-3 era had only a placeholder dummy; the suite has since grown across the AZ-285..AZ-380 baseline + cycle 1 (AZ-484) + cycle 2 (AZ-487, AZ-488) tracks. The "dummy test only" note in older revisions of this file is obsolete — the project now hosts the full unit suite executed by scripts/run-tests.sh --unit-only and CI's 01-test.yml.

Public Interface (test classes)

Existing baseline (pre-cycle-2) test classes cover TileService, RegionService, RouteService, geo math, repositories, validators, idempotency, and migration helpers — not enumerated exhaustively here. Cycle-2 additions:

AZ-487 + AZ-494 — JWT validation baseline + iss/aud

  • Authentication/AuthenticationServiceCollectionExtensionsTests — covers AddSatelliteJwt registration + TokenValidationParameters shape across both AZs:
    • AZ-487 baseline: AddSatelliteJwt_RegistersJwtBearerScheme, AddSatelliteJwt_ThrowsOnMissingSecret, AddSatelliteJwt_ThrowsOnShortSecret.
    • AZ-494 extension: AddSatelliteJwt_ThrowsOnMissingIssuer, _ThrowsOnEmptyIssuer, _ThrowsOnMissingAudience, _ThrowsOnEmptyAudience, plus updated _ConfiguresTokenValidationParameters_AsPerContract and _PrefersEnvironmentVariableOverConfiguration asserting ValidateIssuer = true + ValidIssuer + ValidateAudience = true + ValidAudience.
  • Authentication/JwtTokenFactoryTestsCreate_ProducesTokenValidatedByMatchingParameters, CreateExpired_TokenFailsValidationWithLifetimeException, Create_WithExtraClaims_PropagatesClaimsThroughValidation, TamperSignature_TokenFailsValidationWithSignatureException. The factory itself lives in SatelliteProvider.TestSupport after AZ-491 (single source of truth); this project consumes it via ProjectReference.

AZ-488 — UAV tile upload

  • 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).
  • Authentication/PermissionsRequirementTestsPermissionsAuthorizationHandler 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.
  • TestUtilities/UavTileImageFactory — programmatic JPEG factories used by the gate + handler tests: CreateValidJpeg(width, height, seed), CreateUniformJpeg, CreatePng (for Rule 1 negative path).

AZ-503 — Tile identity foundation

  • Uuidv5Tests — pure-C# UUIDv5 generator parity tests. Create_MatchesPythonReferenceVectors_AC1 pins 10 reference vectors generated by Python's uuid.uuid5(TILE_NAMESPACE, name); Create_IsDeterministic asserts repeated runs return the same Guid; Create_SetsVersionAndVariantBits asserts the version nibble is 5 and the variant top-2-bits are 10 (RFC 9562 §5.5).
  • UavTileFilePathTests (rewritten for AZ-503 from the cycle-2 placeholder) — covers BuildUavTileFilePath(Guid? flightId, int z, int x, int y) across three cases: BuildUavTileFilePath_AnonymousFlight_UsesNoneSegment (null flightId → literal none segment), BuildUavTileFilePath_PerFlight_UsesFlightIdDirectory (per-flight segment), BuildUavTileFilePath_DifferentFlights_ProduceDifferentPaths (path-distinctness across flights at the same cell). Integer-typed coordinates and the Guid? flightId parameter together still preclude string-injection path traversal.

AZ-795 + AZ-796 — strict inventory validation (cycle 7)

  • Validators/InventoryRequestValidatorTests (added cycle 7 — AZ-796) — 16 tests against InventoryRequestValidator + TileCoordValidator in isolation via FluentValidation's TestValidate(...) test helper. Covers every RuleFor(...): Validate_TilesPopulated_LocationHashesNull_Passes, Validate_LocationHashesPopulated_TilesNull_Passes, Validate_BothPopulated_FailsXorRule, Validate_NeitherPopulated_FailsXorRule, Validate_BothEmpty_FailsXorRule, Validate_TilesAtCap_Passes, Validate_TilesOverCap_FailsCapRule, Validate_LocationHashesOverCap_FailsCapRule, Validate_TileZoomOutOfRange_FailsRangeRule ([Theory] with z ∈ {-1, 23, 100}), Validate_TileZoomInRange_PassesRangeRule ([Theory] with z ∈ {0, 18, 22}), Validate_TileXNegative_FailsRangeRule, Validate_TileXAtUpperBound_FailsRangeRule, Validate_TileYNegative_FailsRangeRule, Validate_TileYAtUpperBound_FailsRangeRule, Validate_AxesAtMaxForZoom_Passes.
  • TestSupport/ValidatorTestModuleInitializer.cs (added cycle 7 — AZ-795) — [ModuleInitializer] that calls GlobalValidatorConfig.ApplyOnce() at test-assembly load time. Ensures unit tests see the same camelCase property-name resolution that Program.cs configures for the running API, so validator error keys (e.g., tiles[0].z) match the runtime contract per error-shape.md v1.0.0 Inv-4 without forcing every test to re-run the setup.

Internal Logic

  • Tests follow Arrange / Act / Assert. Time-dependent paths inject a FixedTimeProvider (cycle-2 addition) so Rule 4 has deterministic age windows.
  • JwtSecurityTokenHandler.MapInboundClaims = false is set explicitly in JWT tests so claims read by their original names (sub, permissions, …) rather than the framework-remapped names.
  • Cycle 7 also added validator-isolated assertions via FluentValidation's TestValidate(...) helper (no HTTP, no DI container) — the matching end-to-end assertions live in SatelliteProvider.IntegrationTests/TileInventoryValidationTests.cs.

Dependencies

  • Project references: SatelliteProvider.Services.TileDownloader, SatelliteProvider.Services.RegionProcessing, SatelliteProvider.Services.RouteManagement, SatelliteProvider.Common, SatelliteProvider.DataAccess, SatelliteProvider.Api (for the Authentication tests — added in AZ-487), SatelliteProvider.TestSupport (added by AZ-491; provides the canonical JwtTokenFactory consumed by both this project and SatelliteProvider.IntegrationTests).
  • NuGet: xUnit (2.5.3), Moq (4.20.72), FluentAssertions (8.8.0), coverlet.collector (6.0.0), Microsoft.NET.Test.Sdk (17.8.0), Microsoft.Extensions.* (Caching.Memory, Configuration, DI, Logging, Options, Http — all bumped from 9.0.10 → 10.0.7 by AZ-500 as a coordinated cycle-4 move), Microsoft.AspNetCore.Authentication.JwtBearer 10.0.7 (consumed transitively via the ProjectReference to SatelliteProvider.Api; AZ-487 added the dependency at 8.0.21, AZ-496 bumped it to 8.0.25, AZ-500 bumped it to 10.0.7), SixLabors.ImageSharp 3.1.11 (added by AZ-488 for the gate tests), FluentValidation + FluentValidation.TestHelper 12.0.0 (added cycle 7 — AZ-795; the test helper drives the TestValidate(...) assertions used by InventoryRequestValidatorTests).
  • appsettings.json copied to output (used by Authentication tests for the Jwt section binding scenario).

Consumers

  • CI pipeline (01-test.yml) and scripts/run-tests.sh --unit-only run dotnet test against this project.

Tests

This IS the test module. Cycle-2 added ~25 unit tests on top of the existing baseline; cycle-5 (AZ-503) added 6 more (3 in Uuidv5Tests, 3 in UavTileFilePathTests) plus 2 new methods in UavTileUploadHandlerTests. Cycle 7 (AZ-795 + AZ-796) added 16 more in InventoryRequestValidatorTests covering every RuleFor(...) in the cycle's new validators. The full project executes in seconds (no external services required). Cycle 7 Step 11 reported the unit suite at 311 tests, all green.