Files
Oleksandr Bezdieniezhnykh 5fe67023b2 [AZ-329] [AZ-330] [AZ-523] [AZ-524] Batch 44 atomic refactor
Implements two new C12 services and rebalances the C11/C12 boundary
in one atomic commit:

* AZ-329 PostLandingUploadOrchestrator — gates C11 upload on the
  `flight_footer` FDR record's `clean_shutdown` field; 4 refusal
  modes; new FdrFooterReader Protocol + LocalFdrFooterReader.
* AZ-330 OperatorReLocService — AC-3.4 visual-loss re-localization
  hint; reuses shared LatLonAlt; OperatorCommandTransport Protocol
  cut (E-C8 owns the future pymavlink concrete); new FDR record
  kind `c12.reloc.requested`; log redaction (lat/lon 5 decimals,
  reason 200 chars).
* AZ-523 C11 internal flight-state gate removed (SRP refactor):
  `confirm_flight_state` / `FlightStateSignal` use /
  `FlightStateNotOnGroundError` deleted from C11; TileUploader
  contract bumped to v2.0.0 (frozen) with migration note; AZ-317
  superseded.
* AZ-524 Package rename `c12_operator_tooling` →
  `c12_operator_orchestrator` across source, tests, pyproject,
  CMake, Dockerfile, compose, CI, runtime-root services class
  (`OperatorOrchestratorServices`) + factory function
  (`build_operator_orchestrator`), logger namespaces, config slug,
  docs, and the E-C12 epic title.

Tests: 1543 passed, 80 skipped (all environment gates). Targeted
AC suite (AZ-329 + AZ-330 + FdrFooterReader): 37 passed. Cold-start
NFR-perf still ≤ 500 ms p99.

Tracker: AZ-317 → Done (superseded); AZ-319 v2.0.0 contract bump
comment; AZ-329/AZ-330 → In Testing; AZ-253 epic renamed; AZ-523
+ AZ-524 created and closed as audit-trail tickets.

See `_docs/03_implementation/batch_44_cycle1_report.md`.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 19:42:46 +03:00

9.1 KiB

Test Specification — C11 Tile Manager (TileDownloader + TileUploader)

Component-scoped. Suite-level coverage in _docs/02_document/tests/*.md. C11 was renamed and expanded in this Plan cycle: it now owns BOTH operator-side network I/O directions against satellite-provider. Strict ADR-004 enforcement: never loaded into the airborne companion image.

Acceptance Criteria Traceability

AC ID Acceptance Criterion (one-line) Test IDs Coverage
AC-8.1 Imagery via Suite Sat Service offline cache, ≥0.5 m/px FT-P-15, C11-IT-01 (download) Covered
AC-8.2 Tile freshness <6 mo (active-conflict) / <12 mo (rear) FT-N-05, C11-IT-02 (download-side gate) Covered
AC-8.3 Imagery pre-loaded onto companion before flight FT-P-15, FT-P-16, C11-IT-01 Covered
AC-NEW-6 System rejects/downgrades stale tiles FT-N-05, FT-N-06, C11-IT-02 Covered
AC-8.4 + D-PROJ-2 (POST contract) Mid-flight tile generation + post-landing upload to satellite-provider FT-P-17, C11-IT-03 (upload) Covered (against e2e-test fixture until D-PROJ-2 ships)
RESTRICT-SAT-1 Onboard cache offline-only; no in-flight Service calls NFT-SEC-02, NFT-SEC-05, C11-ST-01 Covered
ADR-004 Process isolation; C11 never airborne C11-ST-01, C11-ST-02 Covered (R02 enforcement)

Component-Internal Tests

C11-IT-01: TileDownloader fetch + sector-classified freshness gate + write to C6

Summary: given a Derkachi-area request, TileDownloader.fetch retrieves tiles from the real satellite-provider (or fixture in tests), enforces the per-sector freshness gate, and writes accepted tiles into C6 with byte-identical filesystem layout.

Traces to: AC-8.1, AC-8.2, AC-8.3, AC-NEW-6

Description: configure the fetch job with the Derkachi bbox + zoom range + sector classification (active_conflict + stable_rear mix); run fetch; assert (a) all returned tiles within the bbox land in C6, (b) tiles with produced_at older than the per-sector threshold are downgrade-flagged or rejected, (c) the DownloadBatchReport reports counts that sum to the request size.

Input data: scripted fetch request + the real satellite-provider (or its test Docker fixture under tests/fixtures/satellite-provider/; this is the REAL service's existing GET surface, not the mock).

Expected result: tiles in C6 match request scope; freshness flags applied per sector.

Max execution time: 4 min on Tier-1 (network-dependent).


C11-IT-02: freshness rejection counts surface in DownloadBatchReport

Summary: when the source has stale tiles, DownloadBatchReport.stale_rejected is non-zero and matches the count rejected at the C6 boundary.

Traces to: AC-NEW-6 (operator visibility)

Description: run a fetch that hits a known-stale tile range; assert (a) stale_rejected > 0, (b) the value equals C6's freshness-rejection count for that batch.

Input data: a synthetic satellite-provider response with stale tiles.

Expected result: counts match.

Max execution time: 60 s.


C11-IT-03: TileUploader posts mid-flight tiles + signs payload

Summary: TileUploader.upload_pending reads mid-flight tiles from C6's pending-upload set, packages them per the D-PROJ-2 POST contract sketch, signs each payload with the per-flight key, and posts to the configured /api/satellite/tiles/ingest endpoint.

Traces to: AC-8.4, D-PROJ-2 contract

Description: stage C6 with 50 mid-flight tiles flagged pending-upload; bring up the e2e-test mock-suite-sat-service fixture (under tests/fixtures/mock-suite-sat-service/); call upload_pending; assert (a) all 50 tiles POSTed, (b) each payload signature verifies against the test public key, (c) on 202 Accepted, the C6 row is marked uploaded, (d) the fixture's request log shows all 50 tiles in arrival.

Input data: scripted C6 + e2e-test mock fixture.

Expected result: all 50 uploaded + acknowledged + marked.

Max execution time: 5 min.


C11-IT-04: post-landing safety gate lives in C12 (cross-reference)

Summary: post-landing safety is owned by C12, not C11. The gate that historically lived in TileUploader.upload_pending_tiles was removed in Batch 44 (supersedes AZ-317); the equivalent check now lives in C12's PostLandingUploadOrchestrator (AZ-329) and refuses to invoke TileUploader.upload_pending_tiles unless the C13 flight_footer FDR record records clean_shutdown=True for the target flight.

Traces to: see _docs/02_document/components/13_c12_operator_orchestrator/tests.md → C12-IT-03 for the post-landing safety test.

Status: cross-reference only. C11's TileUploader no longer exposes confirm_flight_state or raises FlightStateNotOnGroundError.


C11-IT-05: idempotent uploads on retry

Summary: re-running upload_pending after a partial-success batch only POSTs the tiles that weren't acknowledged before.

Traces to: D-PROJ-2 contract resilience

Description: stage 50 pending-upload tiles; call upload_pending with the mock configured to return 202 for first 30, 5xx for last 20; assert C6 marks 30 as uploaded. Reset mock to 202 for all; call upload_pending again; assert only the remaining 20 are POSTed and the first 30 are NOT re-sent.

Input data: scripted with controlled mock failure profile.

Expected result: per assertion above.

Max execution time: 60 s.


Performance Tests

C11-PT-01: download throughput

Traces to: operator-tooling SLO (AC-8.3 supports this).

Load scenario: 10 GB Derkachi area, parallel fetch.

Expected results:

Metric Target Failure Threshold
Throughput ≥ 50 MB/s on a 1 Gbps link < 20 MB/s
DownloadBatchReport produced yes no report = test failure

C11-PT-02: upload throughput

Traces to: post-landing operator UX (AC-8.4 + D-PROJ-2 timing assumption).

Load scenario: 1000 mid-flight tiles, sequential POST + sign.

Expected results:

Metric Target Failure Threshold
Throughput ≥ 20 tile/s with signing < 10 tile/s

Security Tests

C11-ST-01: airborne process cannot import c11_tilemanager

Summary: per ADR-004 R02 enforcement, the airborne production-binary artifact has no path to import the C11 module.

Traces to: ADR-004, R02

Test procedure:

  1. Build the airborne production-binary.
  2. Inside a sandbox running the artifact, attempt import c11_tilemanager (and any submodule).
  3. Assert ModuleNotFoundError for every variant.
  4. Run the runtime_root.py self-check; assert it does NOT panic (because find_spec returns None).

Pass criteria: import fails everywhere; self-check passes silently. Fail criteria: any successful import.


C11-ST-02: NFT-SEC-02 network-egress test

Summary: the airborne process running in a no-route-to-satellite-provider network namespace cannot reach satellite-provider over TCP.

Traces to: ADR-004 R02 enforcement; covers RESTRICT-SAT-1.

Test procedure: as documented in tests/security-tests.md NFT-SEC-02. Listed here for traceability.

Pass criteria: zero outbound connection attempts to satellite-provider's host:port from the airborne process during a 10-min replay. Fail criteria: any attempt.


C11-ST-03: per-flight signing key zeroised after upload completes

Summary: after upload_pending completes (success or final failure), the per-flight key is zeroised in memory.

Traces to: D-C8-9 = (d) (operator-side analogue)

Test procedure:

  1. Run an upload.
  2. Capture the key buffer post-call via test harness.
  3. Assert the buffer is zero-filled.

Pass criteria: zero-filled. Fail criteria: any non-zero residue.


Acceptance Tests

C11-AT-01: operator runs F1 download + F10 upload via CLI

Summary: end-to-end operator flow exercises both interfaces via the C12-driven CLI.

Traces to: AC-8.3, AC-8.4

Preconditions:

  • Operator workstation with Docker + the operator-tooling tarball.
  • A satellite-provider (real or test fixture) reachable.

Steps:

Step Action Expected Result
1 operator-orchestrator download --area derkachi.geojson --since 2026-01 DownloadBatchReport printed; tiles in C6
2 operator-orchestrator build-cache C10 builds engines + descriptors + Manifest
3 (simulate flight) (covered by other tests)
4 operator-orchestrator upload-pending Pending-upload tiles POSTed; report printed

Test Data Management

Data Set Source Size
Real satellite-provider Docker fixture (download) upstream parent-suite Docker image
e2e-test mock-suite-sat-service fixture (upload) tests/fixtures/mock-suite-sat-service/ <50 MB image
Scripted Derkachi bbox + sector classification scripted <1 MB

Setup: bring up the appropriate fixture per test (real for download; e2e-test mock for upload until D-PROJ-2 ships). Teardown: stop fixture containers; clean per-test C6. Data isolation: per-test C6 + per-test fixture instance.