diff --git a/tests/domain/epc_prediction/test_epc_prediction.py b/tests/domain/epc_prediction/test_epc_prediction.py index 00a6ce57..17ea7400 100644 --- a/tests/domain/epc_prediction/test_epc_prediction.py +++ b/tests/domain/epc_prediction/test_epc_prediction.py @@ -9,6 +9,7 @@ from datetime import date from typing import Optional, Union from datatypes.epc.domain.epc_property_data import ( + BuildingPartIdentifier, EpcPropertyData, MainHeatingDetail, SapBuildingPart, @@ -32,6 +33,7 @@ from domain.epc_prediction.prediction_target import PredictionTarget def _epc( *, building_parts: int = 1, + identifier: BuildingPartIdentifier = BuildingPartIdentifier.MAIN, floor_area: float = 80.0, wall_construction: Union[int, str] = 1, wall_insulation_type: Union[int, str] = 1, @@ -57,6 +59,7 @@ def _epc( parts: list[SapBuildingPart] = [] for _ in range(building_parts): part: SapBuildingPart = object.__new__(SapBuildingPart) + part.identifier = identifier part.wall_construction = wall_construction part.wall_insulation_type = wall_insulation_type part.construction_age_band = construction_age_band @@ -148,6 +151,31 @@ def test_template_is_the_member_closest_to_the_cohort_median_size() -> None: assert predicted.total_floor_area_m2 == 80.0 +def test_template_skips_a_main_less_member_so_the_prediction_has_a_main_part() -> None: + # Arrange — the size-median member is OTHER-only (the gov API lodged its part + # with a null identifier), but the cohort holds MAIN-bearing neighbours at + # other sizes. The structural template must be a MAIN-bearing member so the + # predicted dwelling presents a main dwelling — else the modelling handler's + # MAIN-part guard rejects an otherwise-rich cohort as "not predictable". + cohort = _cohort( + _epc(floor_area=80.0, identifier=BuildingPartIdentifier.OTHER), + _epc(floor_area=30.0, identifier=BuildingPartIdentifier.MAIN), + _epc(floor_area=200.0, identifier=BuildingPartIdentifier.MAIN), + ) + + # Act + predicted: EpcPropertyData = EpcPrediction().predict( + PredictionTarget(postcode="LS6 1AA", property_type="2"), cohort + ) + + # Assert — the predicted dwelling has a MAIN part (seeded from a MAIN-bearing + # neighbour), not the OTHER-only median member. + assert any( + part.identifier is BuildingPartIdentifier.MAIN + for part in predicted.sap_building_parts + ) + + def test_sets_main_wall_construction_to_the_cohort_mode() -> None: # Arrange — the template (members[0]) is solid brick (2), but the cohort # majority is cavity (1). The homogeneous categorical should follow the mode,