"""Bulk EPC read: get_for_properties hydrates a batch in a handful of per-table queries, not N x per-property (ADR-0012, #1138).""" from __future__ import annotations import json from collections.abc import Callable from pathlib import Path from typing import Any from sqlalchemy import Engine, event 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 _load_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 _count_queries(engine: Engine, work: Callable[[], None]) -> int: count = 0 def _before(*_args: Any, **_kwargs: Any) -> None: nonlocal count count += 1 event.listen(engine, "before_cursor_execute", _before) try: work() finally: event.remove(engine, "before_cursor_execute", _before) return count def test_get_for_properties_hydrates_the_whole_batch(db_engine: Engine) -> None: # Arrange — the same sample EPC persisted for two properties. epc = _load_epc() with Session(db_engine) as session: repo = EpcPostgresRepository(session) repo.save(epc, property_id=10) repo.save(epc, property_id=11) session.commit() # Act with Session(db_engine) as session: result = EpcPostgresRepository(session).get_for_properties([10, 11]) # Assert — both fully hydrated (load-whole, ADR-0002). assert result == {10: epc, 11: epc} def test_get_for_properties_round_trips_do_not_scale_with_batch_size( db_engine: Engine, ) -> None: # Arrange epc = _load_epc() with Session(db_engine) as session: repo = EpcPostgresRepository(session) repo.save(epc, property_id=10) repo.save(epc, property_id=11) session.commit() def _read(property_ids: list[int]) -> None: with Session(db_engine) as session: EpcPostgresRepository(session).get_for_properties(property_ids) # Act — count queries for a 1-property batch vs a 2-property batch. one = _count_queries(db_engine, lambda: _read([10])) two = _count_queries(db_engine, lambda: _read([10, 11])) # Assert — same number of round-trips regardless of batch size (one query # per table, not per property). assert one == two