fire-planner/tests/test_scenarios.py
2026-05-07 17:06:19 +00:00

113 lines
3.7 KiB
Python

"""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", ),
)