Model/tests/repositories/test_unit_of_work.py
Khalim Conn-Kowlessar e778d1fb97 feat(modelling): expose scenario/product/plan repos on the UnitOfWork (#1157)
Slice 4a. The Modelling stage reads the Scenario + Product catalogue and
writes the Plan + its Plan Measures on one session, committed once
(ADR-0012/0017). Adds uow.scenario / uow.product / uow.plan to the
UnitOfWork port and constructs them in PostgresUnitOfWork.__enter__.
Additive — existing stages and the bare-stub Modelling wiring are
unaffected. Wiring test asserts the unit exposes the three ports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 11:53:34 +00:00

88 lines
3 KiB
Python

from __future__ import annotations
from collections.abc import Callable
import pytest
from sqlalchemy import Engine
from sqlmodel import Session
from datatypes.epc.domain.epc import Epc
from domain.property_baseline.property_baseline_performance import PropertyBaselinePerformance
from domain.property_baseline.performance import Performance
from repositories.plan.plan_repository import PlanRepository
from repositories.postgres_unit_of_work import PostgresUnitOfWork
from repositories.product.product_repository import ProductRepository
from repositories.scenario.scenario_repository import ScenarioRepository
def _session_factory(db_engine: Engine) -> Callable[[], Session]:
return lambda: Session(db_engine)
def _baseline() -> PropertyBaselinePerformance:
perf = Performance(
sap_score=72, epc_band=Epc.C, co2_emissions=1.8, primary_energy_intensity=180
)
return PropertyBaselinePerformance(
lodged=perf,
effective=perf,
rebaseline_reason="none",
space_heating_kwh=5000.0,
water_heating_kwh=2000.0,
)
def test_committed_work_is_visible_to_a_later_unit(db_engine: Engine) -> None:
# Arrange
new_unit = lambda: PostgresUnitOfWork(_session_factory(db_engine))
baseline = _baseline()
# Act
with new_unit() as uow:
uow.property_baseline.save(baseline, property_id=10)
uow.commit()
# Assert — a fresh unit reads back what the first one committed.
with new_unit() as uow:
loaded = uow.property_baseline.get_for_property(10)
assert loaded == baseline
def test_an_exception_in_the_block_rolls_the_batch_back(db_engine: Engine) -> None:
# Arrange
new_unit = lambda: PostgresUnitOfWork(_session_factory(db_engine))
# Act — a property mid-batch raises after a write but before commit.
with pytest.raises(RuntimeError, match="boom"):
with new_unit() as uow:
uow.property_baseline.save(_baseline(), property_id=10)
raise RuntimeError("boom")
# Assert — nothing from the aborted batch is persisted.
with new_unit() as uow:
assert uow.property_baseline.get_for_property(10) is None
def test_unit_exposes_the_modelling_repos_bound_to_its_session(
db_engine: Engine,
) -> None:
# Arrange / Act
with PostgresUnitOfWork(_session_factory(db_engine)) as uow:
# Assert — the Modelling stage reads Scenario + Product and writes Plan
# through the same unit (ADR-0017).
assert isinstance(uow.scenario, ScenarioRepository)
assert isinstance(uow.product, ProductRepository)
assert isinstance(uow.plan, PlanRepository)
def test_leaving_the_block_without_commit_persists_nothing(db_engine: Engine) -> None:
# Arrange
new_unit = lambda: PostgresUnitOfWork(_session_factory(db_engine))
# Act — write but never commit.
with new_unit() as uow:
uow.property_baseline.save(_baseline(), property_id=10)
# Assert
with new_unit() as uow:
assert uow.property_baseline.get_for_property(10) is None