"""The water-heating reclassify maps the house-coal dumping ground onto faithful biomass / wood / dual-fuel / biodiesel / immersion archetypes, leaves genuine coal and already-correct rows alone, and is idempotent (#1376 / ADR-0043).""" from __future__ import annotations from domain.epc.property_overlays.water_heating_overlay import ( water_heating_overlay_for, ) from domain.epc.property_overrides.water_heating_guard import water_heating_guard from domain.epc.property_overrides.water_heating_type import WaterHeatingType from scripts.reclassify_water_heating import water_heating_corrections def test_house_coal_bucket_rows_are_remapped_to_faithful_fuels() -> None: # Arrange — the biomass/wood family + the "no system / immersion assumed" case # all funnelled into house coal, plus a genuinely-coal row and a correctly- # classified mains-gas row (descriptions arrive lower-cased from the query). stored = [ ("from main heating system: wood logs", "From main system, house coal"), ( "from main heating system: dual fuel: mineral and wood", "From main system, house coal", ), ( "hot-water only community scheme - boilers: biomass (community)", "From main system, house coal", ), ( "hot-water only community scheme - boilers: heat from boilers using " "biodiesel from any source (community)", "From main system, house coal", ), ( "no hot water system present - electric immersion assumed: " "no heating or hot water", "From main system, house coal", ), # Genuinely coal — stays (guard resolves house coal, value unchanged). ("from main heating system: house coal", "From main system, house coal"), # Already correct — the guard does not claim it. ("from main heating system: mains gas", "From main system, mains gas"), ] # Act corrections = water_heating_corrections(stored) # Assert — only the mis-scored rows move, each to its faithful archetype. assert corrections == { "from main heating system: wood logs": "From main system, wood logs", "from main heating system: dual fuel: mineral and wood": ( "From main system, dual fuel (mineral and wood)" ), "hot-water only community scheme - boilers: biomass (community)": ( "From main system, biomass (community)" ), "hot-water only community scheme - boilers: heat from boilers using " "biodiesel from any source (community)": ( "From main system, biodiesel (community)" ), "no hot water system present - electric immersion assumed: " "no heating or hot water": "Electric immersion, electricity", } def test_every_guard_target_round_trips_to_a_resolvable_archetype() -> None: # The reclassify decides via the guard; every archetype the guard can emit for # the house-coal bucket must be a real member that decodes to SAP water-heating # codes — a guard/overlay drift can't silently write an unmodellable value to # the DB (ADR-0043). house_coal_bucket = [ "from main heating system: wood logs", "from main heating system: dual fuel: mineral and wood", "hot-water only community scheme - boilers: biomass (community)", "hot-water only community scheme - boilers: heat from boilers using " "biodiesel from any source (community)", "no hot water system present - electric immersion assumed: " "no heating or hot water", "from main heating system: house coal", ] for description in house_coal_bucket: member = water_heating_guard(description) assert member is not None, description assert member in WaterHeatingType, description simulation = water_heating_overlay_for(member.value, 0) assert simulation is not None, description assert simulation.heating is not None, description assert simulation.heating.water_heating_code is not None, description assert simulation.heating.water_heating_fuel is not None, description def test_already_corrected_rows_need_no_change() -> None: # Act / Assert — idempotent: a re-run against corrected data is a no-op. assert ( water_heating_corrections( [("from main heating system: wood logs", "From main system, wood logs")] ) == {} )