test(overrides): RED — thread the Boiler Efficiency Band to the overlay call site

Parse-on-read from the main_heating_system row's original_spreadsheet_description
(ships before FE); an explicit boiler_efficiency_band override row wins when
present; band applied after fuel resolution (oil re-point); no band -> no slot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-29 16:18:53 +00:00
parent 72840cbdf7
commit e91e714221

View file

@ -227,6 +227,92 @@ def test_main_heating_system_row_produces_a_heating_overlay() -> None:
assert overlays[0].heating.sap_main_heating_code == 104
def test_band_parsed_from_the_heating_description_sets_the_efficiency_slot() -> None:
# Ship-before-FE (parse-on-read): the SEDBUK band is already carried in the
# main_heating_system row's original_spreadsheet_description, so the modelling
# honours it without waiting for the new boiler_efficiency_band override row.
overrides = ResolvedPropertyOverrides(
rows=(
ResolvedPropertyOverride(
"main_heating_system",
0,
"Gas boiler, regular",
original_spreadsheet_description="Boiler: G rated Regular Boiler",
),
)
)
overlays = overlays_from(overrides)
assert len(overlays) == 1
assert overlays[0].heating is not None
# G-rated gas regular → Table 4b (66, 56), not the condensing 84% default.
assert overlays[0].heating.seasonal_efficiency_override_pct == (66.0, 56.0)
def test_explicit_band_override_row_wins_over_the_parsed_description() -> None:
# Once the FE classifier writes a structured boiler_efficiency_band row, it is
# the authority — it wins over the on-read parse of the raw description.
overrides = ResolvedPropertyOverrides(
rows=(
ResolvedPropertyOverride(
"main_heating_system",
0,
"Gas boiler, regular",
original_spreadsheet_description="Boiler: G rated Regular Boiler",
),
ResolvedPropertyOverride("boiler_efficiency_band", 0, "D"),
)
)
overlays = overlays_from(overrides)
heating = next(o.heating for o in overlays if o.heating is not None)
# D wins over the description's G → Table 4b (80, 70).
assert heating.seasonal_efficiency_override_pct == (80.0, 70.0)
def test_band_repoints_with_the_resolved_fuel_for_an_oil_boiler() -> None:
# The band is applied AFTER fuel resolution (ADR-0067): a gas archetype on an
# oil main_fuel resolves to the oil boiler, and the band keys off the oil code.
overrides = ResolvedPropertyOverrides(
rows=(
ResolvedPropertyOverride(
"main_heating_system",
0,
"Gas boiler, regular",
original_spreadsheet_description="Boiler: D rated Regular Boiler",
),
ResolvedPropertyOverride("main_fuel", 0, "oil"),
)
)
overlays = overlays_from(overrides)
heating = next(o.heating for o in overlays if o.heating is not None)
assert heating.sap_main_heating_code == 127 # oil regular
assert heating.seasonal_efficiency_override_pct == (80.0, 68.0) # oil D → code 126
def test_no_band_information_leaves_the_efficiency_slot_unset() -> None:
# A plain boiler description carries no band → no slot → condensing default.
overrides = ResolvedPropertyOverrides(
rows=(
ResolvedPropertyOverride(
"main_heating_system",
0,
"Gas boiler, combi",
original_spreadsheet_description="Gas boiler",
),
)
)
overlays = overlays_from(overrides)
heating = next(o.heating for o in overlays if o.heating is not None)
assert heating.seasonal_efficiency_override_pct is None
def test_unresolvable_rows_are_skipped() -> None:
# Arrange — an "Unknown" property type and an unmapped wall material.
overrides = ResolvedPropertyOverrides(