From 5ec0fa5d042628e45956f71b93f5e9d0e1edbdda Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Thu, 7 May 2026 13:01:04 +0000 Subject: [PATCH] =?UTF-8?q?save=5Fplan=20persisting=20domain=20Plan=20to?= =?UTF-8?q?=20magic=5Fplan=5F*=20tables=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/db/functions/tests/__init__.py | 0 .../tests/test_magic_plan_functions.py | 130 ++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 backend/app/db/functions/tests/__init__.py create mode 100644 backend/app/db/functions/tests/test_magic_plan_functions.py diff --git a/backend/app/db/functions/tests/__init__.py b/backend/app/db/functions/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/app/db/functions/tests/test_magic_plan_functions.py b/backend/app/db/functions/tests/test_magic_plan_functions.py new file mode 100644 index 00000000..c9785e26 --- /dev/null +++ b/backend/app/db/functions/tests/test_magic_plan_functions.py @@ -0,0 +1,130 @@ +import json +from pathlib import Path +from unittest.mock import MagicMock + +import pytest +from sqlalchemy.dialects import postgresql + +from datatypes.magicplan.api.response import MagicPlan +from datatypes.magicplan.domain.mapper import map_plan +from datatypes.magicplan.domain.models import Plan + +from backend.app.db.functions.magic_plan_functions import save_plan + +FIXTURE_DIR = Path(__file__).parents[4] / "magic_plan" +PLAN_UID = "a7285ed1-878d-47eb-8aa6-85ef9e187516" + +# fixture 1: 2 floors, 14 rooms total, 13 windows, 27 doors + + +@pytest.fixture(scope="module") +def domain_plan() -> Plan: + data = json.loads((FIXTURE_DIR / "magicplan_api_plan_response_example.json").read_text()) + return map_plan(MagicPlan.model_validate(data["data"])) + + +def _compiled(stmt: object) -> str: + return str(stmt.compile( # type: ignore[union-attr] + dialect=postgresql.dialect(), + compile_kwargs={"literal_binds": True}, + )) + + +@pytest.fixture() +def mock_session() -> MagicMock: + session = MagicMock() + + plan_result = MagicMock() + plan_result.scalar_one.return_value = 1 + + floor_result = MagicMock() + floor_result.scalars.return_value.all.return_value = [10, 20] + + room_result = MagicMock() + room_result.scalars.return_value.all.return_value = list(range(100, 114)) + + session.execute.side_effect = [ + plan_result, # upsert plan + None, # delete windows + None, # delete doors + None, # delete rooms + None, # delete floors + floor_result, # insert floors + room_result, # insert rooms + None, # insert windows + None, # insert doors + ] + return session + + +# --- save_plan orchestration --- + + +def test_save_plan_does_not_raise(mock_session: MagicMock, domain_plan: Plan) -> None: + # Act + save_plan(mock_session, domain_plan) + + +def test_save_plan_upserts_plan_table_first(mock_session: MagicMock, domain_plan: Plan) -> None: + # Act + save_plan(mock_session, domain_plan) + # Assert + first_stmt = mock_session.execute.call_args_list[0][0][0] + sql = _compiled(first_stmt) + assert "magic_plan_plan" in sql + assert "INSERT" in sql.upper() + + +def test_save_plan_upsert_contains_plan_uid(mock_session: MagicMock, domain_plan: Plan) -> None: + # Act + save_plan(mock_session, domain_plan) + # Assert + first_stmt = mock_session.execute.call_args_list[0][0][0] + assert PLAN_UID in _compiled(first_stmt) + + +def test_save_plan_upsert_contains_plan_name(mock_session: MagicMock, domain_plan: Plan) -> None: + # Act + save_plan(mock_session, domain_plan) + # Assert + first_stmt = mock_session.execute.call_args_list[0][0][0] + assert domain_plan.name in _compiled(first_stmt) + + +def test_save_plan_deletes_floors_before_inserting(mock_session: MagicMock, domain_plan: Plan) -> None: + # Act + save_plan(mock_session, domain_plan) + # Assert — find delete and insert stmts targeting magic_plan_floor + stmts = [_compiled(c[0][0]) for c in mock_session.execute.call_args_list] + floor_delete_idx = next(i for i, s in enumerate(stmts) if "DELETE" in s.upper() and "magic_plan_floor" in s) + floor_insert_idx = next(i for i, s in enumerate(stmts) if "INSERT" in s.upper() and "magic_plan_floor" in s) + assert floor_delete_idx < floor_insert_idx + + +def test_save_plan_inserts_correct_floor_count(mock_session: MagicMock, domain_plan: Plan) -> None: + # Act + save_plan(mock_session, domain_plan) + # Assert — floor INSERT contains values for both floors + stmts = [_compiled(c[0][0]) for c in mock_session.execute.call_args_list] + floor_insert = next(s for s in stmts if "INSERT" in s.upper() and "magic_plan_floor" in s) + # Each floor appears as a row — level values 0 and 1 from fixture + assert floor_insert.count("magic_plan_plan_id") >= len(domain_plan.floors) + + +def test_save_plan_inserts_correct_room_count(mock_session: MagicMock, domain_plan: Plan) -> None: + # Act + save_plan(mock_session, domain_plan) + # Assert — room INSERT contains values for all 14 rooms + total_rooms = sum(len(f.rooms) for f in domain_plan.floors) + stmts = [_compiled(c[0][0]) for c in mock_session.execute.call_args_list] + room_insert = next(s for s in stmts if "INSERT" in s.upper() and "magic_plan_room" in s) + assert room_insert.count("magic_plan_floor_id") >= total_rooms + + +def test_save_plan_windows_use_room_ids_from_insert(mock_session: MagicMock, domain_plan: Plan) -> None: + # Act + save_plan(mock_session, domain_plan) + # Assert — window INSERT references one of the mocked room ids (100–113) + stmts = [_compiled(c[0][0]) for c in mock_session.execute.call_args_list] + window_insert = next(s for s in stmts if "INSERT" in s.upper() and "magic_plan_window" in s) + assert any(str(rid) in window_insert for rid in range(100, 114))