diff --git a/datatypes/epc/domain/tests/test_from_site_notes.py b/datatypes/epc/domain/tests/test_from_site_notes.py index 7aad4e162..96e5620e5 100644 --- a/datatypes/epc/domain/tests/test_from_site_notes.py +++ b/datatypes/epc/domain/tests/test_from_site_notes.py @@ -866,3 +866,63 @@ class TestPasHubUnmappedMainFuel: # Assert assert result.sap_heating.main_heating_details[0].main_fuel_type == 30 + + +class TestPasHubMainHeatingControlCoding: + """Each main-heating Controls label the Guinness cohort lodges resolves to + its SAP 10.2 Table 4e Group 1 (PDF p.171) boiler-control code. The mapper + emits the int code (not the raw label) so the calculator's `_control_type` + keys off it instead of strict-raising `UnmappedSapCode` deep in the cascade. + + The resulting control type is the spec's, not a blanket "type 2": a + programmer + a single room thermostat (2104) is type 1; type 2 needs more + (2+ room thermostats, or TRVs). + """ + + # (label, Table 4e code, Table 9 control type) — codes/types straight from + # SAP 10.2 Table 4e Group 1. + _LABEL_CODE_TYPE = [ + ("Programmer and room thermostat", 2104, 1), + ("Programmer, TRVs and bypass", 2107, 2), + ("Room thermostat and TRVs", 2113, 2), + ("TRVs and bypass", 2111, 2), + ] + + @pytest.mark.parametrize( + "label, code", [(lbl, code) for lbl, code, _ in _LABEL_CODE_TYPE] + ) + def test_control_label_maps_to_sap_code(self, label: str, code: int) -> None: + # Arrange — the example fixture with its Controls cell overridden. + raw = load("pashub_rdsap_site_notes_example1.json") + raw["heating_and_hot_water"]["main_heating"]["controls"] = label + survey = from_dict(PasHubRdSapSiteNotes, raw) + + # Act + result = EpcPropertyDataMapper.from_site_notes(survey) + + # Assert + assert ( + result.sap_heating.main_heating_details[0].main_heating_control == code + ) + + @pytest.mark.parametrize( + "label, control_type", [(lbl, ct) for lbl, _, ct in _LABEL_CODE_TYPE] + ) + def test_mapped_control_yields_spec_control_type( + self, label: str, control_type: int + ) -> None: + # Arrange — drive the mapped code through the calculator's control-type + # classifier to pin the end-to-end control type per Table 4e. + from domain.sap10_calculator.rdsap.cert_to_inputs import ( # pyright: ignore[reportPrivateUsage] + _control_type, + ) + + raw = load("pashub_rdsap_site_notes_example1.json") + raw["heating_and_hot_water"]["main_heating"]["controls"] = label + survey = from_dict(PasHubRdSapSiteNotes, raw) + main = EpcPropertyDataMapper.from_site_notes( + survey + ).sap_heating.main_heating_details[0] + + # Act / Assert + assert _control_type(main) == control_type