"""End-to-end pricing, hand-checked. The reference scenario below is the one the notebook ships as its default. Every figure was computed by hand from the published rates before it was pinned here. """ from __future__ import annotations import json import pytest from genesyscalc.appendix import result_json from genesyscalc.meters import LicenceModel, TokenPrice from genesyscalc.model import ( CalculatorInputs, calculate, compare, cost_per_interaction, metered_units, ) from genesyscalc.ratecard import RATE_CARD_SOURCE_DATE from genesyscalc.tts import TtsUsage from genesyscalc.usage import DeflectionMix, VoiceBotUsage, Volumes REFERENCE_ENABLED = frozenset( { "agent_copilot_named", "speech_and_text_analytics_named", "bots_voice", "virtual_agent", "agentic_virtual_agent", "ai_summary_and_insights", "ai_scoring", "whatsapp_messaging", "predictive_engagement", } ) @pytest.fixture def volumes() -> Volumes: return Volumes( voice_inbound_monthly=100_000, voice_outbound_monthly=20_000, digital_sessions_monthly=8_000, email_monthly=12_000, messaging_monthly=6_000, evaluations_monthly=2_000, ) @pytest.fixture def reference(volumes: Volumes) -> CalculatorInputs: return CalculatorInputs( volumes=volumes, users=500, licence=LicenceModel.NAMED, enabled=REFERENCE_ENABLED, mix=DeflectionMix( bot_only_share=0.30, virtual_agent_share=0.15, agentic_va_share=0.05 ), price=TokenPrice(), voice_bot=VoiceBotUsage( calls_per_month=30_000, avg_bot_seconds_per_call=40 ), tts=TtsUsage(calls_monthly=100_000, chars_per_call=2_000), ) # ── the hand-checked reference scenario ────────────────────────────────── def test_subscription_lines(reference: CalculatorInputs) -> None: """500 users: Copilot 500×40 = 20,000; STA 500×30 = 15,000 tokens/mo.""" by_key = {line.key: line for line in calculate(reference).lines} assert by_key["agent_copilot_named"].tokens_monthly == pytest.approx(20_000.0) assert by_key["speech_and_text_analytics_named"].tokens_monthly == pytest.approx( 15_000.0 ) assert by_key["agent_copilot_named"].cost_annual == pytest.approx(240_000.0) def test_voice_bot_line(reference: CalculatorInputs) -> None: """30,000 calls × ceil(40/15)×15s = 45s = 0.75 min → 22,500 min. 22,500 / 17 = 1,323.53 tokens/mo → $15,882.35/yr at $1.00.""" by_key = {line.key: line for line in calculate(reference).lines} assert by_key["bots_voice"].units_monthly == pytest.approx(22_500.0) assert by_key["bots_voice"].tokens_monthly == pytest.approx(1_323.529412, rel=1e-6) assert by_key["bots_voice"].cost_annual == pytest.approx(15_882.35, abs=0.01) def test_virtual_agent_lines_use_the_partition(reference: CalculatorInputs) -> None: """120,000 voice: VA 15% = 18,000 × 0.5 = 9,000 tokens; AVA 5% = 6,000 × 1.2 = 7,200 tokens.""" by_key = {line.key: line for line in calculate(reference).lines} assert by_key["virtual_agent"].units_monthly == pytest.approx(18_000.0) assert by_key["virtual_agent"].tokens_monthly == pytest.approx(9_000.0) assert by_key["agentic_virtual_agent"].units_monthly == pytest.approx(6_000.0) assert by_key["agentic_virtual_agent"].tokens_monthly == pytest.approx(7_200.0) def test_summary_is_zeroed_because_copilot_is_on(reference: CalculatorInputs) -> None: result = calculate(reference) by_key = {line.key: line for line in result.lines} assert by_key["ai_summary_and_insights"].units_monthly == 0.0 assert by_key["ai_summary_and_insights"].cost_annual == 0.0 assert any("Agent Copilot is enabled" in w for w in result.warnings) def test_zero_rated_line_is_shown_not_hidden(reference: CalculatorInputs) -> None: """A published $0 is a finding — it renders as a line, at zero.""" by_key = {line.key: line for line in calculate(reference).lines} assert "predictive_engagement" in by_key assert by_key["predictive_engagement"].units_monthly > 0 assert by_key["predictive_engagement"].cost_annual == 0.0 def test_reference_totals(reference: CalculatorInputs) -> None: """consumption 17,638.53 → ceil 17,639; + 35,000 subscription = 52,639; − 250 allowance = 52,389 billable → $52,389/mo → $628,668/yr. TTS 200M chars, 20% VA-free → 160M → $3,200/mo → $38,400/yr. Grand total $667,068/yr.""" totals = calculate(reference).totals assert totals.consumption_tokens_monthly == pytest.approx(17_638.529412, rel=1e-6) assert totals.subscription_tokens_monthly == pytest.approx(35_000.0) assert totals.gross_tokens_monthly == pytest.approx(52_639.0) assert totals.allowance_tokens_monthly == 250 assert totals.billable_tokens_monthly == 52_389 assert totals.token_cost_annual == pytest.approx(628_668.0) def test_reference_grand_total_splits_tokens_from_tts( reference: CalculatorInputs, ) -> None: result = calculate(reference) assert result.tts_cost_annual == pytest.approx(38_400.0) assert result.grand_total_annual == pytest.approx(667_068.0) assert result.grand_total_annual == pytest.approx( result.token_cost_annual + result.tts_cost_annual ) def test_cost_per_interaction(reference: CalculatorInputs) -> None: """$667,068 / (146,000 × 12) = $0.3807 per interaction.""" result = calculate(reference) assert cost_per_interaction(result, reference.volumes) == pytest.approx( 0.380747, rel=1e-5 ) def test_roundup_warning_is_reported(reference: CalculatorInputs) -> None: assert any("15-second per-call round-up" in w for w in calculate(reference).warnings) # ── structural ties that must hold at any setting ──────────────────────── def test_lines_sum_to_the_gross_token_count(reference: CalculatorInputs) -> None: result = calculate(reference) assert sum(line.tokens_monthly for line in result.lines) == pytest.approx( result.totals.consumption_tokens_monthly + result.totals.subscription_tokens_monthly ) def test_effective_cost_never_exceeds_list(reference: CalculatorInputs) -> None: discounted = reference.with_(price=TokenPrice(concession_pct=0.4)) result = calculate(discounted) assert result.totals.token_cost_annual <= result.totals.list_cost_annual assert result.totals.concession_saving_annual >= 0 def test_allowance_only_ever_reduces_the_bill(reference: CalculatorInputs) -> None: with_allowance = calculate(reference).totals.billable_tokens_monthly without = calculate( reference.with_(apply_allowance=False) ).totals.billable_tokens_monthly assert with_allowance == without - 250 def test_allowance_absorbing_a_month_is_reported() -> None: quiet = CalculatorInputs( volumes=Volumes(voice_inbound_monthly=100), users=0, enabled=frozenset({"ai_scoring"}), ) result = calculate(quiet) assert result.totals.billable_tokens_monthly == 0 assert any("free monthly allowance" in w for w in result.warnings) def test_unknown_feature_key_rejected(volumes: Volumes) -> None: with pytest.raises(ValueError, match="not published meters"): CalculatorInputs(volumes=volumes, users=1, enabled=frozenset({"telepathy"})) def test_metered_units_covers_every_enabled_key(reference: CalculatorInputs) -> None: assert set(metered_units(reference)) == set(reference.enabled) def test_empty_scenario_costs_nothing() -> None: result = calculate(CalculatorInputs(volumes=Volumes(), users=0)) assert result.grand_total_annual == 0.0 assert result.lines == () # ── licence model, scenarios ───────────────────────────────────────────── def test_concurrent_licence_costs_more_per_user(reference: CalculatorInputs) -> None: """Copilot 40→60 and STA 30→45, so 35,000 → 52,500 subscription tokens.""" concurrent = reference.with_( licence=LicenceModel.CONCURRENT, enabled=frozenset( {"agent_copilot_named", "speech_and_text_analytics_named"} ), ) named = reference.with_( enabled=frozenset({"agent_copilot_named", "speech_and_text_analytics_named"}) ) assert calculate(named).totals.subscription_tokens_monthly == pytest.approx(35_000.0) assert calculate(concurrent).totals.subscription_tokens_monthly == pytest.approx( 52_500.0 ) def test_compare_is_monotone_in_deflection(reference: CalculatorInputs) -> None: """More virtual-agent deflection means more consumption tokens.""" rows = compare( { "Conservative": reference.with_( mix=DeflectionMix(bot_only_share=0.20, virtual_agent_share=0.05) ), "Base": reference, "Aggressive": reference.with_( mix=DeflectionMix( bot_only_share=0.35, virtual_agent_share=0.25, agentic_va_share=0.10, ) ), } ) assert [r["Scenario"] for r in rows] == ["Conservative", "Base", "Aggressive"] totals = [r["Token cost / yr"] for r in rows] assert totals[0] < totals[1] < totals[2] # ── the export payload ─────────────────────────────────────────────────── def test_result_json_round_trips_and_carries_provenance( reference: CalculatorInputs, ) -> None: payload = result_json(calculate(reference), reference, meta={"client": "Example"}) assert json.loads(json.dumps(payload)) == payload assert payload["rate_card"]["source_date"] == RATE_CARD_SOURCE_DATE assert payload["rate_card"]["voice_bot_roundup_seconds"] == 15 assert payload["tts_source"]["source_date"] == "2026-05-22" assert payload["totals"]["grand_total_annual"] == pytest.approx(667_068.0) assert payload["meta"]["client"] == "Example" assert len(payload["lines"]) == len(REFERENCE_ENABLED)