mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 16:01:13 +00:00
6e4a575221
Batch 88 — adds four resource-limit blackbox scenarios + pure-logic helpers + unit tests: - NFT-LIM-01 Jetson memory (AC-NEW-13): tier2_only; Plan A/B budgets; AC-4 OOM-event scan; 30 s warm-up window; VmRSS + tegrastats streams. - NFT-LIM-02 FDR size (AC-7.3): 30 min → 8 h linear extrapolation against 50 GiB; ±60 s replay-window slack for AC-1. - NFT-LIM-03+05 storage (AC-7.4 + AC-NEW-12 + RESTRICT-STORAGE): aggregate ≤ 100 GiB across tile-cache + tile-cache-write + fdr-output; thumbnail-log < 1 GiB strict 8 h-extrapolated. - NFT-LIM-04 thermal (AC-NEW-5 PARTIAL): tier2_only; CPU/SoC p99 ≤ T_throttle − 5 °C; throttle-event scan; PARTIAL annotation written to traceability-status.json. Thresholds fixture lives at e2e/fixtures/jetson/thermal-thresholds.json (moved from the task spec's suggested tests/fixtures/ path so the file stays inside the blackbox_tests Owns: e2e/** envelope). All four helpers are public-boundary-only (no src/gps_denied_onboard imports). Scenarios skip cleanly in the Tier-1 docker harness pending AZ-595 (SITL replay builder) for the four shared fixture inputs and AZ-444 (Tier-2 Jetson runner) for the tier2_only scenarios. Code review: PASS_WITH_WARNINGS (0/0/2/1). Both Mediums are carried-over write_csv_evidence + _resolve_fixture_path duplication, deferred to AZ-446 (batch 89). Low is the self-resolved AZ-443 fixture ownership drift documented in the review. Tests: 1223 e2e/_unit_tests passing (+1 vs. batch 87 from the new directory-layout entry); 24 resource_limit scenarios collect and skip cleanly under runner/pytest.ini. Co-authored-by: Cursor <cursoragent@cursor.com>
199 lines
8.9 KiB
Python
199 lines
8.9 KiB
Python
"""Asserts the AZ-406 directory layout is present.
|
|
|
|
Every blackbox / fixture / Jetson task added later relies on these paths.
|
|
Catching a missing directory here is much faster than failing inside the
|
|
e2e-runner image build.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
E2E_ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"relative_path",
|
|
[
|
|
"README.md",
|
|
".gitignore",
|
|
"docker/docker-compose.test.yml",
|
|
"docker/docker-compose.tier2-bridge.yml",
|
|
"docker/secrets/mavlink_passkey",
|
|
"docker/run-tier1.sh",
|
|
"jetson/run-tier2.sh",
|
|
"jetson/tier2-on-jetson.sh",
|
|
"jetson/tier2.service",
|
|
"jetson/tegrastats_parser.py",
|
|
"jetson/jtop_parser.py",
|
|
"runner/Dockerfile",
|
|
"runner/requirements.txt",
|
|
"runner/pytest.ini",
|
|
"runner/conftest.py",
|
|
"runner/reporting/csv_reporter.py",
|
|
"runner/reporting/evidence_bundler.py",
|
|
"runner/reporting/nfr_recorder.py",
|
|
"runner/helpers/frame_source_replay.py",
|
|
"runner/helpers/imu_replay.py",
|
|
"runner/helpers/sitl_observer.py",
|
|
"runner/helpers/mavproxy_tlog_reader.py",
|
|
"runner/helpers/fdr_reader.py",
|
|
"runner/helpers/geo.py",
|
|
"runner/helpers/anchor_pair_detector.py",
|
|
"runner/helpers/estimate_schema.py",
|
|
"runner/helpers/accuracy_evaluator.py",
|
|
"runner/helpers/registration_classifier.py",
|
|
"runner/helpers/mre_evaluator.py",
|
|
"runner/helpers/multi_segment_evaluator.py",
|
|
"runner/helpers/smoothing_evaluator.py",
|
|
"runner/helpers/sharp_turn_detector.py",
|
|
"runner/helpers/msp_frame_observer.py",
|
|
"runner/helpers/ap_contract_evaluator.py",
|
|
"runner/helpers/gcs_telemetry_evaluator.py",
|
|
"runner/helpers/tile_cache_inspector.py",
|
|
"runner/helpers/mid_flight_tile_evaluator.py",
|
|
"runner/helpers/mock_suite_sat_audit.py",
|
|
"runner/helpers/retrieval_evaluator.py",
|
|
"runner/helpers/aged_tile_rejection_evaluator.py",
|
|
"runner/helpers/cold_start_evaluator.py",
|
|
"runner/helpers/outlier_tolerance_evaluator.py",
|
|
"runner/helpers/outage_request_evaluator.py",
|
|
"runner/helpers/blackout_spoof_evaluator.py",
|
|
"runner/helpers/fc_proxy_runtime.py",
|
|
"runner/helpers/replay_mode.py",
|
|
"runner/helpers/streaming_evaluator.py",
|
|
"runner/helpers/spoof_promotion_evaluator.py",
|
|
"runner/helpers/ttff_evaluator.py",
|
|
"runner/helpers/e2e_latency_evaluator.py",
|
|
"runner/helpers/imu_fallback_drift_evaluator.py",
|
|
"runner/helpers/companion_reboot_evaluator.py",
|
|
"runner/helpers/monte_carlo_envelope_evaluator.py",
|
|
"runner/helpers/escalation_ladder_evaluator.py",
|
|
"runner/helpers/cache_poisoning_evaluator.py",
|
|
"runner/helpers/egress_observer.py",
|
|
"runner/helpers/mavlink_signing_evaluator.py",
|
|
"runner/helpers/cve_probe_evaluator.py",
|
|
"runner/helpers/asan_fuzz_evaluator.py",
|
|
"runner/helpers/memory_budget_evaluator.py",
|
|
"runner/helpers/fdr_size_evaluator.py",
|
|
"runner/helpers/storage_budget_evaluator.py",
|
|
"runner/helpers/thermal_envelope_evaluator.py",
|
|
"fixtures/sitl_replay_builder/__init__.py",
|
|
"fixtures/sitl_replay_builder/builder.py",
|
|
"fixtures/sitl_replay_builder/build_p01_fixtures.py",
|
|
"fixtures/sitl_replay_builder/build_p02_fixtures.py",
|
|
"fixtures/sitl_replay_builder/README.md",
|
|
"fixtures/mock-suite-sat/Dockerfile",
|
|
"fixtures/mock-suite-sat/app.py",
|
|
"fixtures/mock-suite-sat/requirements.txt",
|
|
"fixtures/tile-cache-builder/README.md",
|
|
"fixtures/tile-cache-builder/builder.py",
|
|
"fixtures/tile-cache-builder/Dockerfile",
|
|
"fixtures/tile-cache-builder/build.sh",
|
|
"fixtures/age-injector/README.md",
|
|
"fixtures/age-injector/age_injector.py",
|
|
"fixtures/age-injector/inject.sh",
|
|
"fixtures/injectors/outlier.py",
|
|
"fixtures/injectors/blackout_spoof.py",
|
|
"fixtures/injectors/multi_segment.py",
|
|
"fixtures/injectors/cold_boot.py",
|
|
"fixtures/injectors/_common.py",
|
|
"fixtures/injectors/fc_proxy.py",
|
|
"runner/helpers/injector_fixtures.py",
|
|
"fixtures/cold-boot/README.md",
|
|
"fixtures/cold-boot/cold_boot_fixture.json",
|
|
"fixtures/secrets/mavlink-test-passkey.txt",
|
|
"fixtures/security/generate_cve_jpeg.py",
|
|
"fixtures/security/cve-2025-53644.jpg",
|
|
"fixtures/security/README.md",
|
|
"tests/__init__.py",
|
|
"tests/conftest.py",
|
|
"tests/positive/__init__.py",
|
|
"tests/negative/__init__.py",
|
|
"tests/performance/__init__.py",
|
|
"tests/resilience/__init__.py",
|
|
"tests/security/__init__.py",
|
|
"tests/resource_limit/__init__.py",
|
|
"tests/positive/test_smoke.py",
|
|
"tests/positive/test_ft_p_01_still_image_accuracy.py",
|
|
"tests/positive/test_ft_p_02_derkachi_drift.py",
|
|
"tests/positive/test_ft_p_03_14_schema_wgs84.py",
|
|
"tests/positive/test_ft_p_04_derkachi_f2f_registration.py",
|
|
"tests/positive/test_ft_p_05_sat_anchor.py",
|
|
"tests/positive/test_ft_p_06_mre_budgets.py",
|
|
"tests/positive/test_ft_p_07_sharp_turn_recovery.py",
|
|
"tests/positive/test_ft_p_08_multi_segment_reloc.py",
|
|
"tests/positive/test_ft_p_09_ap_signing.py",
|
|
"tests/positive/test_ft_p_09_inav.py",
|
|
"tests/positive/test_ft_p_10_smoothing_lookback.py",
|
|
"tests/positive/test_ft_p_11_cold_start_init.py",
|
|
"tests/positive/test_ft_p_12_gcs_downsample.py",
|
|
"tests/positive/test_ft_p_13_gcs_command.py",
|
|
"tests/positive/test_ft_p_15_cache_schema.py",
|
|
"tests/positive/test_ft_p_16_offline_only.py",
|
|
"tests/positive/test_ft_p_17_mid_flight_tiles.py",
|
|
"tests/positive/test_ft_p_18_no_raw_retention.py",
|
|
"tests/positive/test_ft_p_19_sat_reloc_scale.py",
|
|
"tests/negative/test_ft_n_01_outlier_tolerance.py",
|
|
"tests/negative/test_ft_n_02_sharp_turn_failure.py",
|
|
"tests/negative/test_ft_n_03_outage_reloc.py",
|
|
"tests/negative/test_ft_n_04_blackout_spoof.py",
|
|
"tests/negative/test_ft_n_05_stale_tile_rejection.py",
|
|
"tests/negative/test_ft_n_06_mid_flight_freshness.py",
|
|
"tests/performance/test_nft_perf_01_e2e_latency.py",
|
|
"tests/performance/test_nft_perf_02_streaming.py",
|
|
"tests/performance/test_nft_perf_03_ttff.py",
|
|
"tests/performance/test_nft_perf_04_spoof_promotion.py",
|
|
"tests/resilience/test_nft_res_01_imu_only_fallback.py",
|
|
"tests/resilience/test_nft_res_02_companion_reboot.py",
|
|
"tests/resilience/test_nft_res_03_monte_carlo.py",
|
|
"tests/resilience/test_nft_res_04_blackout_escalation.py",
|
|
"tests/security/test_nft_sec_01_cache_poisoning.py",
|
|
"tests/security/test_nft_sec_02_no_egress.py",
|
|
"tests/security/test_nft_sec_03_mavlink_signing.py",
|
|
"tests/security/test_nft_sec_04_opencv_cve.py",
|
|
"tests/security/test_nft_sec_04_asan_fuzz.py",
|
|
"tests/security/test_nft_sec_05_dns_blackhole.py",
|
|
"fixtures/jetson/thermal-thresholds.json",
|
|
"tests/resource_limit/test_nft_lim_01_jetson_memory.py",
|
|
"tests/resource_limit/test_nft_lim_02_fdr_size.py",
|
|
"tests/resource_limit/test_nft_lim_03_05_storage_budget.py",
|
|
"tests/resource_limit/test_nft_lim_04_thermal.py",
|
|
],
|
|
)
|
|
def test_required_path_exists(relative_path: str) -> None:
|
|
"""Each path AZ-406 + AZ-407 + AZ-444 + AZ-445 commit to must exist on disk."""
|
|
assert (E2E_ROOT / relative_path).exists(), (
|
|
f"layout invariant broken: e2e/{relative_path} is missing"
|
|
)
|
|
|
|
|
|
def test_passkey_files_match() -> None:
|
|
"""Docker secret and runner-side passkey fixture must encode the same secret.
|
|
|
|
The docker-secret file is consumed by mavproxy as a raw 64-hex passkey
|
|
(no comments allowed in its body). The runner-side fixture file is the
|
|
AZ-407 AC-5 deliverable and ships with a ``# TEST ONLY...`` header
|
|
line so it self-documents during code review.
|
|
|
|
We therefore compare the FIRST 64-hex line of each file rather than
|
|
the raw bytes. The two files MUST encode the same 32-byte secret;
|
|
drift between them would mean a mavproxy run uses a different key
|
|
than the runner fixture states.
|
|
"""
|
|
|
|
# Arrange
|
|
docker_pk = (E2E_ROOT / "docker/secrets/mavlink_passkey").read_text().strip().splitlines()
|
|
runner_pk_lines = (E2E_ROOT / "fixtures/secrets/mavlink-test-passkey.txt").read_text().strip().splitlines()
|
|
runner_pk = [line for line in runner_pk_lines if not line.lstrip().startswith("#")]
|
|
|
|
# Assert
|
|
assert docker_pk and runner_pk, "passkey files must contain at least one non-comment line"
|
|
assert docker_pk[0] == runner_pk[0], (
|
|
"MAVLink test passkey secrets differ between docker secret and runner "
|
|
"fixture. They MUST encode the same 32-byte secret — see "
|
|
"e2e/fixtures/secrets/README.md."
|
|
)
|