mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 09:51:13 +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>
122 lines
4.6 KiB
Python
122 lines
4.6 KiB
Python
"""FT-P-16 — Offline-only operation (AZ-421 / AC-8.3, RESTRICT-SAT-1).
|
|
|
|
The full scenario:
|
|
|
|
1. The SUT runs against the local tile-cache mount only.
|
|
2. The Docker compose harness attaches the SUT container to
|
|
``e2e-net`` with ``Internal: true`` — Docker itself blocks egress
|
|
to anything outside that network (AZ-406 owns the compose wiring).
|
|
3. A 60 s Derkachi replay generates load; during the replay the
|
|
scenario reads ``docker network inspect e2e-net`` and
|
|
``docker inspect <sut-container>`` and asserts:
|
|
- ``e2e-net.Internal == true``
|
|
- The SUT container is attached to ``e2e-net`` only.
|
|
|
|
The "0 packets to non-e2e-net destinations" semantic of AC-8.3 is
|
|
enforced structurally — there is no other network the SUT can reach,
|
|
so the packet count is provably 0 without per-packet counters.
|
|
|
|
Gated on:
|
|
|
|
* ``sitl_replay_ready`` — full replay needs the SITL fixture (skip
|
|
cleanly otherwise).
|
|
* ``DOCKER_NETWORK_INSPECT_PATH`` / ``DOCKER_CONTAINER_INSPECT_PATH``
|
|
env vars — point at JSON files produced by the fixture builder
|
|
ahead of test invocation. When unset, the scenario skips with a
|
|
clear reason (the docker CLI is not available inside the runner
|
|
container without volume-mounting the docker socket; the fixture
|
|
builder snapshots the inspect output instead).
|
|
* ``runner.helpers.tile_cache_inspector.evaluate_offline_mode`` —
|
|
pure-logic evaluator covered by
|
|
``e2e/_unit_tests/helpers/test_tile_cache_inspector.py``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from runner.helpers import tile_cache_inspector as tci
|
|
|
|
DOCKER_NETWORK_INSPECT_ENV = "DOCKER_NETWORK_INSPECT_PATH"
|
|
DOCKER_CONTAINER_INSPECT_ENV = "DOCKER_CONTAINER_INSPECT_PATH"
|
|
|
|
|
|
@pytest.mark.traces_to("AC-8.3,AC-3,AC-6,RESTRICT-SAT-1")
|
|
def test_ft_p_16_offline_only(
|
|
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-16 scenario (AC-8.3 / RESTRICT-SAT-1)."""
|
|
if not sitl_replay_ready:
|
|
pytest.skip(
|
|
"FT-P-16 needs `E2E_SITL_REPLAY_DIR` to point at a SITL replay "
|
|
"fixture (AZ-595). Pure-logic AC-8.3 coverage lives in "
|
|
"e2e/_unit_tests/helpers/test_tile_cache_inspector.py."
|
|
)
|
|
|
|
net_path = os.environ.get(DOCKER_NETWORK_INSPECT_ENV)
|
|
ctr_path = os.environ.get(DOCKER_CONTAINER_INSPECT_ENV)
|
|
if not net_path or not ctr_path:
|
|
pytest.skip(
|
|
f"FT-P-16 needs `{DOCKER_NETWORK_INSPECT_ENV}` and "
|
|
f"`{DOCKER_CONTAINER_INSPECT_ENV}` env vars set to JSON files "
|
|
"produced by the compose harness (`docker network inspect "
|
|
"e2e-net` + `docker inspect gps-denied-onboard`). The fixture "
|
|
"builder snapshots both before the test runs."
|
|
)
|
|
|
|
net_inspect = _load_docker_inspect_object(Path(net_path), kind="network")
|
|
ctr_inspect = _load_docker_inspect_object(Path(ctr_path), kind="container")
|
|
|
|
report = tci.evaluate_offline_mode(net_inspect, ctr_inspect)
|
|
|
|
nfr_recorder.record_metric(
|
|
"ft_p_16.network_internal", 1.0 if report.network_internal else 0.0, ac_id="AC-8.3"
|
|
)
|
|
nfr_recorder.record_metric(
|
|
"ft_p_16.container_network_count", float(len(report.container_networks)), ac_id="AC-3"
|
|
)
|
|
|
|
assert report.passes, (
|
|
"AC-8.3 (offline-only operation) failed: "
|
|
f"network_internal={report.network_internal}, "
|
|
f"container_networks={report.container_networks}, "
|
|
f"expected_network={report.expected_network}"
|
|
)
|
|
|
|
|
|
def _load_docker_inspect_object(path: Path, *, kind: str) -> dict:
|
|
"""Load a single inspect object from a JSON file.
|
|
|
|
``docker inspect`` returns a JSON array. The scenario expects
|
|
either the wrapped array OR an unwrapped single-object payload —
|
|
accept both shapes for forwards-compatibility with fixture
|
|
builders that pre-unwrap.
|
|
"""
|
|
if not path.exists():
|
|
pytest.fail(f"FT-P-16: {kind} inspect JSON not found at {path}")
|
|
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
if isinstance(raw, list):
|
|
if not raw:
|
|
pytest.fail(f"FT-P-16: {kind} inspect JSON at {path} is an empty array")
|
|
if not isinstance(raw[0], dict):
|
|
pytest.fail(
|
|
f"FT-P-16: {kind} inspect JSON at {path} array element is not an object"
|
|
)
|
|
return raw[0]
|
|
if isinstance(raw, dict):
|
|
return raw
|
|
pytest.fail(
|
|
f"FT-P-16: {kind} inspect JSON at {path} is neither object nor array: "
|
|
f"type={type(raw).__name__}"
|
|
)
|
|
return {} # unreachable; pytest.fail raises
|