"""AZ-919 — `_c1_vio_wrapper` builds and forwards an `EskfNominalAltitudeProvider`. Tests the composition-root seam: the AZ-919 plumbing is only useful if the wrapper actually constructs the provider and passes it to ``build_vio_strategy``. The provider's supplier must close over the same mutable ``constructed`` dict that the bootstrap loop populates so it sees the C5 estimator after the topo order builds it. """ from __future__ import annotations from typing import Any import pytest from gps_denied_onboard.config import Config from gps_denied_onboard.helpers.altitude_provider import ( AltitudeProvider, EskfNominalAltitudeProvider, ) from gps_denied_onboard.runtime_root import airborne_bootstrap from gps_denied_onboard.runtime_root.airborne_bootstrap import _c1_vio_wrapper def _minimal_config() -> Config: return Config.with_blocks(c1_vio={"strategy": "klt_ransac"}) def test_wrapper_forwards_altitude_provider_kwarg( monkeypatch: pytest.MonkeyPatch, ) -> None: # Arrange — capture the kwargs handed to build_vio_strategy. captured: dict[str, Any] = {} def _capture(_config: Config, **kwargs: Any) -> object: captured.update(kwargs) return object() monkeypatch.setattr(airborne_bootstrap, "build_vio_strategy", _capture) constructed: dict[str, Any] = {"c13_fdr": object()} # Act _c1_vio_wrapper(_minimal_config(), constructed) # Assert assert "altitude_provider" in captured assert isinstance(captured["altitude_provider"], EskfNominalAltitudeProvider) assert isinstance(captured["altitude_provider"], AltitudeProvider) def test_provider_supplier_resolves_c5_estimator_lazily( monkeypatch: pytest.MonkeyPatch, ) -> None: # Arrange — capture the provider, then mutate the constructed dict # after the wrapper returns to simulate C5 landing in the topo order. captured: dict[str, Any] = {} def _capture(_config: Config, **kwargs: Any) -> object: captured.update(kwargs) return object() monkeypatch.setattr(airborne_bootstrap, "build_vio_strategy", _capture) constructed: dict[str, Any] = {"c13_fdr": object()} _c1_vio_wrapper(_minimal_config(), constructed) provider: EskfNominalAltitudeProvider = captured["altitude_provider"] # Pre-C5 the supplier returns None. pre_c5 = provider._estimator_supplier() # The bootstrap loop later assigns the c5_state slot. sentinel_estimator = object() constructed["c5_state"] = sentinel_estimator post_c5 = provider._estimator_supplier() # Assert assert pre_c5 is None assert post_c5 is sentinel_estimator def test_wrapper_still_requires_c13_fdr( monkeypatch: pytest.MonkeyPatch, ) -> None: # Arrange — AZ-919 must not silently relax the existing AC-618-3 # invariant: an absent c13_fdr key still raises AirborneBootstrapError. monkeypatch.setattr( airborne_bootstrap, "build_vio_strategy", lambda *_a, **_kw: object(), ) # Act + Assert with pytest.raises(airborne_bootstrap.AirborneBootstrapError) as exc: _c1_vio_wrapper(_minimal_config(), constructed={}) assert "c13_fdr" in str(exc.value) assert "c1_vio" in str(exc.value)