Model/tests/domain/epc_prediction/test_prediction_target.py
Khalim Conn-Kowlessar f2f954f459 feat(epc-prediction): slice-5d target assembly + eligibility gate
build_prediction_target assembles an EPC-less Property's PredictionTarget from
its identity (postcode), resolved coordinates, and Landlord-Override attributes
(property_type / built_form / wall_construction). The eligibility GATE: a Property
whose property_type is unknown returns None — never sized from a mixed-type
cohort (ADR-0031). property_type is the hard cohort filter.

The override attributes are read through a PredictionTargetAttributesReader port
(stub seam) — the real adapter (a read over property_overrides) is being built
separately by the team; ingestion wiring depends on the abstraction and tests
substitute a fake. 2 tests (assembly + gate); pyright strict clean.

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

56 lines
1.9 KiB
Python

"""Assembling an EPC-less Property's PredictionTarget, and the eligibility gate:
a Property whose property type is unknown is not predicted (ADR-0031 slice-5d)."""
from __future__ import annotations
from typing import Optional
from domain.epc_prediction.comparable_properties import PredictionTarget
from domain.epc_prediction.prediction_target import (
PredictionTargetAttributes,
build_prediction_target,
)
from domain.geospatial.coordinates import Coordinates
from domain.property.property import PropertyIdentity
def _identity(postcode: str = "LS6 1AA") -> PropertyIdentity:
return PropertyIdentity(
portfolio_id=1, postcode=postcode, address="1 Some Street", uprn=12345
)
def test_target_is_assembled_from_identity_coords_and_overrides() -> None:
# Arrange — a known property type + built form + wall (Landlord Overrides),
# and resolved coordinates.
here = Coordinates(longitude=-1.55, latitude=53.81)
attributes = PredictionTargetAttributes(
property_type="2", built_form="3", wall_construction=1
)
# Act
target: Optional[PredictionTarget] = build_prediction_target(
_identity(), here, attributes
)
# Assert — every known input is threaded onto the target.
assert target is not None
assert target.postcode == "LS6 1AA"
assert target.property_type == "2"
assert target.built_form == "3"
assert target.wall_construction == 1
assert target.coordinates is here
def test_an_unknown_property_type_gates_the_property_out() -> None:
# Arrange — property type is the hard cohort filter; without it the Property
# must not be predicted from a mixed-type cohort (ADR-0031).
attributes = PredictionTargetAttributes(property_type=None)
# Act
target: Optional[PredictionTarget] = build_prediction_target(
_identity(), None, attributes
)
# Assert — gated out: no target to predict from.
assert target is None