"""Cartesian scenario builder + strategy/regime factory.""" from decimal import Decimal import pytest from fire_planner.scenarios import ( DEFAULT_GLIDES, DEFAULT_JURISDICTIONS, DEFAULT_LEAVE_YEARS, DEFAULT_STRATEGIES, ScenarioSpec, build_regime_schedule, build_strategy, cartesian_scenarios, ) from fire_planner.strategies.guyton_klinger import GuytonKlingerStrategy from fire_planner.strategies.trinity import TrinityStrategy from fire_planner.strategies.vpw import VpwStrategy, VpwWithFloorStrategy from fire_planner.tax.bulgaria import BulgariaTaxRegime from fire_planner.tax.cyprus import CyprusTaxRegime from fire_planner.tax.uae import UaeTaxRegime from fire_planner.tax.uk import UkTaxRegime def test_default_cartesian_count_is_120() -> None: specs = cartesian_scenarios(spending_gbp=Decimal("100000"), nw_seed_gbp=Decimal("1000000")) expected = (len(DEFAULT_JURISDICTIONS) * len(DEFAULT_STRATEGIES) * len(DEFAULT_LEAVE_YEARS) * len(DEFAULT_GLIDES)) assert expected == 120 assert len(specs) == 120 def test_external_id_format() -> None: spec = ScenarioSpec( jurisdiction="cyprus", strategy="vpw", leave_uk_year=3, glide_path="rising", spending_gbp=Decimal("100000"), nw_seed_gbp=Decimal("1000000"), ) assert spec.external_id == "cyprus-vpw-leave-y3-glide-rising" def test_cartesian_unique_external_ids() -> None: specs = cartesian_scenarios(spending_gbp=Decimal("100000"), nw_seed_gbp=Decimal("1000000")) ids = [s.external_id for s in specs] assert len(ids) == len(set(ids)) def test_build_strategy_dispatch() -> None: assert isinstance(build_strategy("trinity"), TrinityStrategy) assert isinstance(build_strategy("guyton_klinger"), GuytonKlingerStrategy) assert isinstance(build_strategy("vpw"), VpwStrategy) def test_build_strategy_vpw_floor_requires_floor() -> None: s = build_strategy("vpw_floor", floor=40_000.0) assert isinstance(s, VpwWithFloorStrategy) assert s.floor == 40_000.0 def test_build_strategy_vpw_floor_missing_floor_raises() -> None: with pytest.raises(ValueError): build_strategy("vpw_floor") def test_build_strategy_unknown_raises() -> None: with pytest.raises(KeyError): build_strategy("walmart") def test_build_regime_schedule_uae() -> None: fn = build_regime_schedule("uae", leave_uk_year=2) assert isinstance(fn(0), UkTaxRegime) assert isinstance(fn(1), UkTaxRegime) assert isinstance(fn(2), UaeTaxRegime) assert isinstance(fn(50), UaeTaxRegime) def test_build_regime_schedule_uk_constant() -> None: fn = build_regime_schedule("uk", leave_uk_year=3) # All years should resolve to UK assert isinstance(fn(0), UkTaxRegime) assert isinstance(fn(50), UkTaxRegime) def test_build_regime_schedule_cyprus_switches_at_leave_year() -> None: fn = build_regime_schedule("cyprus", leave_uk_year=3) assert isinstance(fn(0), UkTaxRegime) assert isinstance(fn(2), UkTaxRegime) assert isinstance(fn(3), CyprusTaxRegime) assert isinstance(fn(50), CyprusTaxRegime) def test_build_regime_schedule_bulgaria() -> None: fn = build_regime_schedule("bulgaria", leave_uk_year=1) assert isinstance(fn(0), UkTaxRegime) assert isinstance(fn(1), BulgariaTaxRegime) def test_build_regime_schedule_unknown_raises() -> None: with pytest.raises(KeyError): build_regime_schedule("madeupistan", leave_uk_year=3) def test_cartesian_unknown_glide_raises() -> None: with pytest.raises(KeyError): cartesian_scenarios( spending_gbp=Decimal("100000"), nw_seed_gbp=Decimal("1000000"), glides=("staircase", ), )