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

1"""Maintenance-contract test for PriceBreakdown. 

2 

3Section 2 of the TTS pricing design doc pins this rule: 

4 

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. 

8 

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""" 

13 

14from __future__ import annotations 

15 

16import dataclasses 

17 

18from voice_prices.types import ModelPrice, PriceBreakdown 

19 

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} 

49 

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'} 

53 

54 

55def _model_price_field_names() -> set[str]: 

56 return {f.name for f in dataclasses.fields(ModelPrice)} 

57 

58 

59def _breakdown_field_names() -> set[str]: 

60 return {f.name for f in dataclasses.fields(PriceBreakdown)} 

61 

62 

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 

71 

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 ) 

77 

78 

79def test_breakdown_covers_priced_fields(): 

80 """Every priced ModelPrice field has a matching PriceBreakdown field.""" 

81 breakdown_fields = _breakdown_field_names() 

82 

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 ) 

89 

90 assert not missing, '\n'.join(missing) 

91 

92 

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() 

96 

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 ) 

105 

106 assert not missing, '\n'.join(missing) 

107 

108 

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) 

120 

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 )