From 1a8b27453e90df099d5c720d29578b9e120d0bdd Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Thu, 7 May 2026 13:11:18 +0000 Subject: [PATCH] =?UTF-8?q?MagicPlanService=20orchestrating=20fetch,=20mat?= =?UTF-8?q?ch,=20map,=20persist=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/test_magic_plan_service.py | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 backend/magic_plan/tests/test_magic_plan_service.py diff --git a/backend/magic_plan/tests/test_magic_plan_service.py b/backend/magic_plan/tests/test_magic_plan_service.py new file mode 100644 index 00000000..8ae7fbda --- /dev/null +++ b/backend/magic_plan/tests/test_magic_plan_service.py @@ -0,0 +1,128 @@ +import json +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pytest + +from datatypes.magicplan.api.response import MagicPlan, PlanSummary +from datatypes.magicplan.domain.mapper import map_plan +from datatypes.magicplan.domain.models import Plan + +from backend.magic_plan.magic_plan_client import MagicPlanClient +from backend.magic_plan.magic_plan_service import MagicPlanService + +FIXTURE_DIR = Path(__file__).parents[2] / "magic_plan" +PLAN_ID = "a7285ed1-878d-47eb-8aa6-85ef9e187516" + + +@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"])) + + +@pytest.fixture(scope="module") +def api_magic_plan() -> MagicPlan: + data = json.loads((FIXTURE_DIR / "magicplan_api_plan_response_example.json").read_text()) + return MagicPlan.model_validate(data["data"]) + + +@pytest.fixture(scope="module") +def plan_summary() -> PlanSummary: + data = json.loads((FIXTURE_DIR / "magicplan_api_plan_response_example.json").read_text()) + return MagicPlan.model_validate(data["data"]).plan + + +@pytest.fixture() +def mock_client() -> MagicMock: + return MagicMock(spec=MagicPlanClient) + + +def _make_service(mock_client: MagicMock) -> MagicPlanService: + return MagicPlanService(client=mock_client) + + +# --- no match --- + + +def test_run_raises_when_no_plan_found(mock_client: MagicMock) -> None: + # Arrange + mock_client.get_plans.return_value.plans = [] + service = _make_service(mock_client) + # Act / Assert + with pytest.raises(ValueError, match="No MagicPlan found"): + service.run("99 Nowhere Road London SW1A 1AA") + + +# --- match found --- + + +def test_run_fetches_plan_with_matched_id( + mock_client: MagicMock, + api_magic_plan: MagicPlan, + plan_summary: PlanSummary, + domain_plan: Plan, +) -> None: + # Arrange + mock_client.get_plans.return_value.plans = [plan_summary] + mock_client.get_plan.return_value = api_magic_plan + service = _make_service(mock_client) + with patch("backend.magic_plan.magic_plan_service.find_matching_plan", return_value=plan_summary), \ + patch("backend.magic_plan.magic_plan_service.save_plan"), \ + patch("backend.magic_plan.magic_plan_service.db_session"): + service.run("2 Laburnum Way Bromley BR2 8BZ") + # Assert + mock_client.get_plan.assert_called_once_with(plan_summary.id) + + +def test_run_returns_mapped_plan( + mock_client: MagicMock, + api_magic_plan: MagicPlan, + plan_summary: PlanSummary, + domain_plan: Plan, +) -> None: + # Arrange + mock_client.get_plans.return_value.plans = [plan_summary] + mock_client.get_plan.return_value = api_magic_plan + service = _make_service(mock_client) + with patch("backend.magic_plan.magic_plan_service.find_matching_plan", return_value=plan_summary), \ + patch("backend.magic_plan.magic_plan_service.save_plan"), \ + patch("backend.magic_plan.magic_plan_service.db_session"): + result = service.run("2 Laburnum Way Bromley BR2 8BZ") + # Assert + assert isinstance(result, Plan) + assert result.uid == PLAN_ID + + +def test_run_calls_save_plan_with_mapped_plan( + mock_client: MagicMock, + api_magic_plan: MagicPlan, + plan_summary: PlanSummary, +) -> None: + # Arrange + mock_client.get_plans.return_value.plans = [plan_summary] + mock_client.get_plan.return_value = api_magic_plan + service = _make_service(mock_client) + with patch("backend.magic_plan.magic_plan_service.find_matching_plan", return_value=plan_summary), \ + patch("backend.magic_plan.magic_plan_service.save_plan") as mock_save, \ + patch("backend.magic_plan.magic_plan_service.db_session"): + service.run("2 Laburnum Way Bromley BR2 8BZ") + # Assert — save_plan called with a Plan whose uid matches + call_args = mock_save.call_args + saved_plan: Plan = call_args[0][1] + assert saved_plan.uid == PLAN_ID + + +def test_run_accepts_uprn_without_error( + mock_client: MagicMock, + api_magic_plan: MagicPlan, + plan_summary: PlanSummary, +) -> None: + # Arrange + mock_client.get_plans.return_value.plans = [plan_summary] + mock_client.get_plan.return_value = api_magic_plan + service = _make_service(mock_client) + with patch("backend.magic_plan.magic_plan_service.find_matching_plan", return_value=plan_summary), \ + patch("backend.magic_plan.magic_plan_service.save_plan"), \ + patch("backend.magic_plan.magic_plan_service.db_session"): + service.run("2 Laburnum Way Bromley BR2 8BZ", uprn="100023336956")