Files
palladium/studies/202512_TEI_Genesys_CX_Cloud/tests/test_scenarios.py
Robert Helewka e88449d15a Migrate Genesys CX Cloud TEI study to the pattern; retire Streamlit app
studies/202512_GenesysCX -> studies/202512_TEI_Genesys_CX_Cloud,
rebuilt as pattern Variant 4 (TEI composite reproduction):

- teicalc/ self-contained engine: Forrester's tables as the never-edited
  verbatim anchor (incl. the p.14 typo note and the $0 AI-token line),
  generic model/scenarios/staging carried over from the Amazon Connect
  study, ClientDrivers overlay (agents / weekly interactions / revenue,
  flat composite so no growth re-base) with ai_tokens_annual as a direct
  input for the token line the published study models at $0
- one deliverable notebook (business_case.ipynb): widget-pair sidebar
  drivers incl. the AI-token price, published-vs-overlay KPI columns,
  cash-flow/waterfall/scenario charts, verification gate, backstage JSON
  data appendix
- gate + tests reproduce the published totals within $2: NPV $10.8M /
  ROI 266% (engine $10,783,466 / 265.79%; payback 3.3 months, not
  headlined in the PDF); 29 study tests, headless nbconvert green,
  stage simulation leak-free, exports carry the appendix
- old Athena workflow (00_provision..04_export, config.py, seed_data.py,
  PALLADIUM_GENESYSCX_* keys, ATHENA_EXPECTED reconciliation) deleted;
  git history preserves it

With the last legacy study migrated, the retirement lands too:
- app/ (Streamlit UI) and core/notebook_helpers deleted; nothing else
  imported them
- streamlit stripped from pyproject extras, requirements.txt, Makefile;
  .env.example reduced to the Athena keys; 00_setup.ipynb and
  core/bootstrap.py repointed at the pattern studies
- root README reworked: self-contained studies + slim core/ Athena
  toolkit (tei_client, calculations, export, cli)

All suites green: Genesys 29, Amazon Connect 27, CTM 55, template 7,
root 58.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 16:38:51 -04:00

75 lines
2.5 KiB
Python

"""Scenario pins — hand-checked composite results per scenario, clamp
behaviour, and copy semantics."""
import pytest
from teicalc import (
BENEFITS_VERBATIM,
COSTS_VERBATIM,
SCENARIOS,
apply_scenario,
compute_summary,
)
def _summary(scenario):
return compute_summary(
apply_scenario(BENEFITS_VERBATIM, scenario),
apply_scenario(COSTS_VERBATIM, scenario),
0.10,
)
def test_scenario_definitions():
assert SCENARIOS == {
"conservative": {"adoption": 0.80, "risk_delta": 0.10},
"moderate": {"adoption": 1.00, "risk_delta": 0.00},
"aggressive": {"adoption": 1.15, "risk_delta": -0.05},
}
def test_moderate_is_identity():
got = _summary("moderate")
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)
def test_conservative_pins():
s = _summary("conservative")
assert s["benefits_pv"] == pytest.approx(10_543_493.91, abs=1)
assert s["costs_pv"] == pytest.approx(3_026_631.40, abs=1)
assert s["npv"] == pytest.approx(7_516_862.51, abs=1)
assert s["roi_pct"] == pytest.approx(248.36, abs=0.01)
assert s["payback_months"] == pytest.approx(3.464, abs=0.001)
def test_aggressive_pins():
s = _summary("aggressive")
assert s["benefits_pv"] == pytest.approx(18_021_962.25, abs=1)
assert s["costs_pv"] == pytest.approx(4_883_285.09, abs=1)
assert s["npv"] == pytest.approx(13_138_677.16, abs=1)
assert s["roi_pct"] == pytest.approx(269.05, abs=0.01)
assert s["payback_months"] == pytest.approx(3.294, abs=0.001)
def test_risk_delta_clamps_at_zero():
"""Conservative subtracts 0.10 from cost risk; every cost rf clamps to 0
(licenses 0.05, implementation 0.10, ongoing 0.10, tokens 0.0)."""
rows = apply_scenario(COSTS_VERBATIM, "conservative")
assert all(r["risk_adjustment"] == 0.0 for r in rows)
impl = next(r for r in rows if r["field_key"] == "implementation")
assert impl["initial"] == pytest.approx(1_190_000 * 0.80) # adoption scales initial
def test_unknown_scenario_raises():
with pytest.raises(KeyError):
apply_scenario(BENEFITS_VERBATIM, "wildly_optimistic")
def test_inputs_not_mutated():
apply_scenario(BENEFITS_VERBATIM, "aggressive")
apply_scenario(COSTS_VERBATIM, "conservative")
assert BENEFITS_VERBATIM[0]["year_values"]["1"] == 680_000
assert COSTS_VERBATIM[1]["initial"] == 1_190_000