Model/tests/domain/magicplan/test_mapper.py

231 lines
6.1 KiB
Python

import json
from pathlib import Path
from typing import Any
import pytest
import domain.magicplan.api.response as api
from domain.magicplan.api.response import MagicPlanPlan, Symbol, Vec3, WallItem
from domain.magicplan.mapper import (
_map_window, # pyright: ignore[reportPrivateUsage]
map_address,
map_plan,
)
from domain.magicplan.models import Plan
FIXTURE_DIR = Path(__file__).parents[3] / "tests" / "magic_plan"
PLAN_ID = "72efd2e0-b2b9-48cd-b82e-41f5b3166c9a"
@pytest.fixture(scope="module")
def raw_data() -> dict[str, Any]:
payload = json.loads(
(FIXTURE_DIR / "magicplan_api_plan_response.json").read_text()
)
return payload["data"]
@pytest.fixture(scope="module")
def mp(raw_data: dict[str, Any]) -> MagicPlanPlan:
return MagicPlanPlan.model_validate(raw_data)
@pytest.fixture(scope="module")
def plan(mp: MagicPlanPlan) -> Plan:
return map_plan(mp)
# --- Plan-level ---
def test_plan_uid(plan: Plan) -> None:
assert plan.uid == PLAN_ID
def test_plan_postcode(plan: Plan) -> None:
assert plan.postcode == "BR2 8DU"
def test_plan_address(plan: Plan) -> None:
assert plan.address == "20 Larch Way, Bromley, GB"
def test_floor_count(plan: Plan) -> None:
assert len(plan.floors) == 2
# --- Room dimensions ---
def test_first_room_name(plan: Plan) -> None:
assert plan.floors[0].rooms[0].name == "Kitchen"
def test_room_dimensions_are_floats(plan: Plan) -> None:
room = plan.floors[0].rooms[0]
assert isinstance(room.width_m, float)
assert isinstance(room.length_m, float)
assert isinstance(room.area_m2, float)
def test_room_area_rounded_to_2dp(plan: Plan) -> None:
room = plan.floors[0].rooms[0]
assert room.area_m2 == 9.39
def test_room_dimensions_parsed_from_string(plan: Plan) -> None:
room = plan.floors[0].rooms[0]
assert room.width_m == 3.19
assert room.length_m == 2.94
# --- Windows ---
def test_kitchen_has_windows(plan: Plan) -> None:
assert len(plan.floors[0].rooms[0].windows) >= 1
def test_window_fields_are_floats(plan: Plan) -> None:
window = plan.floors[0].rooms[0].windows[0]
assert isinstance(window.width_m, float)
assert isinstance(window.height_m, float)
assert isinstance(window.area_m2, float)
def test_window_area_is_width_times_height(plan: Plan) -> None:
window = plan.floors[0].rooms[0].windows[0]
assert window.area_m2 == round(window.width_m * window.height_m, 2)
def test_window_dimensions_rounded_to_2dp(plan: Plan) -> None:
window = plan.floors[0].rooms[0].windows[0]
assert window.width_m == 0.90
assert window.height_m == 1.00
assert window.area_m2 == 0.90
def test_hallway_has_no_windows(plan: Plan) -> None:
hallway = plan.floors[0].rooms[3]
assert hallway.name == "Hallway"
assert hallway.windows == []
# --- Doors ---
def test_kitchen_has_doors(plan: Plan) -> None:
assert len(plan.floors[0].rooms[0].doors) >= 1
def test_door_width_is_float(plan: Plan) -> None:
door = plan.floors[0].rooms[0].doors[0]
assert isinstance(door.width_mm, float)
def test_door_width_rounded_to_2dp(plan: Plan) -> None:
door = plan.floors[0].rooms[0].doors[0]
assert door.width_mm == 800.0
def test_door_height_is_correct(plan: Plan) -> None:
# Kitchen doorhinged has size.z = 2.04 m = 2040 mm
door = plan.floors[0].rooms[0].doors[0]
assert door.height_mm == 2040.0
# --- Ventilation ---
def test_window_with_no_custom_fields_has_no_ventilation() -> None:
wi = WallItem(
uid="test",
symbol=Symbol(id="windowcasement", name="Casement Window", valid=True),
size=Vec3(x=1.0, y=0.0, z=1.2),
position=Vec3(x=0.0, y=0.0, z=0.0),
rotation=Vec3(x=0.0, y=0.0, z=0.0),
)
window = _map_window(wi)
assert window.ventilation is None
def test_mapped_window_has_no_opening_type() -> None:
wi = WallItem(
uid="test",
symbol=Symbol(id="windowcasement", name="Casement Window", valid=True),
size=Vec3(x=1.0, y=0.0, z=1.2),
position=Vec3(x=0.0, y=0.0, z=0.0),
rotation=Vec3(x=0.0, y=0.0, z=0.0),
)
window = _map_window(wi)
assert not hasattr(window, "opening_type")
def test_kitchen_window_has_ventilation(plan: Plan) -> None:
window = plan.floors[0].rooms[0].windows[0]
assert window.ventilation is not None
assert window.ventilation.trickle_vent_area_mm2 == 1700
def test_toilet_door_has_ventilation_undercut(plan: Plan) -> None:
toilet_doors = plan.floors[0].rooms[2].doors
hinged = next(d for d in toilet_doors if d.ventilation is not None)
assert hinged.ventilation is not None
assert hinged.ventilation.undercut_mm == 70.0
def test_doorglass_is_classified_as_window(plan: Plan) -> None:
room = plan.floors[0].rooms[1]
assert any(
w.ventilation is not None and w.ventilation.opening_type == "External.Door"
for w in room.windows
)
def test_glass_door_ventilation_opening_type(plan: Plan) -> None:
room = plan.floors[0].rooms[1]
glass = next(
w for w in room.windows
if w.ventilation is not None and w.ventilation.opening_type == "External.Door"
)
assert glass.ventilation is not None
assert glass.ventilation.opening_type == "External.Door"
# --- Address unit tests ---
def test_map_address_with_street_and_number() -> None:
addr = api.Address(street_number="2", street="Laburnum Way", city="Bromley", country="GB")
assert map_address(addr) == "2 Laburnum Way, Bromley, GB"
def test_map_address_with_street_number_only() -> None:
addr = api.Address(street_number="2", city="Bromley", country="GB")
assert map_address(addr) == "2, Bromley, GB"
def test_map_address_with_street_only() -> None:
addr = api.Address(street="Laburnum Way", city="Bromley", country="GB")
assert map_address(addr) == "Laburnum Way, Bromley, GB"
def test_map_address_city_absent_is_omitted() -> None:
addr = api.Address(street_number="2", street="Laburnum Way", country="GB")
assert map_address(addr) == "2 Laburnum Way, GB"
def test_map_address_none_returns_none() -> None:
assert map_address(None) is None