From 355c1e7d2fc5f433daca66ffd0d854830eea7552 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 6 Jul 2026 11:06:45 +0000 Subject: [PATCH] =?UTF-8?q?Age=20band=20conditions=20within=20one=20band;?= =?UTF-8?q?=20the=20floor-area=20band=20widens=20to=20=C2=B120%=20?= =?UTF-8?q?=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- .../test_comparable_properties.py | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/tests/domain/epc_prediction/test_comparable_properties.py b/tests/domain/epc_prediction/test_comparable_properties.py index db6b386ca..9bf903718 100644 --- a/tests/domain/epc_prediction/test_comparable_properties.py +++ b/tests/domain/epc_prediction/test_comparable_properties.py @@ -243,21 +243,24 @@ def test_historic_main_fuel_conditions_the_cohort() -> None: } -def test_floor_area_band_keeps_comparables_within_5_percent() -> None: - # Arrange — the expired cert observed 100 m²; the band is ±5% (ADR-0054): - # 96 and 104.9 are in, 90 and 111 are out. +def test_floor_area_band_keeps_comparables_within_20_percent() -> None: + # Arrange — the expired cert observed 100 m²; the band is ±20% (ADR-0054 as + # amended: the 439-pair harness put historic-vs-new TFA agreement at only + # 45% within ±5% but 82% within ±20% — a coarse dwelling-size filter, not a + # precision match): 96, 118 and 84 are in, 125 and 79 are out. target = PredictionTarget( postcode="LS6 1AA", property_type="2", total_floor_area_m2=100.0 ) candidates = [ _comparable(property_type="2", total_floor_area_m2=96.0, certificate_number="A"), _comparable( - property_type="2", total_floor_area_m2=104.9, certificate_number="B" + property_type="2", total_floor_area_m2=118.0, certificate_number="B" ), - _comparable(property_type="2", total_floor_area_m2=90.0, certificate_number="X"), + _comparable(property_type="2", total_floor_area_m2=84.0, certificate_number="C"), _comparable( - property_type="2", total_floor_area_m2=111.0, certificate_number="Y" + property_type="2", total_floor_area_m2=125.0, certificate_number="X" ), + _comparable(property_type="2", total_floor_area_m2=79.0, certificate_number="Y"), ] # Act @@ -266,7 +269,41 @@ def test_floor_area_band_keeps_comparables_within_5_percent() -> None: ) # Assert - assert {c.certificate_number for c in result.members} == {"A", "B"} + assert {c.certificate_number for c in result.members} == {"A", "B", "C"} + + +def test_historic_age_band_conditions_within_one_band() -> None: + # Arrange — the expired cert observed band C, but assessors re-band ±1 + # constantly (harness: 52% exact agreement, 90% within one band), so the + # filter keeps the band NEIGHBOURHOOD: B/C/D match, G does not (ADR-0054 as + # amended). + target = PredictionTarget( + postcode="LS6 1AA", property_type="2", construction_age_band="C" + ) + near = ["B", "C", "D", "D", "B"] + candidates = [ + _comparable( + property_type="2", + construction_age_band=band, + certificate_number=f"N{i}", + ) + for i, band in enumerate(near) + ] + [ + _comparable( + property_type="2", construction_age_band="G", certificate_number=f"G{i}" + ) + for i in range(2) + ] + + # Act + result: ComparableProperties = select_comparables( + target, candidates, minimum_cohort=5 + ) + + # Assert — the five band-neighbourhood comparables survive; band G drops. + assert {c.certificate_number for c in result.members} == { + "N0", "N1", "N2", "N3", "N4" + } def test_floor_area_band_relaxes_when_too_few_match() -> None: