Model/tests/domain/sap10_calculator/test_table_12.py
Khalim Conn-Kowlessar d7d5084f90 Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI
The calculator tests lived under domain/sap10_calculator/{tests,worksheet/
tests,rdsap/tests,climate/tests,validation/tests}, none of which are in
pytest.ini testpaths — so CI (which collects tests/) never ran them. Relocate
all five dirs to tests/domain/sap10_calculator/{,worksheet,rdsap,climate,
validation}, mirroring the tests/domain/property_baseline/ convention, so the
cascade-pin / golden / e2e conformance suites run in CI.

Mechanics:
- git mv preserves history (110 files).
- Flattening the trailing /tests keeps each file's depth-to-repo-root
  identical, so all 16 repo-root parents[4] fixture refs stay valid. Only
  test_pcdb_etl.py's parents[1] (→ pcdb data) and one hardcoded absolute
  golden-fixture path in test_cert_to_inputs.py needed rebasing.
- Cross-imports rewritten domain.sap10_calculator.worksheet.tests →
  tests.domain.sap10_calculator.worksheet (21 files incl. the external
  importer backend/documents_parser/tests/test_summary_pdf_mapper_chain.py).
- Golden-fixture path strings in test_summary_pdf_mapper_chain.py +
  scripts/fetch_cohort2_api_jsons.py updated to the new location (the JSONs
  moved with the rdsap tests).

load_cells / gitignored worksheet xlsx: the xlsx-pinned tests (test_dimensions
/ ventilation / water_heating) read 2026-05-19-17-18 RdSap10Worksheet.xlsx,
which is gitignored (.gitignore `*.xlsx`) and so absent in CI. _xlsx_loader.
load_cells now pytest.skip()s when the file is absent, so those tests run
locally and skip cleanly in CI instead of erroring — no new CI failures from
the move, and the gitignore policy is respected.

Verified: tests/domain/sap10_calculator + backend/documents_parser +
tests/domain/property_baseline = 2248 pass, 1 skipped; pyright resolves the
new import paths with zero import-resolution errors.

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

82 lines
2.9 KiB
Python

"""SAP 10.2 (14-03-2025 amendment) Table 12 value-correctness tests.
Locks the CO2 emission factors and primary energy factors against the
published SAP 10.2 specification at
`domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-03-14.pdf`, page 189.
The price column (`UNIT_PRICE_P_PER_KWH`) was already SAP 10.2-correct
when the calculator code was authored; the CO2 column was authored
against SAP 10.3 (13-01-2026) values by mistake. ADR-0010 retargets the
calculator to SAP 10.2 (14-03-2025) until the corpus migrates, so the
CO2 column was corrected during P2.4. These tests lock the corrected
values.
"""
from __future__ import annotations
import pytest
from domain.sap10_calculator.tables.table_12 import (
co2_factor_kg_per_kwh,
primary_energy_factor,
unit_price_p_per_kwh,
)
@pytest.mark.parametrize(
"fuel_code, expected_co2_kg_per_kwh, fuel_name",
[
# Most-common cases first — gas + electricity dominate the corpus.
(1, 0.210, "mains gas"),
(30, 0.136, "standard tariff electricity"),
# Sanity: heating oil is unchanged between SAP 10.2 and SAP 10.3.
(4, 0.298, "heating oil"),
# Off-peak electricity tariffs all share the annual-average factor.
(31, 0.136, "7-hour low rate electricity"),
(35, 0.136, "24-hour heating tariff"),
# Bulk LPG — SAP 10.2 says 0.241 (file had 0.24 rounded).
(2, 0.241, "bulk LPG"),
],
)
def test_co2_factor_matches_sap_10_2_table_12(
fuel_code: int, expected_co2_kg_per_kwh: float, fuel_name: str
) -> None:
# Arrange — table_12.co2_factor_kg_per_kwh is the only CO2 source
# in the calculator pipeline (see cert_to_inputs._co2_factor_kg_per_kwh).
# Act
actual = co2_factor_kg_per_kwh(fuel_code)
# Assert
assert actual == pytest.approx(expected_co2_kg_per_kwh, abs=1e-6), (
f"{fuel_name} (code {fuel_code}): expected SAP 10.2 CO2 factor "
f"{expected_co2_kg_per_kwh}, got {actual}. See SAP 10.2 PDF p.189."
)
def test_default_co2_factor_is_mains_gas_baseline() -> None:
# Arrange — unknown fuel codes fall back to mains gas (the SAP 10.2
# convention; see table_12._DEFAULT_CO2_KG_PER_KWH).
# Act
actual = co2_factor_kg_per_kwh(None)
# Assert
assert actual == pytest.approx(0.210, abs=1e-6)
def test_mains_gas_unit_price_unchanged_at_sap_10_2_value() -> None:
# Arrange — sanity: prices were already SAP 10.2-correct before P2.4.
# This locks that we didn't accidentally regress them while fixing CO2.
# Act
actual = unit_price_p_per_kwh(1)
# Assert
assert actual == pytest.approx(3.64, abs=1e-6)
def test_standard_electricity_primary_energy_factor_unchanged() -> None:
# Arrange — sanity: PE factor for electricity is 1.501 in both SAP
# 10.2 and SAP 10.3; locks that P2.4 didn't touch the PEF column.
# Act
actual = primary_energy_factor(30)
# Assert
assert actual == pytest.approx(1.501, abs=1e-6)