Drains all three deferred perf-harness items in one batch: - PT-01..PT-06 now carry Authorization: Bearer minted via the canonical SatelliteProvider.TestSupport.JwtTokenFactory (AZ-491) — no third copy of JWT logic in the shell. - PT-07 implemented as cold + warm dual-pass distribution (N=20 each), reports p50/p95 for both passes and fails if warm p95 >= cold p95. - PT-08 implemented as 20-batch upload distribution with batch p95 gated at the AZ-488 2000 ms target; per-item gate cost reported as derived proxy (batch_p95 / batch_size). New SatelliteProvider.IntegrationTests/PerfBootstrap.cs adds two CLI short-circuit subcommands (--mint-only and --gen-uav-fixture <path>) invoked by the shell so the perf script never inlines the JWT or JPEG-fixture logic. The dispatch sits at the top of Program.cs Main and runs before any HTTP / DB / readiness setup. performance-tests.md PT-07 + PT-08 flip from Deferred to Implemented. traceability-matrix.md PT-07 + PT-08 rows move from recorded to covered (PT-08 partial due to per-item proxy — flagged Low in batch-4 review). _docs/_process_leftovers/2026-05-11_perf-pt07-harness.md deleted; the leftovers directory is now empty. Closes cycle-2 retro Action 2; LESSONS.md [process] rule about Deferred NFRs remains in force as a guardrail. Also includes the previously-uncommitted cumulative review report for cycle-3 batches 01-03 (generated at the end of batch 3 but not staged). Co-authored-by: Cursor <cursoragent@cursor.com>
8.3 KiB
Module: Tests/SatelliteProvider.IntegrationTests
Purpose
Console application that runs end-to-end integration tests against a live API instance. Designed to run in Docker alongside the API and PostgreSQL containers.
Public Interface
Test Classes
TileTests— tile download via lat/lon endpointRegionTests— region request → polling → completion flowBasicRouteTests— route creation with intermediate pointsComplexRouteTests— routes with geofencingExtendedRouteTests— routes withrequestMaps: trueand tile ZIP creationMigrationTests— direct PostgreSQL schema/index validation (no HTTP). AZ-484 cycle added:NewUniqueConstraintIncludesSourceColumn_AZ484_AC1,BackfillUpdateAssignsGoogleMapsAndCapturedAt_AZ484_AC4,MultiSourceInsertCoexistsUnderNewIndex_AZ484_AC1,MostRecentAcrossSourcesSelection_AZ484_AC2,SameSourceUpsertReplacesPreviousRow_AZ484_AC3(latter four use temp tables to keep production data untouched).JwtIntegrationTests(added by AZ-487, cycle 2; helpers consolidated by AZ-491 cycle 3) —AnonymousRequest_To_AnyEndpoint_Returns401,ExpiredToken_Returns401,InvalidSignature_Returns401,ValidToken_Returns200_OnHealthyEndpoint,SwaggerDocument_AdvertisesBearerSecurityScheme. HS256 token minting lives in the sharedSatelliteProvider.TestSupport.JwtTokenFactory(consumed viaProjectReference); runner-specific concerns (JwtTestHelpers.ResolveSecretOrThrow,AttachDefaultAuthorization,DefaultSubject = "integration-tests") remain in this project. The test runner setsJWT_SECRETon the API container and attaches a Bearer token to every existing test's HTTP requests so the pre-cycle-2 suite continues to pass.UavUploadTests(added by AZ-488, cycle 2; coordinate-counter promoted to defense-in-depth by AZ-493 cycle 3) —HappyPathSingleItem_PersistsRow,MixedBatch_ReturnsPerItemResults,MultiSourceCoexistence_AZ484_Cycle2,SameSourceUpsert_AZ484_Cycle2,NoToken_Returns401,ValidTokenWithoutGpsPermission_Returns403,OversizedBatch_Returns400. The wall-clock-seeded_coordinateCounteris retained as a belt-and-suspenders safeguard alongside the AZ-493 startup DB-reset (below) — if a developer runs with--keep-state, or the DB-reset path is skipped for any reason, the wall-clock seed still spreads coordinates across runs so the per-source unique index does not collide.StubAndErrorContractTests(existing) — updated in cycle 2 to drop the legacyStubUpload_Returns501expectation since AZ-488 implemented the endpoint.
Supporting Classes
Models.cs— HTTP response DTOs for deserializationRouteTestHelpers.cs— shared utilities (wait-for-completion polling, geofence polygon builders, test data)Program.cs— test runner entry point (handles--smoke/--fullmode selection,--keep-stateopt-out flag, default-token issuance viaJwtTokenFactory, the AZ-493 DB-reset hook, and the AZ-492--mint-only/--gen-uav-fixtureperf-bootstrap subcommands that short-circuit before any HTTP / DB setup)JwtTestHelpers.cs— runner-side JWT concerns (ResolveSecretOrThrowreads theJWT_SECRETenv var with size validation;AttachDefaultAuthorizationputs a Bearer token on the sharedHttpClient;DefaultSubject = "integration-tests"is the canonical runner subject value). Token minting lives in the sharedSatelliteProvider.TestSupport.JwtTokenFactory(AZ-491) — runner-side concerns deliberately stay here.IntegrationTestDatabaseReset.cs(AZ-493) — instance class with a singleEnsureCleanStateAsync()method that truncates the integration-test target tables in FK-safe order. Guarded viaSatelliteProvider.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 subjectperf-testsand apermissions: GPSclaim via the canonicalSatelliteProvider.TestSupport.JwtTokenFactory.Create;GenerateUavFixture(args)writes a 256×256 random-noise JPEG viaSixLabors.ImageSharpto the path passed on the CLI. Invoked fromscripts/run-performance-tests.shviadotnet <SatelliteProvider.IntegrationTests.dll> --mint-onlyand--gen-uav-fixture <path>.
Internal Logic
- Makes HTTP calls to the API at
API_URLenvironment variable (default:http://api:8080) - Tests are methods called sequentially from
Program.cs(not xUnit — plain console app) - Poll-based waiting for async operations (region/route completion)
- Validates response structure, status transitions, file creation
Dependencies
ProjectReferencetoSatelliteProvider.TestSupport(added by AZ-491; providesJwtTokenFactory. Added by AZ-493; providesIntegrationTestResetGuard).- Communicates with the API exclusively via HTTP for end-to-end tests; communicates with PostgreSQL directly only via the dedicated DB-reset hook + the existing
MigrationTestsschema assertions. - NuGet:
Npgsql9.0.2 (Postgres client for DB-reset + MigrationTests),SixLabors.ImageSharp3.1.11 (UAV fixture image generation).
Consumers
docker-compose.tests.yml— runs as a container that depends on the API service
Configuration
API_URLenvironment variable (set in docker-compose.tests.yml tohttp://api:8080)INTEGRATION_TESTS_MODE—smokeorfull(defaultfull). DrivesTestRunMode.Smoke.INTEGRATION_KEEP_STATE— set to1ortrue(or pass--keep-statetoProgram.cs/scripts/run-tests.sh) to skip the AZ-493 DB-reset hook. Useful for debugging a failed run.ASPNETCORE_ENVIRONMENT=Testing— guard for the DB-reset hook. The reset refuses to run unless this is set (see Reliability § Test isolation below).JWT_SECRET— shared HMAC secret with the API container; must be ≥ 32 bytes (UTF-8).DB_CONNECTION_STRING— Npgsql connection string; the reset hook additionally requires the Host to be in the allowed-host list (postgres,localhost,127.0.0.1).
Reliability
Test isolation (AZ-493)
Program.cs runs IntegrationTestDatabaseReset.EnsureCleanStateAsync() at startup, before any test class executes. The hook truncates route_regions, route_points, routes, regions, tiles (in that FK-safe order, with RESTART IDENTITY CASCADE) so each run starts from a known empty state. The Postgres named volume in docker-compose.yml is intentionally persisted across docker-compose down cycles for fast iteration; the AZ-493 reset hook is what gives back per-run isolation in spite of that.
Two guards protect against accidental truncate against a non-test database:
ASPNETCORE_ENVIRONMENTMUST equalTesting(case-insensitive). Set bydocker-compose.tests.yml; absent in production / dev.DB_CONNECTION_STRINGHost MUST be one ofpostgres,localhost,127.0.0.1. Set bydocker-compose.tests.ymland developer machines; a remote-host connection string is rejected even with the env guard satisfied.
Both guards are pure-string checks in SatelliteProvider.TestSupport.IntegrationTestResetGuard — unit-tested in SatelliteProvider.Tests/TestSupport/IntegrationTestResetGuardTests.cs. Failure of either guard surfaces a clear InvalidOperationException and exits the runner with code 1.
To debug leftover state from a failed run, opt out of the reset:
- CLI:
./scripts/run-tests.sh --full --keep-state - Direct:
INTEGRATION_KEEP_STATE=1 docker compose ... up - In the runner Main:
dotnet run --project SatelliteProvider.IntegrationTests -- --keep-state
Adding new tables
If a new task adds a table that integration tests insert into AND that table participates in foreign-key relationships with tiles / regions / routes, update IntegrationTestDatabaseReset.TruncateOrder to include the new table in FK-safe order. The current order assumes the AZ-484 + AZ-488 schema; future migrations that introduce new FK chains need a corresponding order revision. The CASCADE clause is a safety net but is not a substitute for an explicit order — the order is the audit trail for "what does an integration-test runner see at startup".
External Integrations
- HTTP to the SatelliteProvider API
- Reads output files from mounted
./ready/and./tiles/volumes
Security
None.
Tests
This IS the integration test suite.