diff --git a/tests/orchestration/test_modelling_fabric_first.py b/tests/orchestration/test_modelling_fabric_first.py new file mode 100644 index 000000000..86d5e7746 --- /dev/null +++ b/tests/orchestration/test_modelling_fabric_first.py @@ -0,0 +1,60 @@ +"""The ModellingOrchestrator honours a Fabric First Scenario: when +``scenario.fabric_first`` is set, the Optimiser treats the building envelope +with the full budget before heating / renewables are considered, so a budget +the plain optimiser would spend on a heating system is spent on fabric +instead. End-to-end through ``run_modelling`` (no database) with the real +calculator, against the uninsulated solid-brick 001431 dwelling whose plain +plan is heating-led. +""" + +from __future__ import annotations + +from datatypes.epc.domain.epc_property_data import EpcPropertyData +from domain.modelling.measure_type import FABRIC_MEASURE_TYPES, MeasureType +from domain.modelling.plan import Plan +from domain.modelling.scenario import Scenario +from harness.console import run_modelling +from tests.domain.modelling._elmhurst_recommendation import ( + parse_recommendation_summary, +) + +_HEATING_MEASURES: frozenset[MeasureType] = frozenset( + { + MeasureType.GAS_BOILER_UPGRADE, + MeasureType.AIR_SOURCE_HEAT_PUMP, + MeasureType.HIGH_HEAT_RETENTION_STORAGE_HEATERS, + MeasureType.SOLAR_PV, + } +) + + +def _fabric_first_scenario(*, budget: float) -> Scenario: + return Scenario( + id=999, + goal="Increasing EPC", + goal_value="C", + budget=budget, + is_default=True, + fabric_first=True, + ) + + +def test_fabric_first_scenario_spends_the_budget_on_fabric_before_heating() -> None: + # Arrange — uninsulated solid brick: at a £4000 budget the plain optimiser + # buys the £3200 gas boiler (the cheapest route toward band C). Fabric + # first must commit the envelope work first, after which the boiler no + # longer fits the leftover budget. + epc: EpcPropertyData = parse_recommendation_summary( + "solid_brick_ewi_001431_before.pdf" + ) + + # Act + plan: Plan = run_modelling( + epc, scenario=_fabric_first_scenario(budget=4000.0), print_table=False + ) + + # Assert — the plan is fabric-led: at least one envelope measure, and none + # of the budget leaked to a heating system or renewables. + selected = {measure.measure_type for measure in plan.measures} + assert selected & FABRIC_MEASURE_TYPES + assert not selected & _HEATING_MEASURES