mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-27 23:35:01 +00:00
126 lines
4.6 KiB
Python
126 lines
4.6 KiB
Python
"""EPC store gains a `source` discriminator so a lodged EPC and a predicted EPC
|
|
(EPC Prediction gap-fill) coexist per property — ADR-0031 slice-5c."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from sqlalchemy import Engine
|
|
from sqlmodel import Session
|
|
|
|
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
|
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
|
|
from repositories.epc.epc_postgres_repository import EpcPostgresRepository
|
|
|
|
_JSON_SAMPLES = Path(__file__).resolve().parents[3] / "backend/epc_api/json_samples"
|
|
|
|
|
|
def _epc() -> EpcPropertyData:
|
|
raw: dict[str, Any] = json.loads(
|
|
(_JSON_SAMPLES / "RdSAP-Schema-21.0.0" / "epc.json").read_text()
|
|
)
|
|
return EpcPropertyDataMapper.from_api_response(raw)
|
|
|
|
|
|
def test_lodged_and_predicted_epcs_coexist_per_property(db_engine: Engine) -> None:
|
|
# Arrange — the same property gets a lodged EPC and a predicted EPC.
|
|
epc = _epc()
|
|
with Session(db_engine) as session:
|
|
repo = EpcPostgresRepository(session)
|
|
repo.save(epc, property_id=42)
|
|
repo.save(epc, property_id=42, source="predicted")
|
|
session.commit()
|
|
|
|
# Act
|
|
with Session(db_engine) as session:
|
|
repo = EpcPostgresRepository(session)
|
|
lodged = repo.get_for_property(42)
|
|
predicted = repo.get_predicted_for_property(42)
|
|
|
|
# Assert — both slots are populated independently; neither overwrote the other.
|
|
assert lodged == epc
|
|
assert predicted == epc
|
|
|
|
|
|
def test_re_saving_a_predicted_epc_leaves_the_lodged_one_intact(
|
|
db_engine: Engine,
|
|
) -> None:
|
|
# Arrange — a lodged EPC, then a predicted one saved twice (idempotent re-run).
|
|
epc = _epc()
|
|
with Session(db_engine) as session:
|
|
repo = EpcPostgresRepository(session)
|
|
repo.save(epc, property_id=7)
|
|
repo.save(epc, property_id=7, source="predicted")
|
|
repo.save(epc, property_id=7, source="predicted")
|
|
session.commit()
|
|
|
|
# Act
|
|
with Session(db_engine) as session:
|
|
repo = EpcPostgresRepository(session)
|
|
lodged = repo.get_for_property(7)
|
|
predicted_bulk = repo.get_predicted_for_properties([7])
|
|
|
|
# Assert — the lodged slot survived the predicted re-saves; bulk read finds it.
|
|
assert lodged == epc
|
|
assert predicted_bulk == {7: epc}
|
|
|
|
|
|
def test_a_lodged_only_property_has_no_predicted_epc(db_engine: Engine) -> None:
|
|
# Arrange — only a lodged EPC is saved.
|
|
epc = _epc()
|
|
with Session(db_engine) as session:
|
|
repo = EpcPostgresRepository(session)
|
|
repo.save(epc, property_id=9)
|
|
session.commit()
|
|
|
|
# Act / Assert — the predicted slot is empty; the lodged one is unaffected.
|
|
with Session(db_engine) as session:
|
|
repo = EpcPostgresRepository(session)
|
|
assert repo.get_predicted_for_property(9) is None
|
|
assert repo.get_for_property(9) == epc
|
|
|
|
|
|
def test_an_expired_source_epc_lives_in_the_predicted_slot(db_engine: Engine) -> None:
|
|
# Arrange — an Expired-Enhanced Prediction is persisted with source="expired"
|
|
# (ADR-0054): enhanced by an expired historic observation, not totally
|
|
# predicted — but it occupies the same predicted slot.
|
|
epc = _epc()
|
|
with Session(db_engine) as session:
|
|
repo = EpcPostgresRepository(session)
|
|
repo.save(epc, property_id=11, source="expired")
|
|
session.commit()
|
|
|
|
# Act / Assert — readable through the predicted slot; the lodged slot is empty.
|
|
with Session(db_engine) as session:
|
|
repo = EpcPostgresRepository(session)
|
|
assert repo.get_predicted_for_property(11) == epc
|
|
assert repo.get_predicted_for_properties([11]) == {11: epc}
|
|
assert repo.get_for_property(11) is None
|
|
|
|
|
|
def test_saving_predicted_over_expired_replaces_the_slot_without_stranding(
|
|
db_engine: Engine,
|
|
) -> None:
|
|
# Arrange — a re-ingestion can flip the slot's flavour (expired -> predicted
|
|
# or back); the slot holds ONE row, never a stranded stale sibling.
|
|
from sqlmodel import select
|
|
|
|
from infrastructure.postgres.epc_property_table import EpcPropertyModel
|
|
|
|
epc = _epc()
|
|
with Session(db_engine) as session:
|
|
repo = EpcPostgresRepository(session)
|
|
repo.save(epc, property_id=13, source="expired")
|
|
repo.save(epc, property_id=13, source="predicted")
|
|
session.commit()
|
|
|
|
# Act
|
|
with Session(db_engine) as session:
|
|
rows = session.exec(
|
|
select(EpcPropertyModel).where(EpcPropertyModel.property_id == 13)
|
|
).all()
|
|
|
|
# Assert — exactly one slot row remains, the latest flavour.
|
|
assert [r.source for r in rows] == ["predicted"]
|