mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-08-02 21:08:24 +00:00
fix: wire boiler_efficiency_band into the reader registry (consistency guard)
test_override_component_consistency caught two real gaps: the band was in the override_component pgEnum but (a) missing from _ROW_TYPES — so the finaliser couldn't load its classified vocab (a latent bug the injected-vocab finaliser tests missed), and (b) not in _COMPONENT_OVERLAYS (correct — it's an attribute consumed by the main_heating_system overlay, not a standalone one). Add the band to _ROW_TYPES (reader), and introduce _ATTRIBUTE_COMPONENTS to name the classified+stored-but-no-standalone-overlay case. The guard now asserts: pgEnum == _ROW_TYPES; and _ROW_TYPES minus attribute components == _COMPONENT_OVERLAYS. Full repositories suite (247) + finaliser green; pyright clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
50554ff57e
commit
3e4b8bd781
3 changed files with 45 additions and 12 deletions
|
|
@ -37,6 +37,9 @@ from infrastructure.postgres.landlord_main_fuel_override_table import (
|
|||
from infrastructure.postgres.landlord_main_heating_system_override_table import (
|
||||
LandlordMainHeatingSystemOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_boiler_efficiency_band_override_table import (
|
||||
LandlordBoilerEfficiencyBandOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_wall_type_override_table import (
|
||||
LandlordWallTypeOverrideRow,
|
||||
)
|
||||
|
|
@ -58,6 +61,10 @@ _ROW_TYPES: dict[str, type] = {
|
|||
"construction_age_band": LandlordConstructionAgeBandOverrideRow,
|
||||
"water_heating": LandlordWaterHeatingOverrideRow,
|
||||
"main_heating_system": LandlordMainHeatingSystemOverrideRow,
|
||||
# An attribute of the main_heating_system override (consumed by its overlay,
|
||||
# not a standalone one) — but still classified + cached + stored, so it needs
|
||||
# a reader entry for the finaliser to load its vocab (ADR-0068).
|
||||
"boiler_efficiency_band": LandlordBoilerEfficiencyBandOverrideRow,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,15 @@ _COMPONENT_OVERLAYS: dict[str, Callable[[str, int], Optional[EpcSimulation]]] =
|
|||
}
|
||||
|
||||
|
||||
# Components that are classified + stored but have **no standalone overlay** — they
|
||||
# are *attributes* consumed by another component's overlay, so they are absent from
|
||||
# `_COMPONENT_OVERLAYS` on purpose. The Boiler Efficiency Band rides the
|
||||
# main_heating_system overlay (ADR-0068), read at its call site (see
|
||||
# `_boiler_efficiency_band_for`). The override-component consistency guard treats
|
||||
# these as the reader/pgEnum-minus-overlays difference.
|
||||
_ATTRIBUTE_COMPONENTS: frozenset[str] = frozenset({"boiler_efficiency_band"})
|
||||
|
||||
|
||||
# Components whose overlay must be applied LAST so an explicit value wins a
|
||||
# default another overlay dragged. `apply_simulations` is last-wins and override
|
||||
# rows arrive in arbitrary order, so a `main_fuel` override must be applied after
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
"""Every override component must be wired through the WHOLE chain.
|
||||
|
||||
The finaliser reader (`_ROW_TYPES`, component -> landlord table) and the overlay
|
||||
registry (`_COMPONENT_OVERLAYS`, component -> overlay mapper) must cover exactly
|
||||
the same set of components. If a component is classified + stored but has no
|
||||
reader entry, the finaliser silently never writes its `property_overrides` rows;
|
||||
if it has no overlay entry, the row never reaches the calculator. This guard
|
||||
keeps the two registries in lock-step (it would have caught the missing
|
||||
main_fuel / glazing / construction_age_band reader entries).
|
||||
The finaliser reader (`_ROW_TYPES`, component -> landlord table), the overlay
|
||||
registry (`_COMPONENT_OVERLAYS`, component -> overlay mapper), and the
|
||||
`override_component` pgEnum mirror must stay in lock-step. If a component is
|
||||
classified + stored but has no reader entry, the finaliser silently never writes
|
||||
its `property_overrides` rows; if it has no overlay entry, the row never reaches
|
||||
the calculator; if it is missing from the pgEnum, writing/reading it throws a
|
||||
LookupError against Postgres (caught live on the Hyde portfolio-796 run). This
|
||||
guard would have caught the missing main_fuel / glazing / construction_age_band
|
||||
reader entries.
|
||||
|
||||
The one exception is an **attribute component** (`_ATTRIBUTE_COMPONENTS`): it is
|
||||
classified + stored + read (so it *is* in `_ROW_TYPES` and the pgEnum) but is
|
||||
consumed by another component's overlay rather than having its own, so it is
|
||||
deliberately absent from `_COMPONENT_OVERLAYS` — the Boiler Efficiency Band rides
|
||||
the main_heating_system overlay (ADR-0068).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -18,18 +26,27 @@ from infrastructure.landlord_overrides.landlord_override_reader_postgres_reposit
|
|||
)
|
||||
from infrastructure.postgres.property_override_table import override_component_sa_enum
|
||||
from repositories.property.landlord_override_overlays import (
|
||||
_ATTRIBUTE_COMPONENTS, # pyright: ignore[reportPrivateUsage]
|
||||
_COMPONENT_OVERLAYS, # pyright: ignore[reportPrivateUsage]
|
||||
)
|
||||
|
||||
|
||||
def test_reader_and_overlay_registries_cover_the_same_components() -> None:
|
||||
# Assert
|
||||
assert set(_ROW_TYPES) == set(_COMPONENT_OVERLAYS)
|
||||
# Every classified/stored component (`_ROW_TYPES`) has a standalone overlay,
|
||||
# except the attribute components consumed by another overlay.
|
||||
assert set(_ROW_TYPES) - _ATTRIBUTE_COMPONENTS == set(_COMPONENT_OVERLAYS)
|
||||
|
||||
|
||||
def test_attribute_components_are_read_and_stored_but_have_no_overlay() -> None:
|
||||
# An attribute component must still be classified/read (in `_ROW_TYPES`) and
|
||||
# stored (in the pgEnum), but must NOT appear in `_COMPONENT_OVERLAYS`.
|
||||
assert _ATTRIBUTE_COMPONENTS <= set(_ROW_TYPES)
|
||||
assert _ATTRIBUTE_COMPONENTS.isdisjoint(_COMPONENT_OVERLAYS)
|
||||
|
||||
|
||||
def test_override_component_pgenum_covers_every_component() -> None:
|
||||
# The property_overrides.override_component pgEnum mirror must list every
|
||||
# component, or writing/reading a new-component row through it throws a
|
||||
# LookupError against Postgres (caught live on the Hyde portfolio-796 run).
|
||||
# classified/stored component (overlay-backed + attribute), or writing/reading
|
||||
# a new-component row through it throws a LookupError against Postgres.
|
||||
pgenum_values = cast(list[str], getattr(override_component_sa_enum, "enums"))
|
||||
assert set(pgenum_values) == set(_COMPONENT_OVERLAYS)
|
||||
assert set(pgenum_values) == set(_ROW_TYPES)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue