"""Behaviour of the Secondary Heating Removal Recommendation Generator: offering to strip a dwelling's lodged secondary heating system so the main serves 100% of space heating (ADR-0028). A standalone, co-selectable Recommendation; eligibility is purely physical (offer iff a secondary is lodged) — the Optimiser de-selects the cases where removal cannot move SAP. Detection + pricing only (ADR-0016). """ import copy from datatypes.epc.domain.epc_property_data import EpcPropertyData from domain.modelling.generators.secondary_heating_recommendation import ( recommend_secondary_heating_removal, ) from domain.modelling.product import Product from domain.modelling.recommendation import Recommendation from domain.modelling.simulation import SecondaryHeatingOverlay from repositories.product.product_repository import ProductRepository from tests.domain.sap10_calculator.worksheet._elmhurst_worksheet_000490 import ( build_epc, ) class _StubProducts(ProductRepository): """In-memory ProductRepository returning a fixed flat per-dwelling decommission price (ADR-0028).""" def get(self, measure_type: str) -> Product: return Product( measure_type=measure_type, unit_cost_per_m2=250.0, contingency_rate=0.25, id=11, ) def _without_secondary(epc: EpcPropertyData) -> EpcPropertyData: """Return a copy of `epc` with no secondary heating system lodged.""" clone: EpcPropertyData = copy.deepcopy(epc) clone.sap_heating.secondary_heating_type = None clone.sap_heating.secondary_fuel_type = None return clone def test_dwelling_with_a_lodged_secondary_yields_a_removal_recommendation() -> None: # Arrange — 000490 lodges a secondary system (SAP code 691, electric panel/ # convector/radiant heaters). baseline: EpcPropertyData = build_epc() assert baseline.sap_heating.secondary_heating_type == 691 # Act recommendation: Recommendation | None = recommend_secondary_heating_removal( baseline, _StubProducts() ) # Assert — one Option whose overlay clears the secondary. assert recommendation is not None assert recommendation.surface == "Secondary Heating" assert len(recommendation.options) == 1 option = recommendation.options[0] assert option.measure_type == "secondary_heating_removal" assert option.overlay.secondary_heating == SecondaryHeatingOverlay() def test_dwelling_without_a_secondary_yields_no_recommendation() -> None: # Arrange — nothing lodged to remove (RdSAP only records a secondary when a # fixed emitter is present; a portable would not be lodged at all). baseline: EpcPropertyData = _without_secondary(build_epc()) # Act recommendation: Recommendation | None = recommend_secondary_heating_removal( baseline, _StubProducts() ) # Assert assert recommendation is None def test_recommendation_prices_a_flat_per_dwelling_decommission() -> None: # Arrange — a lodged secondary; the cost is a flat per-dwelling decommission # figure (one electrician visit + localised making-good), not room-scaled. baseline: EpcPropertyData = build_epc() # Act recommendation: Recommendation | None = recommend_secondary_heating_removal( baseline, _StubProducts() ) # Assert assert recommendation is not None cost = recommendation.options[0].cost assert cost is not None assert cost.total == 250.0 assert cost.contingency_rate == 0.25 assert recommendation.options[0].material_id == 11