Model/backend/magic_plan/tests/test_audit_script.py

320 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import openpyxl
from datatypes.magicplan.domain.models import Door, Floor, Plan, Room, Window
from backend.magic_plan.audit_script import AuditRow, _apply_section_borders, _build_rows, _write_data_rows, _write_headers
def test_build_rows_opening_and_trickle_vent_fields_are_none() -> None:
# Arrange — plan with one window so opening/trickle_vent fields are exercised
plan = Plan(
uid="x",
name=None,
floors=[
Floor(level=0, name=None, rooms=[
Room(
name="Living Room",
width_m=3.0,
length_m=4.0,
area_m2=12.0,
windows=[Window(width_m=1.0, height_m=1.0, area_m2=1.0, opening_type="side_hung")],
),
]),
],
)
# Act
rows = _build_rows(plan)
# Assert — all new forward-compatible fields default to None
data_row = next(r for r in rows if r.window_label is not None)
assert data_row.opening_count is None
assert data_row.opening_width_m is None
assert data_row.opening_height_m is None
assert data_row.opening_total_area_m2 is None
assert data_row.trickle_vent_blocked is None
assert data_row.trickle_vent_pictured is None
assert data_row.trickle_vent_effective_area is None
assert data_row.trickle_vent_count is None
assert data_row.trickle_vent_total_effective_area is None
def test_build_rows_empty_plan_returns_empty_list() -> None:
# Arrange
plan = Plan(uid="x", name=None, floors=[])
# Act
rows: list[AuditRow] = _build_rows(plan)
# Assert
assert rows == []
def test_build_rows_single_room_no_windows_or_doors() -> None:
# Arrange
plan = Plan(
uid="x",
name=None,
floors=[
Floor(level=0, name=None, rooms=[
Room(name="Hall", width_m=2.0, length_m=3.0, area_m2=6.0),
]),
],
)
# Act
rows: list[AuditRow] = _build_rows(plan)
# Assert — separator + one data row
assert len(rows) == 2
separator, data = rows
assert separator.floor_level == 0
assert data.room_name == "Hall"
assert data.room_width_m == 2.0
assert data.room_length_m == 3.0
assert data.room_area_m2 == 6.0
assert data.window_label is None
assert data.door_location is None
def test_build_rows_room_with_more_windows_than_doors() -> None:
# Arrange — 2 windows, 1 door → 2 data rows; room columns blank on row 2
plan = Plan(
uid="x",
name=None,
floors=[
Floor(level=0, name=None, rooms=[
Room(
name="Lounge",
width_m=4.0,
length_m=5.0,
area_m2=20.0,
windows=[
Window(width_m=1.0, height_m=1.0, area_m2=1.0, opening_type="side_hung"),
Window(width_m=0.8, height_m=1.0, area_m2=0.8, opening_type="top_hung"),
],
doors=[Door(width_mm=762.0)],
),
]),
],
)
# Act
rows: list[AuditRow] = _build_rows(plan)
# Assert
data_rows = [r for r in rows if r.floor_level is None]
assert len(data_rows) == 2
# row 0 carries room columns
assert data_rows[0].room_name == "Lounge"
assert data_rows[0].window_label == "W1"
assert data_rows[0].door_location == "Lounge"
# row 1 room columns are blank
assert data_rows[1].room_name is None
assert data_rows[1].window_label == "W2"
assert data_rows[1].door_location is None
def test_build_rows_room_with_more_doors_than_windows() -> None:
# Arrange — 1 window, 2 doors → 2 data rows
plan = Plan(
uid="x",
name=None,
floors=[
Floor(level=0, name=None, rooms=[
Room(
name="Bedroom",
width_m=3.5,
length_m=4.0,
area_m2=14.0,
windows=[Window(width_m=1.2, height_m=1.0, area_m2=1.2, opening_type="side_hung")],
doors=[Door(width_mm=762.0), Door(width_mm=686.0)],
),
]),
],
)
# Act
rows: list[AuditRow] = _build_rows(plan)
# Assert
data_rows = [r for r in rows if r.floor_level is None]
assert len(data_rows) == 2
assert data_rows[0].window_label == "W1"
assert data_rows[0].door_width_mm == 762.0
assert data_rows[1].window_label is None
assert data_rows[1].door_width_mm == 686.0
def test_build_rows_floor_separator_precedes_rooms() -> None:
# Arrange
plan = Plan(
uid="x",
name=None,
floors=[
Floor(level=0, name=None, rooms=[
Room(name="Room A", width_m=1.0, length_m=1.0, area_m2=1.0),
]),
Floor(level=1, name=None, rooms=[
Room(name="Room B", width_m=1.0, length_m=1.0, area_m2=1.0),
]),
],
)
# Act
rows: list[AuditRow] = _build_rows(plan)
# Assert — rows in order: sep(0), data, sep(1), data
assert rows[0].floor_level == 0
assert rows[1].room_name == "Room A"
assert rows[2].floor_level == 1
assert rows[3].room_name == "Room B"
def test_build_rows_window_labels_sequential_across_floors() -> None:
# Arrange — two floors, one window each; labels must be W1, W2 (no reset)
plan = Plan(
uid="x",
name=None,
floors=[
Floor(level=0, name=None, rooms=[
Room(
name="Ground Room",
width_m=1.0,
length_m=1.0,
area_m2=1.0,
windows=[Window(width_m=1.0, height_m=1.0, area_m2=1.0, opening_type="side_hung")],
),
]),
Floor(level=1, name=None, rooms=[
Room(
name="First Room",
width_m=1.0,
length_m=1.0,
area_m2=1.0,
windows=[Window(width_m=1.0, height_m=1.0, area_m2=1.0, opening_type="top_hung")],
),
]),
],
)
# Act
rows: list[AuditRow] = _build_rows(plan)
# Assert
labels = [r.window_label for r in rows if r.window_label is not None]
assert labels == ["W1", "W2"]
def test_build_rows_room_name_in_location_on_every_row() -> None:
# Arrange — room with 2 windows and 2 doors
plan = Plan(
uid="x",
name=None,
floors=[
Floor(level=0, name=None, rooms=[
Room(
name="Kitchen",
width_m=3.0,
length_m=3.0,
area_m2=9.0,
windows=[
Window(width_m=1.0, height_m=1.0, area_m2=1.0, opening_type="side_hung"),
Window(width_m=0.8, height_m=1.0, area_m2=0.8, opening_type="top_hung"),
],
doors=[Door(width_mm=762.0), Door(width_mm=686.0)],
),
]),
],
)
# Act
rows: list[AuditRow] = _build_rows(plan)
# Assert — every data row carries "Kitchen" in location fields
data_rows = [r for r in rows if r.floor_level is None]
for row in data_rows:
assert row.window_location == "Kitchen"
assert row.door_location == "Kitchen"
def test_write_headers_two_rows_correct_labels_and_column_positions() -> None:
# Arrange
wb = openpyxl.Workbook()
ws = wb.active # type: ignore[assignment]
# Act
_write_headers(ws)
# Assert — group labels in row 1 at the new column positions
assert ws.cell(row=1, column=1).value == "Room"
assert ws.cell(row=1, column=6).value == "Window Area"
assert ws.cell(row=1, column=12).value == "Openings"
assert ws.cell(row=1, column=16).value == "Trickle Vents"
assert ws.cell(row=1, column=22).value == "Doors"
# Assert — "Door Undercuts" label no longer used
assert ws.cell(row=1, column=28).value is None
# Assert — only two header rows; row 3 is empty
assert ws.cell(row=3, column=1).value is None
assert ws.cell(row=3, column=13).value is None
# Assert — opening columns use self-describing labels (no "Opening 1" sub-header)
assert ws.cell(row=2, column=12).value == "No. of Openings"
assert ws.cell(row=2, column=13).value == "Opening Width m"
assert ws.cell(row=2, column=14).value == "Opening Height m"
assert ws.cell(row=2, column=15).value == "Total Area m²"
def test_apply_section_borders_sets_medium_right_border_on_boundary_columns() -> None:
# Arrange
wb = openpyxl.Workbook()
ws = wb.active # type: ignore[assignment]
# Act
_apply_section_borders(ws, 1)
# Assert — medium right-border on last col of each subtable
for col in (4, 11, 15, 20):
assert ws.cell(row=1, column=col).border.right.style == "medium", f"col {col}"
# Assert — non-boundary columns have no medium right-border
for col in (1, 6, 12, 22):
right = ws.cell(row=1, column=col).border.right
assert right is None or right.style != "medium", f"col {col}"
def test_write_data_rows_places_door_data_at_cols_22_to_25() -> None:
# Arrange
wb = openpyxl.Workbook()
ws = wb.active # type: ignore[assignment]
rows = [
AuditRow(
room_name="Hall",
room_width_m=2.0,
room_length_m=3.0,
room_area_m2=6.0,
door_location="Hall",
door_width_mm=762.0,
),
]
# Act
_write_data_rows(ws, rows)
# Assert — door columns at VY (2225), not old positions 2831
# data starts at row 3 (after 2 header rows)
assert ws.cell(row=3, column=22).value == "Hall"
assert ws.cell(row=3, column=23).value == 762.0
assert ws.cell(row=3, column=28).value is None
assert ws.cell(row=3, column=29).value is None
def test_write_headers_blank_separator_columns_have_no_fill() -> None:
# Arrange
wb = openpyxl.Workbook()
ws = wb.active # type: ignore[assignment]
# Act
_write_headers(ws)
# Assert — cols 5 (E) and 21 (U) have no fill in either header row
for col in (5, 21):
for row in (1, 2):
fill = ws.cell(row=row, column=col).fill
assert fill.patternType is None, f"col {col} row {row} should have no fill"