Coverage for tests/test_price_breakdown_contract.py: 92.45%
39 statements
« prev ^ index » next coverage.py v7.9.2, created at 2026-09-27 04:08 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2026-09-27 04:08 +0000
1"""Maintenance-contract test for PriceBreakdown.
3Section 2 of the TTS pricing design doc pins this rule:
5 Every Decimal-typed priced field on ModelPrice MUST have a matching Decimal
6 field on PriceBreakdown. When ModelPrice gains a new priced field, add the
7 corresponding field here in the same PR.
9This test walks ModelPrice's dataclass fields at runtime and asserts each
10priced field has its counterpart on PriceBreakdown. Drift across an upstream
11merge will fail CI rather than silently shipping an underspecified breakdown.
12"""
14from __future__ import annotations
16import dataclasses
18from voice_prices.types import ModelPrice, PriceBreakdown
20# Mapping from a priced ModelPrice field name to:
21# (main_breakdown_field, optional_multiplier_eligible_adjustment_field_or_None)
22#
23# Multiplier-eligible fields (input_kchars, output_audio_kseconds) additionally
24# require their paired voice_class_*_adjustment slot to exist on PriceBreakdown.
25_PRICED_FIELD_MAP: dict[str, tuple[str, str | None]] = {
26 # token-based fields
27 'input_mtok': ('input_tokens', None),
28 'output_mtok': ('output_tokens', None),
29 'cache_read_mtok': ('cache_read_tokens', None),
30 'cache_write_mtok': ('cache_write_tokens', None),
31 'input_audio_mtok': ('input_audio_tokens', None),
32 'output_audio_mtok': ('output_audio_tokens', None),
33 'cache_audio_read_mtok': ('cache_audio_read_tokens', None),
34 # request-count field
35 'requests_kcount': ('requests', None),
36 # TTS character/audio-second fields with multiplier pairs
37 'input_kchars': ('input_kchars', 'voice_class_input_adjustment'),
38 'output_audio_kseconds': ('output_audio_kseconds', 'voice_class_output_adjustment'),
39 # STT input duration; intentionally multiplier-exempt (no paired adjustment).
40 'input_audio_kseconds': ('input_audio_kseconds', None),
41 # Bundled voice-agent session time. Multiplier-exempt and direction-exempt: a bundled
42 # minute is not attributable to input or output, so it sums into total alongside requests.
43 'agent_kminutes': ('agent_kminutes', None),
44 # Connected call minutes on the phone network. Direction-exempt for the same reason, and
45 # deliberately separate from agent_kminutes: a phone call on a bundled platform pays both,
46 # so one field for the two would double-count the same minute.
47 'telephony_kminutes': ('telephony_kminutes', None),
48}
50# Fields on ModelPrice that are NOT priced (and thus should not appear in the map).
51# This guards against accidentally classifying a configuration field as priced.
52_NON_PRICED_MODEL_PRICE_FIELDS: set[str] = {'voice_multipliers'}
55def _model_price_field_names() -> set[str]:
56 return {f.name for f in dataclasses.fields(ModelPrice)}
59def _breakdown_field_names() -> set[str]:
60 return {f.name for f in dataclasses.fields(PriceBreakdown)}
63def test_priced_field_map_covers_every_modelprice_field():
64 """Every field on ModelPrice is either priced (in the map) or explicitly
65 excluded as non-priced configuration. Drift here means a new field landed
66 without a deliberate decision about its breakdown counterpart.
67 """
68 model_price_fields = _model_price_field_names()
69 accounted_for = set(_PRICED_FIELD_MAP) | _NON_PRICED_MODEL_PRICE_FIELDS
70 unaccounted = model_price_fields - accounted_for
72 assert not unaccounted, (
73 f'ModelPrice fields not classified as priced or non-priced: {unaccounted!r}. '
74 f'Add to _PRICED_FIELD_MAP or _NON_PRICED_MODEL_PRICE_FIELDS in '
75 f'test_price_breakdown_contract.py.'
76 )
79def test_breakdown_covers_priced_fields():
80 """Every priced ModelPrice field has a matching PriceBreakdown field."""
81 breakdown_fields = _breakdown_field_names()
83 missing: list[str] = []
84 for model_price_field, (breakdown_field, _) in _PRICED_FIELD_MAP.items():
85 if breakdown_field not in breakdown_fields: 85 ↛ 86line 85 didn't jump to line 86 because the condition on line 85 was never true
86 missing.append(
87 f'ModelPrice.{model_price_field} requires PriceBreakdown.{breakdown_field} (maintenance contract).'
88 )
90 assert not missing, '\n'.join(missing)
93def test_breakdown_covers_multiplier_eligible_adjustments():
94 """Every multiplier-eligible priced field also has its paired adjustment slot."""
95 breakdown_fields = _breakdown_field_names()
97 missing: list[str] = []
98 for model_price_field, (_, adjustment_field) in _PRICED_FIELD_MAP.items():
99 if adjustment_field is None:
100 continue
101 if adjustment_field not in breakdown_fields: 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true
102 missing.append(
103 f'ModelPrice.{model_price_field} is multiplier-eligible and requires PriceBreakdown.{adjustment_field}.'
104 )
106 assert not missing, '\n'.join(missing)
109def test_no_orphan_breakdown_fields():
110 """Every PriceBreakdown field maps back to either a main priced field or an
111 adjustment counterpart. Catches drift in the other direction (breakdown gains
112 a field with no ModelPrice source).
113 """
114 breakdown_fields = _breakdown_field_names()
115 expected_breakdown_fields: set[str] = set()
116 for main, adjustment in _PRICED_FIELD_MAP.values():
117 expected_breakdown_fields.add(main)
118 if adjustment is not None:
119 expected_breakdown_fields.add(adjustment)
121 orphans = breakdown_fields - expected_breakdown_fields
122 assert not orphans, (
123 f'PriceBreakdown fields with no source in ModelPrice: {orphans!r}. '
124 f'Either add a priced field on ModelPrice or remove the orphan from PriceBreakdown.'
125 )