Model/tests/scripts/test_build_property_overrides_smoke.py
Jun-te Kim e03fd27357 Type-clean the override_component consistency guard 🟪
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 18:19:07 +00:00

88 lines
4.2 KiB
Python

"""End-to-end smoke of the Hyde override script for ONE property, against a real
(ephemeral) Postgres. Seeds the landlord vocab (simulating post-classify, so no
ChatGPT) + a minimal ``property`` row, then runs the script's real
``write`` + ``verify`` paths and asserts property_overrides + overlays land.
"""
from __future__ import annotations
import argparse
from typing import Any
from sqlalchemy import Engine, text
from sqlmodel import Session
import scripts.hyde.build_property_overrides as b
from domain.epc.property_overrides.built_form_type import BuiltFormType
from domain.epc.property_overrides.construction_age_band import ConstructionAgeBand
from domain.epc.property_overrides.glazing_type import GlazingType
from domain.epc.property_overrides.main_fuel_type import MainFuelType
from domain.epc.property_overrides.main_heating_system_type import MainHeatingSystemType
from domain.epc.property_overrides.property_type import PropertyType
from domain.epc.property_overrides.roof_type import RoofType
from domain.epc.property_overrides.wall_type import WallType
from domain.epc.property_overrides.water_heating_type import WaterHeatingType
from infrastructure.landlord_overrides.landlord_overrides_postgres_repository import (
LandlordOverridesRepository,
)
PORTFOLIO = 795
ORG_REF = "55180004001"
EXCEL = "scripts/hyde/hyde_property_overrides.xlsx"
# What ChatGPT WOULD resolve this property's 9 descriptions to (component ->
# (raw Excel entry, enum member)). Seeded into the landlord ledger.
SEED = {
"property_type": ("House: MidTerrace", PropertyType.HOUSE),
"built_form_type": ("House: MidTerrace", BuiltFormType.MID_TERRACE),
"wall_type": ("TimberFrame: AsBuilt", WallType.TIMBER_FRAME_AS_BUILT_NO_INSULATION_ASSUMED),
"roof_type": ("PitchedNormalLoftAccess: 300mm", RoofType.PITCHED_300_MM),
"construction_age_band": ("L: 2012-2022", ConstructionAgeBand.L_2012_2022),
"main_fuel": ("Gas: Mains Gas", MainFuelType.MAINS_GAS),
"glazing": ("100% Double glazing 2002 or later", GlazingType.DOUBLE_POST_2002),
"water_heating": ("From main heating system: Mains Gas", WaterHeatingType.FROM_MAIN_MAINS_GAS),
"main_heating_system": ("Boiler: C rated Combi", MainHeatingSystemType.GAS_COMBI),
}
def test_one_property_end_to_end(db_engine: Engine, monkeypatch: Any, capsys: Any) -> None:
specs = b._specs_by_component()
# minimal FE-owned `property` table + the one row we'll match by org_ref
with Session(db_engine) as s:
s.execute(text(
"CREATE TABLE property (id bigint PRIMARY KEY, portfolio_id bigint, "
"landlord_property_id text)"))
s.execute(text("INSERT INTO property VALUES (1, :p, :ref)"),
{"p": PORTFOLIO, "ref": ORG_REF})
# seed the classifier ledger (keyed on normalised description)
for comp, (raw, member) in SEED.items():
LandlordOverridesRepository(s, specs[comp].row_type).upsert_all(
PORTFOLIO, {b._norm(raw): member})
s.commit()
# point the script at the ephemeral engine
monkeypatch.setattr(b, "_db_session", lambda: Session(db_engine))
# --- run the real write() for this one property ---
b.write(argparse.Namespace(excel=EXCEL, sheet="AddressProfilingResults",
portfolio_id=PORTFOLIO, org_ref=ORG_REF, limit=None, apply=True))
with Session(db_engine) as s:
rows = list(s.execute(text(
"SELECT override_component, building_part, override_value "
"FROM property_overrides WHERE property_id = 1 ORDER BY override_component")))
got = {c: v for c, _, v in rows}
# every seeded component produced a property_overrides row with the resolved value
assert got["main_fuel"] == "mains gas"
assert got["glazing"] == "Double glazing, 2002 or later"
assert got["construction_age_band"] == "L"
assert got["main_heating_system"] == "Gas boiler, combi"
assert got["water_heating"] == "From main system, mains gas"
assert len(rows) == 9 # all 9 components
# --- run the real verify() and confirm overlays are produced ---
b.verify(argparse.Namespace(portfolio_id=PORTFOLIO, org_ref=ORG_REF))
out = capsys.readouterr().out
print(out) # surfaced with -s so we can eyeball the one-property result
assert "EpcSimulation overlay" in out