Model/tests/domain/scenario_export/test_scenario_sheet.py
Khalim Conn-Kowlessar df768d882e Total each Property's retrofit cost from its measure costs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 23:11:59 +00:00

144 lines
4.7 KiB
Python

"""The Scenario Export sheet shape (ADR-0065) — pure layout of one scenario
sheet: measure costs pivoted onto a frozen column contract, savings summed per
Property, and the Plan's post-works figures."""
from typing import Optional, Sequence
import pytest
from domain.scenario_export.scenario_sheet import (
MEASURE_COLUMNS,
ExportMeasure,
PropertyScenarioData,
shape_scenario_sheet,
)
def _measure(
*,
measure_type: str = "loft_insulation",
estimated_cost: Optional[float] = 1000.0,
sap_points: Optional[float] = None,
co2_equivalent_savings: Optional[float] = None,
kwh_savings: Optional[float] = None,
energy_cost_savings: Optional[float] = None,
includes_battery: bool = False,
) -> ExportMeasure:
return ExportMeasure(
measure_type=measure_type,
estimated_cost=estimated_cost,
sap_points=sap_points,
co2_equivalent_savings=co2_equivalent_savings,
kwh_savings=kwh_savings,
energy_cost_savings=energy_cost_savings,
includes_battery=includes_battery,
)
def _property(
*,
property_id: int = 1,
landlord_property_id: Optional[str] = "LP1",
measures: Sequence[ExportMeasure] = (),
**fields: object,
) -> PropertyScenarioData:
return PropertyScenarioData(
property_id=property_id,
landlord_property_id=landlord_property_id,
measures=measures,
**fields, # pyright: ignore[reportArgumentType]
)
def test_a_measures_cost_lands_in_its_own_column_with_the_property_identity() -> None:
# arrange — one Property with one loft-insulation measure priced at £1200.
prop = _property(
property_id=42,
landlord_property_id="LP42",
measures=[_measure(measure_type="loft_insulation", estimated_cost=1200.0)],
)
# act
sheet = shape_scenario_sheet([prop])
# assert — one row carrying the Property identity and the cost in the
# loft_insulation column.
assert len(sheet.rows) == 1
row = sheet.rows[0]
assert row["property_id"] == 42
assert row["landlord_property_id"] == "LP42"
assert row["loft_insulation"] == 1200.0
def test_every_measure_column_is_present_and_blank_where_the_property_lacks_it() -> None:
# arrange — a Property with a single measure. Every other measure column
# must still appear on the sheet (the frozen contract, ADR-0065), blank for
# this Property, so all scenario sheets share one column set.
prop = _property(
measures=[_measure(measure_type="cavity_wall_insulation", estimated_cost=800.0)]
)
# act
sheet = shape_scenario_sheet([prop])
# assert — the full frozen measure contract is present; the one selected
# measure carries its cost, every other measure column is blank.
assert set(MEASURE_COLUMNS).issubset(set(sheet.columns))
row = sheet.rows[0]
assert row["cavity_wall_insulation"] == 800.0
assert row["loft_insulation"] == ""
assert row["solar_pv"] == ""
def test_savings_and_sap_points_are_summed_across_a_propertys_measures() -> None:
# arrange — two measures, each pivoting to its own cost column and each
# contributing SAP points and carbon / energy / bill savings.
prop = _property(
measures=[
_measure(
measure_type="loft_insulation",
estimated_cost=1000.0,
sap_points=3.0,
co2_equivalent_savings=0.4,
kwh_savings=900.0,
energy_cost_savings=120.0,
),
_measure(
measure_type="solar_pv",
estimated_cost=5000.0,
sap_points=5.0,
co2_equivalent_savings=1.1,
kwh_savings=2500.0,
energy_cost_savings=300.0,
),
]
)
# act
sheet = shape_scenario_sheet([prop])
# assert — each cost pivots to its own column, and the savings roll up to the
# Property's totals.
row = sheet.rows[0]
assert row["loft_insulation"] == 1000.0
assert row["solar_pv"] == 5000.0
assert row["sap_points"] == 8.0
assert row["co2_equivalent_savings"] == pytest.approx(1.5)
assert row["kwh_savings"] == 3400.0
assert row["energy_cost_savings"] == 420.0
def test_total_retrofit_cost_sums_the_per_measure_costs() -> None:
# arrange — a Property with two priced measures.
prop = _property(
measures=[
_measure(measure_type="loft_insulation", estimated_cost=1200.0),
_measure(measure_type="cavity_wall_insulation", estimated_cost=800.0),
]
)
# act
sheet = shape_scenario_sheet([prop])
# assert — the package total is the sum of the measure costs.
assert sheet.rows[0]["total_retrofit_cost"] == 2000.0