"""HistoricConditioning resolves an expired Historic EPC's stable attributes into the cohort's code spaces (ADR-0054). Only stable attributes are resolved — volatile ones (heating system, glazing, PV, insulation states) never condition prediction. Every resolver degrades to None on an unresolvable value, which leaves its cohort filter inactive. """ from __future__ import annotations import dataclasses from datatypes.epc.domain.historic_epc import HistoricEpc from domain.epc_prediction.historic_conditioning import ( HistoricConditioning, conditioning_from_historic, ) def _hist(**overrides: str) -> HistoricEpc: fields = {f.name: "" for f in dataclasses.fields(HistoricEpc)} fields.update(overrides) return HistoricEpc(**fields) def test_resolves_stable_attributes_into_cohort_code_spaces(): # Arrange — display-text values exactly as the old register lodged them. record = _hist( property_type="House", built_form="Semi-Detached", walls_description="Cavity wall, as built, no insulation (assumed)", roof_description="Pitched, 100 mm loft insulation", construction_age_band="England and Wales: 1930-1949", main_fuel="mains gas (not community)", total_floor_area="84", ) # Act conditioning = conditioning_from_historic(record) # Assert — each attribute lands in the code space its cohort filter compares. assert isinstance(conditioning, HistoricConditioning) assert conditioning.property_type == "0" assert conditioning.built_form == "2" assert conditioning.wall_construction == 4 assert conditioning.roof_form == "pitched" assert conditioning.construction_age_band == "C" assert conditioning.main_fuel == 26 assert conditioning.total_floor_area_m2 == 84.0 def test_roof_descriptions_resolve_to_form_families(): # Arrange / Act / Assert — the FORM half of the description (before the # comma) maps to a family, because the API's roof_construction codes group # that way (7,974-cert co-occurrence sweep: 1=Flat 98%, 4/5/8=Pitched # 88-99%, 3=another dwelling above 100%; 7/9=another premises above per # the #1452 suppression fix). Insulation state is volatile and ignored. cases = { "Pitched, 250 mm loft insulation": "pitched", "Pitched, no insulation (assumed)": "pitched", "Flat, insulated (assumed)": "flat", "(another dwelling above)": "dwelling_above", "(another premises above)": "premises_above", } for text, family in cases.items(): record = _hist(roof_description=text) assert conditioning_from_historic(record).roof_form == family, text # Unpinned forms (roof rooms, thatched) must not guess. assert conditioning_from_historic( _hist(roof_description="Roof room(s), insulated") ).roof_form is None assert conditioning_from_historic( _hist(roof_description="Thatched, with additional insulation") ).roof_form is None def test_legacy_register_fuel_descriptions_resolve(): # Arrange / Act / Assert — the old register lodged pre-RdSAP-17 fuels with # a "backwards compatibility" rider or a SAP-style prefix; they name the # same physical fuels (dominant in the pre-2012 dump: a 65-shard scan found # 636/663 unresolved values were these variants). cases = { "mains gas - this is for backwards compatibility only and should not be used": 26, "electricity - this is for backwards compatibility only and should not be used": 29, "LPG - this is for backwards compatibility only and should not be used": 27, "oil - this is for backwards compatibility only and should not be used": 28, "Gas: mains gas": 26, "Electricity: electricity, unspecified tariff": 29, "dual fuel - mineral + wood": 10, } for text, code in cases.items(): record = _hist(main_fuel=text) assert conditioning_from_historic(record).main_fuel == code, text def test_unresolvable_values_degrade_to_none(): # Arrange — junk and blanks must never guess a code. record = _hist( property_type="Castle", built_form="Not Recorded", walls_description="Average thermal transmittance 0.3 W/m²K", construction_age_band="INVALID!", main_fuel="To be used only when there is no heating system", total_floor_area="", ) # Act conditioning = conditioning_from_historic(record) # Assert assert conditioning.property_type is None assert conditioning.built_form is None assert conditioning.wall_construction is None assert conditioning.roof_form is None assert conditioning.construction_age_band is None assert conditioning.main_fuel is None assert conditioning.total_floor_area_m2 is None