"""Unit tests for `e2e/runner/helpers/fc_proxy_runtime.py` (AZ-596).""" from __future__ import annotations import json from pathlib import Path import pytest from e2e.runner.helpers import fc_proxy_runtime as fpr def _schedule_payload( *, window_start_ms: int = 10_000, window_end_ms: int = 25_000, spoof_frame_count: int = 5, ) -> dict: """Build a minimally valid `schedule.json` payload that `BlackoutSpoofProxy.from_schedule_file` accepts.""" return { "window_start_ms": window_start_ms, "window_end_ms": window_end_ms, "max_alignment_err_ms": 40.0, "spoof_gps": [ { "monotonic_ms": window_start_ms + (i * 200), "lat_deg": 50.0 + (i * 0.0001), "lon_deg": 36.2 + (i * 0.0001), "alt_m": 200.0, "fix_type": 3, "hdop": 1.0, } for i in range(spoof_frame_count) ], } def _write_schedule(path: Path, payload: dict) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(payload)) # AC-1: schedule load + error branches def test_missing_schedule_raises_file_not_found(tmp_path: Path): # Assert with pytest.raises(FileNotFoundError, match="schedule.json not found"): fpr.drive_fc_proxy(tmp_path / "nope.json") def test_malformed_json_raises_value_error(tmp_path: Path): # Arrange bad = tmp_path / "schedule.json" bad.write_text("{not valid json") # Assert with pytest.raises(ValueError, match="malformed schedule JSON"): fpr.drive_fc_proxy(bad) def test_happy_path_returns_well_formed_report(tmp_path: Path): # Arrange schedule = tmp_path / "schedule.json" _write_schedule(schedule, _schedule_payload(spoof_frame_count=7)) # Act report = fpr.drive_fc_proxy(schedule) # Assert assert report.schedule_path == str(schedule) assert report.window_start_ms == 10_000 assert report.window_end_ms == 25_000 assert report.spoof_frame_count == 7 assert report.alignment_err_ms == 0 assert report.was_replay_mode is True # AC-2: now_ms_provider activation + alignment_err_ms def test_now_ms_provider_activates_proxy_and_reports_alignment(tmp_path: Path): # Arrange schedule = tmp_path / "schedule.json" _write_schedule(schedule, _schedule_payload(window_start_ms=5_000)) def clock() -> int: return 5_002 # 2 ms drift from window_start_ms # Act report = fpr.drive_fc_proxy(schedule, now_ms_provider=clock) # Assert assert report.alignment_err_ms == 0 # `activate(...)` with no first_blackout_ms anchors at `now` assert report.was_replay_mode is False def test_now_ms_provider_with_replay_mode_false_distinguishes_from_default(tmp_path: Path): # Arrange schedule = tmp_path / "schedule.json" _write_schedule(schedule, _schedule_payload()) # Act replay_report = fpr.drive_fc_proxy(schedule) live_report = fpr.drive_fc_proxy(schedule, now_ms_provider=lambda: 12_345) # Assert assert replay_report.was_replay_mode is True assert live_report.was_replay_mode is False # AC-3: replay_dir / E2E_SITL_REPLAY_DIR JSON write def test_writes_report_when_replay_dir_supplied(tmp_path: Path): # Arrange schedule = tmp_path / "schedule.json" _write_schedule(schedule, _schedule_payload(spoof_frame_count=3)) replay_dir = tmp_path / "replay" # Act fpr.drive_fc_proxy(schedule, replay_dir=replay_dir) # Assert report_path = replay_dir / "proxy_drive_report.json" assert report_path.is_file() written = json.loads(report_path.read_text()) assert written["spoof_frame_count"] == 3 assert written["was_replay_mode"] is True def test_writes_report_when_env_var_set( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): # Arrange schedule = tmp_path / "schedule.json" _write_schedule(schedule, _schedule_payload()) env_dir = tmp_path / "from-env" monkeypatch.setenv("E2E_SITL_REPLAY_DIR", str(env_dir)) # Act fpr.drive_fc_proxy(schedule) # Assert assert (env_dir / "proxy_drive_report.json").is_file() def test_explicit_replay_dir_overrides_env_var( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): # Arrange schedule = tmp_path / "schedule.json" _write_schedule(schedule, _schedule_payload()) env_dir = tmp_path / "from-env" explicit_dir = tmp_path / "explicit" monkeypatch.setenv("E2E_SITL_REPLAY_DIR", str(env_dir)) # Act fpr.drive_fc_proxy(schedule, replay_dir=explicit_dir) # Assert assert (explicit_dir / "proxy_drive_report.json").is_file() assert not (env_dir / "proxy_drive_report.json").exists() def test_no_file_written_when_neither_supplied( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): # Arrange schedule = tmp_path / "schedule.json" _write_schedule(schedule, _schedule_payload()) monkeypatch.delenv("E2E_SITL_REPLAY_DIR", raising=False) # Act fpr.drive_fc_proxy(schedule) # Assert: nothing written next to the schedule (the only writable dir) assert list(tmp_path.iterdir()) == [schedule] def test_no_file_written_when_env_var_empty( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): # Arrange schedule = tmp_path / "schedule.json" _write_schedule(schedule, _schedule_payload()) monkeypatch.setenv("E2E_SITL_REPLAY_DIR", " ") # Act fpr.drive_fc_proxy(schedule) # Assert assert list(tmp_path.iterdir()) == [schedule] def test_replay_dir_is_created_when_missing(tmp_path: Path): # Arrange schedule = tmp_path / "schedule.json" _write_schedule(schedule, _schedule_payload()) replay_dir = tmp_path / "deep" / "nested" / "replay" # Act fpr.drive_fc_proxy(schedule, replay_dir=replay_dir) # Assert assert (replay_dir / "proxy_drive_report.json").is_file()