"""Simulator + COL integration — verifies `_resolve_col_adjustment` is applied to the request before paths are built and surfaced in the result. These tests bypass HTTP and call the resolver directly to keep them fast. """ from __future__ import annotations from decimal import Decimal from fire_planner.api.schemas import SimulateRequest from fire_planner.api.simulate import _resolve_col_adjustment def _req(**overrides: object) -> SimulateRequest: base = dict( jurisdiction="uk", strategy="trinity", leave_uk_year=0, spending_gbp=Decimal("85000"), nw_seed_gbp=Decimal("1050000"), horizon_years=73, ) base.update(overrides) return SimulateRequest(**base) # type: ignore[arg-type] def test_col_default_on_for_known_jurisdiction() -> None: """Default config + cyprus jurisdiction → multiplier ~0.67.""" req = _req(jurisdiction="cyprus", leave_uk_year=2) adj, mult, adj_spend, city = _resolve_col_adjustment(req) assert mult is not None and Decimal("0.55") < mult < Decimal("0.75") assert city == "limassol" assert adj_spend is not None and adj_spend < Decimal("85000") assert adj.spending_gbp == adj_spend # the simulator runs on the adjusted figure def test_col_off_returns_unchanged_request() -> None: req = _req(jurisdiction="cyprus", leave_uk_year=2, col_auto_adjust=False) adj, mult, adj_spend, city = _resolve_col_adjustment(req) assert mult is None assert adj_spend is None assert city is None assert adj.spending_gbp == Decimal("85000") # The returned request is the same instance — no copy when no-op. assert adj is req def test_col_nomad_jurisdiction_skipped() -> None: """Nomad has no representative city — auto-adjust should silently skip.""" req = _req(jurisdiction="nomad", leave_uk_year=2) adj, mult, adj_spend, city = _resolve_col_adjustment(req) assert mult is None assert adj_spend is None assert city is None # no representative city for nomad def test_col_uk_to_uk_identity_returns_no_multiplier() -> None: """UK staying in UK is identity — surface the city but no scaling.""" req = _req(jurisdiction="uk", leave_uk_year=0) adj, mult, adj_spend, city = _resolve_col_adjustment(req) assert mult is None assert adj_spend is None assert city == "london" assert adj.spending_gbp == Decimal("85000") def test_col_explicit_target_city_overrides_jurisdiction_default() -> None: """User picks Sofia explicitly even though jurisdiction is cyprus.""" req = _req(jurisdiction="cyprus", leave_uk_year=2, col_target_city="sofia") adj, mult, adj_spend, city = _resolve_col_adjustment(req) assert city == "sofia" # sofia ratio ~0.41 — should be smaller than the limassol default assert mult is not None and mult < Decimal("0.50") def test_col_unknown_city_degrades_gracefully() -> None: """Unknown city → skip, do not raise — Phase-2 scraper will close gap.""" req = _req(jurisdiction="cyprus", leave_uk_year=2, col_target_city="atlantis") adj, mult, adj_spend, city = _resolve_col_adjustment(req) assert mult is None assert adj_spend is None assert city == "atlantis" # the requested name is still echoed assert adj.spending_gbp == Decimal("85000") def test_col_bangkok_dramatic_discount() -> None: req = _req(jurisdiction="thailand", leave_uk_year=2) adj, mult, adj_spend, city = _resolve_col_adjustment(req) assert city == "bangkok" assert mult is not None and mult < Decimal("0.35") assert adj_spend is not None and adj_spend < Decimal("30000") # £85k → ~£24k