Files
gps-denied-onboard/e2e/_unit_tests/test_directory_layout.py
T
Oleksandr Bezdieniezhnykh a644debdb7 [AZ-416] [AZ-417] [AZ-419] Test batch 72: FT-P-09 AP/iNav + FT-P-11 cold start
- AZ-416 (FT-P-09-AP): fills mavproxy_tlog_reader.iter_messages with
  pymavlink body (AZ-406 surface kept); adds ap_contract_evaluator
  covering AC-1 (signing handshake <=5s), AC-2 (GPS_INPUT >=4.5 Hz),
  AC-3 (EK3_SRC1_POSXY=3), AC-4 (GPS_RAW_INT health >=80%); scenario
  forces fc_adapter=ardupilot.
- AZ-417 (FT-P-09-iNav): msp_frame_observer covering AC-2 (MSP rate)
  and AC-3 (fix_type/provider/numSat); scenario forces
  fc_adapter=inav.
- AZ-419 (FT-P-11): cold_start_evaluator covering AC-1 (operator
  manifest origin), AC-2 (FC EKF fallback), AC-3 (no-origin abort),
  AC-4 (bounded-delta conflict, ADR-010 Principle #11 amended);
  scenario parametrized on origin_source plus dedicated no-origin
  abort scenario.
- All scenarios skip-gated on upstream frame_source_replay /
  imu_replay / fdr_reader / sitl_observer extensions.
- +67 unit tests; full e2e unit suite: 460 passed.
- K=3 cumulative review fired: PASS for batches 70-72.

See _docs/03_implementation/batch_72_report.md,
_docs/03_implementation/reviews/batch_72_review.md,
_docs/03_implementation/cumulative_review_batches_70-72_cycle1_report.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-17 07:49:17 +03:00

135 lines
5.5 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/cold_start_evaluator.py",
"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/negative/test_ft_n_02_sharp_turn_failure.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."
)