Model/tests/harness/test_report.py
Khalim Conn-Kowlessar ecebb07c9e feat(modelling): calculator-error per property (lodged vs calculated SAP)
Section 1 of the property inspection report: PropertyReport compares the
cert's lodged energy_rating_current to Sap10Calculator's un-rounded SAP and
flags |Δ| > 0.5 (the ADR-0010/0013 shadow-validation design target). A
mapping/scoring raise is captured per-cert as calculator_error, never
propagated, so one bad cert can't abort the sweep.

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

68 lines
2.3 KiB
Python

"""Per-property inspection report over a dump of API-shaped EPC JSONs."""
from __future__ import annotations
import json
from pathlib import Path
from harness.report import PropertyReport, build_property_report
_GOLDEN = (
Path(__file__).resolve().parents[1]
/ "domain/sap10_calculator/rdsap/fixtures/golden"
)
# Two real golden certs straddling the |Δ| > 0.5 calculator-error flag:
# 0036 — lodged 63, calculated 62.747 -> Δ 0.253 (not flagged)
# 0240 — lodged 73, calculated 71.727 -> Δ 1.273 (flagged)
_WITHIN_TOLERANCE = "0036-6325-1100-0063-1226"
_DIVERGENT = "0240-0200-5706-2365-8010"
def test_calculator_error_is_lodged_minus_calculated_and_within_tolerance() -> None:
# Arrange
path: Path = _GOLDEN / f"{_WITHIN_TOLERANCE}.json"
# Act
report: PropertyReport = build_property_report(path)
# Assert — lodged SAP read straight off the cert; calculated un-rounded.
assert report.lodged_sap == 63
assert report.calculated_sap is not None
assert abs(report.calculated_sap - 62.747) <= 0.01
assert report.sap_error is not None
assert abs(report.sap_error - (63 - report.calculated_sap)) <= 1e-9
assert report.sap_error_exceeds_threshold is False
assert report.calculator_error is None
def test_calculator_error_flags_divergence_beyond_half_a_sap_point() -> None:
# Arrange
path: Path = _GOLDEN / f"{_DIVERGENT}.json"
# Act
report: PropertyReport = build_property_report(path)
# Assert — Δ 1.273 > 0.5, so the shadow-validation flag fires.
assert report.lodged_sap == 73
assert report.sap_error is not None
assert report.sap_error > 0.5
assert report.sap_error_exceeds_threshold is True
def test_unparseable_cert_is_captured_not_raised(tmp_path: Path) -> None:
# Arrange — a payload the mapper rejects must not abort the report.
bad: Path = tmp_path / "broken.json"
bad.write_text(json.dumps({"not": "an epc"}))
# Act
report: PropertyReport = build_property_report(bad)
# Assert — the raise is recorded as this property's calculator error.
assert report.name == "broken"
assert report.lodged_sap is None
assert report.calculated_sap is None
assert report.sap_error is None
assert report.sap_error_exceeds_threshold is False
assert report.calculator_error is not None
assert "ValueError" in report.calculator_error