From 1041c8ed0e17cecddad1fa6886ad8911dd121819 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 26 Jun 2026 16:43:26 +0000 Subject: [PATCH] =?UTF-8?q?test:=20solar=20PV=20arrays=20must=20round-trip?= =?UTF-8?q?=20through=20persistence=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sap_energy_source.photovoltaic_arrays has no table, so every array is dropped on save — worth ~12 SAP points on an electrically-heated dwelling (persist != score). Inject two ordered arrays onto a PV-free fixture. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/repositories/epc/test_epc_round_trip.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/repositories/epc/test_epc_round_trip.py b/tests/repositories/epc/test_epc_round_trip.py index ed49b6d9..ab6b7f65 100644 --- a/tests/repositories/epc/test_epc_round_trip.py +++ b/tests/repositories/epc/test_epc_round_trip.py @@ -86,6 +86,44 @@ def test_non_separated_conservatory_round_trips(db_engine: Engine) -> None: assert reloaded == original +def test_photovoltaic_arrays_round_trip(db_engine: Engine) -> None: + # SAP 10.2 Appendix M — a dwelling's solar PV arrays generate electricity that + # offsets demand, worth a large slice of the SAP score (≈12 points on an + # electrically-heated dwelling). `sap_energy_source.photovoltaic_arrays` had + # no table, so every array was dropped on save (persist != score). We inject + # two arrays (distinct, so ORDER is observable) onto a clean PV-free fixture + # so the ONLY thing that can break deep-equality is the array list itself. + from dataclasses import replace + + from datatypes.epc.domain.epc_property_data import PhotovoltaicArray + + # Arrange — a green fixture with no PV, plus two ordered arrays. + original = _load_epc("RdSAP-Schema-21.0.1") + assert original.sap_energy_source.photovoltaic_arrays is None, ( + "fixture must start PV-free so the array list is the only variable" + ) + arrays = [ + PhotovoltaicArray(peak_power=3.24, pitch=3, overshading=1, orientation=3), + PhotovoltaicArray(peak_power=1.5, pitch=2, overshading=2, orientation=None), + ] + original = replace( + original, + sap_energy_source=replace( + original.sap_energy_source, photovoltaic_arrays=arrays + ), + ) + + # Act + with Session(db_engine) as session: + epc_property_id = EpcPostgresRepository(session).save(original) + session.commit() + with Session(db_engine) as session: + reloaded = EpcPostgresRepository(session).get(epc_property_id) + + # Assert — both arrays survive in order, deep-equal. + assert reloaded == original + + def test_floor_dimension_heat_loss_flags_round_trip(db_engine: Engine) -> None: # SAP 10.2 §3.3 — `is_exposed_floor` and `is_above_partially_heated_space` # are heat-loss flags on a `SapFloorDimension`: the calculator routes a floor