diff --git a/tests/domain/modelling/fixtures/ashp_from_gas_boiler_instant_hw_001431_after.pdf b/tests/domain/modelling/fixtures/ashp_from_gas_boiler_instant_hw_001431_after.pdf new file mode 100644 index 00000000..a1c98819 Binary files /dev/null and b/tests/domain/modelling/fixtures/ashp_from_gas_boiler_instant_hw_001431_after.pdf differ diff --git a/tests/domain/modelling/fixtures/ashp_from_gas_boiler_instant_hw_001431_before.pdf b/tests/domain/modelling/fixtures/ashp_from_gas_boiler_instant_hw_001431_before.pdf new file mode 100644 index 00000000..0208fb61 Binary files /dev/null and b/tests/domain/modelling/fixtures/ashp_from_gas_boiler_instant_hw_001431_before.pdf differ diff --git a/tests/domain/modelling/test_elmhurst_cascade_pins.py b/tests/domain/modelling/test_elmhurst_cascade_pins.py index 50c7f7cd..e5841e37 100644 --- a/tests/domain/modelling/test_elmhurst_cascade_pins.py +++ b/tests/domain/modelling/test_elmhurst_cascade_pins.py @@ -641,3 +641,53 @@ def test_ashp_overlay_reproduces_the_relodged_after_from_a_gas_boiler() -> None: # Act / Assert _assert_overlay_reproduces_after(before, after, option.overlay) + + +def test_ashp_overlay_reproduces_the_relodged_after_from_a_gas_boiler_instant_hw() -> None: + # Arrange — a gas boiler whose hot water is electric/instantaneous (water- + # heating SAP code 909, no cylinder) re-lodged as an ASHP. This exercises the + # overlay's water_heating_code reset (909 -> 901, "from the heat pump") that + # boiler-1 didn't (its HW was already 901). The relodged after lodges control + # 2209 vs the overlay's 2210 — SAP-equivalent zone controls, so the cascade + # still closes at 1e-4. + before: EpcPropertyData = parse_recommendation_summary( + "ashp_from_gas_boiler_instant_hw_001431_before.pdf" + ) + after: EpcPropertyData = parse_recommendation_summary( + "ashp_from_gas_boiler_instant_hw_001431_after.pdf" + ) + recommendation: Recommendation | None = recommend_heating(before, _AnyProduct()) + assert recommendation is not None + option = next( + o for o in recommendation.options if o.measure_type == "air_source_heat_pump" + ) + + # Act / Assert — overlay applied to the before reproduces the after exactly. + _assert_overlay_reproduces_after(before, after, option.overlay) + + +_BOILER_INSTANT_HW_FUEL_REASON: Final[str] = ( + "Blocked on the Elmhurst mapper deriving main_fuel_type for a gas boiler " + "lodged with EES code 'BGB' / Main Heating SAP code 102: it currently maps " + "to '' (empty), so Sap10Calculator raises MissingMainFuelType when " + "baselining the raw before. (boiler-1's 'RGE' resolves to mains gas 26; " + "'BGB' has no mapping.) The ASHP overlay still reproduces the after exactly " + "because it overwrites main_fuel_type -> 30, so the cascade pin above passes " + "— only baselining the unmodified before is blocked. Flips green once the " + "mapper derives mains gas (26) from the gas-boiler SAP code. Owner: " + "mapper/extractor front." +) + + +@pytest.mark.xfail(strict=True, reason=_BOILER_INSTANT_HW_FUEL_REASON) +def test_gas_boiler_instant_hw_before_baselines() -> None: + # The Modelling pipeline baselines the dwelling before modelling it, so the + # before must be scorable on its own. This one is not yet: its main fuel is + # unresolved (see reason). A failing tripwire for the separate mapper fix. + # Arrange + before: EpcPropertyData = parse_recommendation_summary( + "ashp_from_gas_boiler_instant_hw_001431_before.pdf" + ) + + # Act / Assert — currently raises MissingMainFuelType. + Sap10Calculator().calculate(before)