mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 15:21:14 +00:00
7d1288e4ba
FT-P-15: parse FDR `cache-self-check` records; assert every tile-manifest entry has CRS, tile_matrix, dimension, m_per_px, capture_date, source, compression; m_per_px >= 0.5 (or rejected by FDR `tile-load-rejected`). FT-P-16: read `docker network inspect e2e-net` + `docker inspect <sut>` snapshots; assert `Internal == true` AND SUT attached only to e2e-net. The 0-egress semantic of AC-8.3 is enforced structurally. FT-P-18: walk FDR + tile-cache, probe JPEG dimensions via stdlib SOF parser, reject any file matching nav-camera raw pattern (5472x3648 or 880x720). Extrapolate thumbnail-log size to 8h; assert < 1 GB. Adds runner.helpers.tile_cache_inspector with five evaluators (manifest schema, offline mode, raw-frame detection, thumbnail budget, JPEG dimension probe) + walk_files helper. Pure-logic coverage: 43 new unit tests; full e2e/_unit_tests/ suite 793 passing (was 746). Scenarios skip locally when SITL replay fixture or docker-inspect env vars are missing; production hooks (cache-self-check FDR record, tile-load-rejected events, docker-inspect snapshots) are tracked outside this task. See _docs/03_implementation/batch_82_report.md + reviews/batch_82_review.md. Co-authored-by: Cursor <cursoragent@cursor.com>
108 lines
4.0 KiB
Python
108 lines
4.0 KiB
Python
"""FT-P-15 — Tile cache manifest schema + resolution floor (AZ-421 / AC-8.1).
|
|
|
|
The full scenario:
|
|
|
|
1. SUT cold-starts against the bind-mounted ``tile-cache-fixture`` and
|
|
emits a one-time ``cache-self-check`` FDR record carrying every
|
|
manifest entry it loaded (CRS, tile_matrix, dimension, m_per_px,
|
|
capture_date, source, compression).
|
|
2. The SUT additionally emits ``tile-load-rejected`` FDR records for
|
|
any entry the freshness/floor gate rejected at load time.
|
|
3. The test parses the FDR archive, evaluates the manifest schema
|
|
contract (AC-1: every required field present; AC-2: every entry
|
|
either ≥ 0.5 m/px or rejected), and asserts the report passes.
|
|
|
|
AC-1: every required field present per entry — ``MANIFEST_REQUIRED_FIELDS``.
|
|
AC-2: m/px ≥ 0.5 OR rejected by FDR ``tile-load-rejected``.
|
|
AC-3 of FT-P-15-spec maps to AC-6 of the task (parameterisation).
|
|
|
|
Gated on:
|
|
|
|
* ``runner.helpers.fdr_reader`` — owned by AZ-594; present.
|
|
* ``runner.helpers.tile_cache_inspector.evaluate_manifest_schema`` —
|
|
pure-logic evaluator covered by
|
|
``e2e/_unit_tests/helpers/test_tile_cache_inspector.py``.
|
|
* ``sitl_replay_ready`` — skip-gates the scenario when no FDR archive
|
|
is present locally.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from runner.helpers import tile_cache_inspector as tci
|
|
|
|
|
|
@pytest.mark.traces_to("AC-8.1,AC-1,AC-2,AC-6")
|
|
def test_ft_p_15_cache_schema(
|
|
fc_adapter: str,
|
|
vio_strategy: str,
|
|
evidence_dir, # type: ignore[no-untyped-def]
|
|
run_id: str,
|
|
nfr_recorder, # type: ignore[no-untyped-def]
|
|
sitl_replay_ready: bool,
|
|
) -> None:
|
|
"""Full FT-P-15 scenario (AC-8.1)."""
|
|
if not sitl_replay_ready:
|
|
pytest.skip(
|
|
"FT-P-15 requires `E2E_SITL_REPLAY_DIR` to point at a SITL replay "
|
|
"fixture that includes the FDR `cache-self-check` record + any "
|
|
"`tile-load-rejected` records (AZ-595 + AZ-421 fixture builder). "
|
|
"Pure-logic AC-8.1 coverage lives in "
|
|
"e2e/_unit_tests/helpers/test_tile_cache_inspector.py."
|
|
)
|
|
|
|
from runner.helpers import fdr_reader
|
|
|
|
fdr_root = Path(evidence_dir).parent / f"run-{run_id}" / "fdr"
|
|
|
|
manifest_entries: list[dict] = []
|
|
rejected_ids: list[str] = []
|
|
for rec in fdr_reader.iter_records(fdr_root):
|
|
if rec.record_type == tci.CACHE_SELF_CHECK_FDR_KIND:
|
|
raw_entries = rec.payload.get("entries")
|
|
if isinstance(raw_entries, list):
|
|
for entry in raw_entries:
|
|
if isinstance(entry, dict):
|
|
manifest_entries.append(entry)
|
|
elif rec.record_type == tci.TILE_LOAD_REJECTED_FDR_KIND:
|
|
entry_id = rec.payload.get("id") or rec.payload.get("tile_id")
|
|
if isinstance(entry_id, str) and entry_id:
|
|
rejected_ids.append(entry_id)
|
|
|
|
if not manifest_entries:
|
|
pytest.fail(
|
|
f"FT-P-15: no `{tci.CACHE_SELF_CHECK_FDR_KIND}` FDR record with "
|
|
f"manifest entries found under {fdr_root}. The fixture builder "
|
|
"must emit one at cold start."
|
|
)
|
|
|
|
report = tci.evaluate_manifest_schema(
|
|
manifest_entries,
|
|
tile_load_rejected_ids=rejected_ids,
|
|
)
|
|
|
|
nfr_recorder.record_metric(
|
|
"ft_p_15.manifest_entries", float(report.total_entries), ac_id="AC-8.1"
|
|
)
|
|
nfr_recorder.record_metric(
|
|
"ft_p_15.entries_missing_fields",
|
|
float(len(report.entries_with_missing_fields)),
|
|
ac_id="AC-1",
|
|
)
|
|
nfr_recorder.record_metric(
|
|
"ft_p_15.entries_below_floor",
|
|
float(len(report.entries_below_floor)),
|
|
ac_id="AC-2",
|
|
)
|
|
|
|
assert report.passes, (
|
|
"AC-8.1 (manifest schema + ≥0.5 m/px floor) failed: "
|
|
f"total={report.total_entries}, "
|
|
f"missing_fields={[(e.entry_id, e.missing_fields) for e in report.entries_with_missing_fields]}, "
|
|
f"below_floor_not_rejected="
|
|
f"{[e.entry_id for e in report.entries_below_floor if e.entry_id not in report.rejected_below_floor_ids]}"
|
|
)
|