Model/tests/scripts/test_reclassify_main_heating.py
Khalim Conn-Kowlessar de7bfc5dfe Reclassify Gas CPSU dumping-ground overrides via the generalized main-heating reclassify 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:58:29 +00:00

101 lines
4 KiB
Python

"""The main-heating reclassify maps the garbage-drawer archetypes (old storage,
Gas CPSU) onto the faithful ones the guard resolves, leaves correctly-classified
rows alone, and is idempotent (#1376 / ADR-0044, ADR-0045)."""
from __future__ import annotations
from domain.epc.property_overlays.main_heating_system_overlay import (
main_heating_overlay_for,
)
from domain.epc.property_overrides.main_heating_guard import main_heating_guard
from domain.epc.property_overrides.main_heating_system_type import (
MainHeatingSystemType,
)
from scripts.reclassify_main_heating import main_heating_corrections
def test_garbage_drawer_rows_are_remapped_to_faithful_archetypes() -> None:
# Arrange — HHRSH funnelled into old storage, and the Gas CPSU dumping ground
# (solid-fuel room heaters + electric "NA" boiler); plus correctly-classified
# rows (genuine old storage, a gas combi) that must be left untouched.
stored = [
(
"electric storage systems: high heat retention storage heaters",
"Electric storage heaters, old",
),
("solid fuel room heaters: open fire in grate", "Gas CPSU"),
(
"solid fuel room heaters: open fire with back boiler (no radiators)",
"Gas CPSU",
),
(
"solid fuel room heaters: closed room heater with boiler (no radiators)",
"Gas CPSU",
),
("boiler: a rated na", "Gas CPSU"),
# Correctly classified — the guard does not claim these.
(
"electric storage systems: old (large volume) storage heaters",
"Electric storage heaters, old",
),
("boiler: a rated combi", "Gas boiler, combi"),
]
# Act
corrections = main_heating_corrections(stored)
# Assert — every mis-classified row moves to its faithful archetype.
assert corrections == {
"electric storage systems: high heat retention storage heaters": (
"Electric storage heaters, high heat retention"
),
"solid fuel room heaters: open fire in grate": (
"Solid fuel room heater, open fire"
),
"solid fuel room heaters: open fire with back boiler (no radiators)": (
"Solid fuel room heater, open fire with back boiler"
),
"solid fuel room heaters: closed room heater with boiler (no radiators)": (
"Solid fuel room heater, closed with boiler"
),
"boiler: a rated na": "Electric boiler",
}
def test_every_guard_target_round_trips_to_a_resolvable_archetype() -> None:
# Every archetype the guard emits for the rescued descriptions must decode to a
# real SAP heating code — a guard/overlay drift can't silently write an
# unmodellable value (ADR-0044, ADR-0045).
descriptions = [
"Electric Storage Systems: High heat retention storage heaters",
"Solid fuel room heaters: Open fire in grate",
"Solid fuel room heaters: Open fire with back boiler (no radiators)",
"Solid fuel room heaters: Closed room heater with boiler (no radiators)",
"Boiler: A rated NA",
]
for description in descriptions:
member = main_heating_guard(description)
assert member is not None, description
assert member in MainHeatingSystemType, description
simulation = main_heating_overlay_for(member.value, 0)
assert simulation is not None, description
assert simulation.heating is not None, description
assert simulation.heating.sap_main_heating_code is not None, description
def test_already_corrected_rows_need_no_change() -> None:
# Act / Assert — idempotent.
assert (
main_heating_corrections(
[
(
"electric storage systems: high heat retention storage heaters",
"Electric storage heaters, high heat retention",
),
("boiler: a rated na", "Electric boiler"),
]
)
== {}
)