"""Toy-model acceptance pins — hand-checked, like every study must have. Every number here was computed by hand before it was pinned. When you replace the toy domain, replace these with hand-checks of YOUR math; the in-notebook verification gate is the second layer, not a substitute. """ from __future__ import annotations import pytest from studylib import model as m def test_contracted_overlays_verbatim(): assert m.anchor("platform_annual") == 500_000 # signed contract assert m.ANCHOR_VERBATIM["platform_annual"] == 600_000 # pitch record intact assert m.anchor("baseline_annual") == m.ANCHOR_VERBATIM["baseline_annual"] def test_ramp_mechanics(): assert m.platform_costs_by_year() == {2026: 300_000, 2027: 600_000, 2028: 600_000} assert m.platform_costs_by_year(12)[2026] == 0.0 assert m.platform_costs_by_year(0)[2026] == 600_000.0 assert m.platform_costs_by_year(annual=m.anchor("platform_annual")) == \ {2026: 250_000, 2027: 500_000, 2028: 500_000} def test_benefits_phase_on_the_schedule(): ben = m.benefits_by_year() assert ben[2026] == 0.0 # nothing lands in year 1 assert ben[2027] == pytest.approx(900_000 * 6 / 18) # months 19-24 assert ben[2028] == pytest.approx(900_000 * 12 / 18) assert sum(ben.values()) == pytest.approx(m.anchor("benefit_3yr")) def _frame(annual): plat = m.platform_costs_by_year(annual=annual) total = {y: m.current_costs_by_year()[y] + plat[y] + m.services_by_year()[y] for y in m.YEARS} return m.case_flows(total, m.benefits_by_year()) def test_contracted_frame_kpis(): inc, net = _frame(m.anchor("platform_annual")) assert sum(net.values()) == pytest.approx(400_000) kpi = m.case_kpis(inc, net) assert kpi["roi"] == pytest.approx(0.8) assert kpi["npv"] == pytest.approx(206_612, abs=1) # @10%, hand-checked assert kpi["payback"] == "32 months (~Aug 2028)" def test_vendor_frame_kpis(): inc, net = _frame(None) # verbatim pitch rate assert sum(net.values()) == pytest.approx(150_000) assert m.case_kpis(inc, net)["payback"] == "35 months (~Nov 2028)" def test_payback_edges(): assert m.payback_label({2026: 1.0, 2027: 0.0, 2028: 0.0}) == "immediate" assert m.payback_label({2026: -1.0, 2027: -1.0, 2028: -1.0}) == "beyond 2028"