diff --git a/tests/orchestration/test_property_baseline_orchestrator.py b/tests/orchestration/test_property_baseline_orchestrator.py index 76ee42ce1..a848ddd22 100644 --- a/tests/orchestration/test_property_baseline_orchestrator.py +++ b/tests/orchestration/test_property_baseline_orchestrator.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import Optional, TYPE_CHECKING import pytest @@ -244,6 +244,80 @@ def test_run_raises_when_no_rhi_and_no_scored_result() -> None: assert uow.commits == 0 +class _PredictedRebaseliner(Rebaseliner): + """Scores a predicted picture into a fixed Effective Performance, ignoring the + (absent) lodged half — a predicted Property always has ``physical_state_changed``, + so the calculator output IS the Effective Performance (ADR-0039).""" + + def __init__(self, effective: Performance, result: SapResult) -> None: + self._effective = effective + self._result = result + + def rebaseline( + self, + property_id: int, + effective_epc: EpcPropertyData, + lodged: Optional[Performance], + *, + physical_state_changed: bool = False, + ) -> RebaselineResult: + return RebaselineResult( + effective=self._effective, + reason="physical_state_changed", + sap_result=self._result, + ) + + +def _predicted_property() -> Property: + """A predicted Property (ADR-0031): no lodged EPC, only a neighbour-synthesised + predicted EPC whose recorded performance fields are a *different dwelling's* — + the phantom Lodged Performance this fix must not propagate. Mirrors the #1361 + exemplar (uprn 34003067 / property 742072), predicted-only.""" + predicted = object.__new__(EpcPropertyData) + predicted.energy_rating_current = 14 # phantom: copied from a neighbour template + predicted.current_energy_efficiency_band = Epc.G + predicted.co2_emissions_current = 5.0 + predicted.energy_consumption_current = 400 + predicted.sap_version = 10.2 + predicted.renewable_heat_incentive = None + return Property( + identity=PropertyIdentity( + portfolio_id=1, postcode="A0 0AA", address="1 Some Street", uprn=34003067 + ), + epc=None, + predicted_epc=predicted, + ) + + +def test_run_persists_no_lodged_performance_for_a_predicted_property() -> None: + # Arrange — a predicted Property (no lodged cert); the rebaseliner scores its + # predicted picture into a known Effective Performance. + property_baseline_repo = FakePropertyBaselineRepo() + uow = FakeUnitOfWork( + property=FakePropertyRepo({742072: _predicted_property()}), + property_baseline=property_baseline_repo, + ) + effective = Performance( + sap_score=57, epc_band=Epc.D, co2_emissions=1.5, primary_energy_intensity=160 + ) + orchestrator = PropertyBaselineOrchestrator( + unit_of_work=lambda: uow, + rebaseliner=_PredictedRebaseliner( + effective, _sap_result_with_heating(space_kwh=4200.0, water_kwh=1600.0) + ), + fuel_rates=FuelRatesStaticFileRepository(), + ) + + # Act + orchestrator.run([742072]) + + # Assert — no lodged comparator was manufactured from the neighbour-synthesised + # EPC's recorded fields; the Effective half is still persisted. + (baseline, _) = property_baseline_repo.saved[0] + assert baseline.lodged is None + assert baseline.effective == effective + + def test_run_derives_and_persists_a_bill_when_the_rebaseliner_scores() -> None: # Arrange — a rebaseliner that hands back a SapResult with lighting energy, # so the orchestrator prices it into a Bill at the committed snapshot.