diff --git a/tests/repositories/property/test_landlord_override_overlays.py b/tests/repositories/property/test_landlord_override_overlays.py index e6e60b4e3..37bf4bc19 100644 --- a/tests/repositories/property/test_landlord_override_overlays.py +++ b/tests/repositories/property/test_landlord_override_overlays.py @@ -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(