roverdevkit / tests /test_scenarios.py
jjreif's picture
Deploy roverdevkit @ 2676a67
b3d14e3
Raw
History Blame Contribute Delete
3.21 kB
"""Tests for the mission-scenario YAML loader."""
from __future__ import annotations
import pytest
from roverdevkit.mission.scenarios import list_scenarios, load_scenario
from roverdevkit.schema import MissionScenario
from roverdevkit.terramechanics.soils import list_soil_simulants
EXPECTED_SCENARIOS = {
"equatorial_mare_traverse",
"polar_prospecting",
"highland_slope_capability",
"crater_rim_survey",
}
def test_list_scenarios_returns_all_four_canonical_scenarios() -> None:
names = set(list_scenarios())
missing = EXPECTED_SCENARIOS - names
assert not missing, f"missing scenarios: {missing}"
@pytest.mark.parametrize("name", sorted(EXPECTED_SCENARIOS))
def test_load_scenario_round_trips_to_pydantic_model(name: str) -> None:
scenario = load_scenario(name)
assert isinstance(scenario, MissionScenario)
assert scenario.name == name
@pytest.mark.parametrize("name", sorted(EXPECTED_SCENARIOS))
def test_soil_simulant_in_every_scenario_is_in_the_catalogue(name: str) -> None:
# The traverse sim resolves soil names via the catalogue; if a
# scenario references an unknown simulant the evaluator will crash
# later. Catch it at config-load time.
scenario = load_scenario(name)
assert scenario.soil_simulant in list_soil_simulants()
def test_unknown_scenario_raises_file_not_found() -> None:
with pytest.raises(FileNotFoundError, match="scenario config"):
load_scenario("nonexistent_scenario")
def test_equatorial_scenario_has_expected_fields() -> None:
s = load_scenario("equatorial_mare_traverse")
assert s.latitude_deg == pytest.approx(20.2)
assert s.mission_duration_earth_days == pytest.approx(14.0)
assert s.traverse_distance_m > 0
def test_polar_scenario_has_high_latitude() -> None:
s = load_scenario("polar_prospecting")
assert abs(s.latitude_deg) >= 70.0
assert s.sun_geometry == "polar_intermittent"
# ---------------------------------------------------------------------------
# Schema v6 (v6 schema update): operational_duty_cycle calibration on YAMLs
# ---------------------------------------------------------------------------
# Pin the four canonical scenarios to the calibration agreed in
# data/analytical/SCHEMA.md so an accidental YAML edit doesn't
# silently shift the surrogate's training distribution. Mare 0.30,
# crater 0.20, highland 0.15, polar 0.05 — see decision.md §"δ_ops
# calibration" for the published-rover-anchored derivation.
_EXPECTED_DOPS = {
"equatorial_mare_traverse": 0.30,
"crater_rim_survey": 0.20,
"highland_slope_capability": 0.15,
"polar_prospecting": 0.05,
}
@pytest.mark.parametrize("name,expected", sorted(_EXPECTED_DOPS.items()))
def test_canonical_scenario_operational_duty_cycle_matches_calibration(
name: str, expected: float
) -> None:
"""``operational_duty_cycle`` on each canonical scenario YAML matches
the v6 schema update calibration. Catch silent YAML drift before it
contaminates the LHS dataset."""
s = load_scenario(name)
assert s.operational_duty_cycle == pytest.approx(expected, abs=1e-9), (
f"{name}: operational_duty_cycle drifted from the schema-v7 calibration"
)