"""Client-overlay pins — identity at the composite, linear per-driver scaling, growth re-basing, and copy semantics.""" import dataclasses import pytest from teicalc import ( BENEFIT_DRIVERS, BENEFITS_VERBATIM, COMPOSITE, COST_DRIVERS, COSTS_VERBATIM, ClientDrivers, compute_summary, growth_multiplier, overlay_rows, scale_factor, ) def _row(rows, key): return next(r for r in rows if r["field_key"] == key) def test_identity_at_composite(): """overlay_rows(COMPOSITE) reproduces the verbatim study to the cent.""" ob, oc = overlay_rows(COMPOSITE) got = compute_summary(ob, oc, 0.10) want = compute_summary(BENEFITS_VERBATIM, COSTS_VERBATIM, 0.10) assert got["benefits_pv"] == pytest.approx(want["benefits_pv"], abs=0.01) assert got["costs_pv"] == pytest.approx(want["costs_pv"], abs=0.01) assert got["npv"] == pytest.approx(want["npv"], abs=0.01) def test_driver_map_covers_every_row(): assert set(BENEFIT_DRIVERS) == {r["field_key"] for r in BENEFITS_VERBATIM} assert set(COST_DRIVERS) == {r["field_key"] for r in COSTS_VERBATIM} def test_scale_factor(): d = ClientDrivers(agents_fte=1_000, annual_contacts_y1=40_000_000) assert scale_factor("agents", d) == pytest.approx(0.5) assert scale_factor("contacts", d) == pytest.approx(2.0) assert scale_factor("fixed", d) == 1.0 with pytest.raises(KeyError): scale_factor("revenue", d) def test_half_agents_halves_agent_rows_only(): ob, oc = overlay_rows(ClientDrivers(agents_fte=1_000)) assert _row(ob, "ai_forecasting_supervision")["year_values"]["1"] == \ pytest.approx(6_651_680 / 2) assert _row(ob, "legacy_solution_savings")["year_values"]["1"] == \ pytest.approx(6_177_600 / 2) # Contact-driven and fixed rows unmoved. assert _row(ob, "ai_contact_resolution")["year_values"]["1"] == \ pytest.approx(13_911_040) assert _row(oc, "amazon_connect_usage")["year_values"]["1"] == \ pytest.approx(6_456_448) assert _row(oc, "implementation_migration")["initial"] == 1_087_500 def test_double_contacts_doubles_usage_only(): _, oc = overlay_rows(ClientDrivers(annual_contacts_y1=40_000_000)) assert _row(oc, "amazon_connect_usage")["year_values"]["1"] == \ pytest.approx(6_456_448 * 2) assert _row(oc, "implementation_migration")["year_values"]["1"] == \ pytest.approx(188_333) assert _row(oc, "ongoing_management")["year_values"]["1"] == \ pytest.approx(256_200) def test_growth_rebase(): assert growth_multiplier(1, 0.0) == 1.0 # Y1 always 1.0 assert growth_multiplier(2, 0.0) == pytest.approx(1 / 1.3) assert growth_multiplier(3, 0.0) == pytest.approx((1 / 1.3) ** 2) assert growth_multiplier(3, 0.30) == 1.0 # composite growth = identity ob, oc = overlay_rows(ClientDrivers(growth_rate=0.0)) row = _row(ob, "ai_contact_resolution") assert row["year_values"]["1"] == pytest.approx(13_911_040) assert row["year_values"]["2"] == pytest.approx(23_932_480 / 1.3) assert row["year_values"]["3"] == pytest.approx(37_797_760 / 1.3**2) # Fixed rows ignore the growth re-base too. assert _row(oc, "ongoing_management")["year_values"]["2"] == pytest.approx(187_200) def test_drivers_frozen_and_rows_are_copies(): with pytest.raises(dataclasses.FrozenInstanceError): COMPOSITE.agents_fte = 1 # type: ignore[misc] ob, oc = overlay_rows(COMPOSITE) ob[0]["year_values"]["1"] = -1 oc[0]["year_values"]["1"] = -1 assert BENEFITS_VERBATIM[0]["year_values"]["1"] == 13_911_040 assert COSTS_VERBATIM[0]["year_values"]["1"] == 6_456_448