Model/tests/domain/property_baseline/test_rebaseliner.py
Khalim Conn-Kowlessar 15da2d3970 feat(baseline): CalculatorRebaseliner — calculator goes load-bearing (ADR-0013 amend)
Slice 5a: the promotion. Replaces StubRebaseliner in production and collapses the
shadow runner into the rebaseliner (ADR-0013 amendment).

- CalculatorRebaseliner runs Sap10Calculator on every Property:
  * sap_version < 10.2 -> Effective Performance IS the calculator output
    (band via Epc.from_sap_score, CO2 kg->t, PEUI rounded), reason "pre_sap10".
  * sap_version >= 10.2 -> Effective = lodged (API figures on-target), and the
    calculator only logs divergence (SAP>0.5, PEUI/CO2 1%) as a validation signal.
  * a calculator raise propagates -> batch aborts (ADR-0012); fix the cert at once.
- Rebaseliner.rebaseline gains property_id (for the divergence log).
- LoggingCalculatorShadow / the calculator_shadow seam removed from the
  orchestrator; its divergence-comparison logic now lives in the rebaseliner.
- StubRebaseliner kept (signature updated) for orchestrator/repo unit tests.

The SapResult->EnergyBreakdown adapter + BillDerivation wiring (to populate the
bill block) follow once the appliances/cooking SapResult fields land.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 10:04:24 +00:00

48 lines
1.5 KiB
Python

from __future__ import annotations
from typing import Optional
import pytest
from datatypes.epc.domain.epc import Epc
from datatypes.epc.domain.epc_property_data import EpcPropertyData
from domain.property_baseline.performance import Performance
from domain.property_baseline.rebaseliner import RebaselineNotImplemented, StubRebaseliner
def _epc(*, sap_version: Optional[float]) -> EpcPropertyData:
epc = object.__new__(EpcPropertyData)
epc.sap_version = sap_version
return epc
def _lodged() -> Performance:
return Performance(
sap_score=72, epc_band=Epc.C, co2_emissions=1.8, primary_energy_intensity=180
)
def test_sap10_epc_is_not_rebaselined_so_effective_equals_lodged() -> None:
# Arrange — a SAP 10.2 cert: no rebaselining trigger fires.
epc = _epc(sap_version=10.2)
lodged = _lodged()
rebaseliner = StubRebaseliner()
# Act
effective, reason = rebaseliner.rebaseline(10, epc, lodged)
# Assert — Effective Performance equals Lodged, reason "none".
assert effective == lodged
assert reason == "none"
def test_pre_sap10_epc_raises_because_rebaselining_is_not_implemented() -> None:
# Arrange — a cert lodged under a pre-SAP10 schema genuinely needs ML
# rebaselining, which does not exist yet; the stub must not fabricate a
# "none" answer for it.
epc = _epc(sap_version=9.94)
rebaseliner = StubRebaseliner()
# Act / Assert
with pytest.raises(RebaselineNotImplemented):
rebaseliner.rebaseline(10, epc, _lodged())