From ef5c44f7ae65c28a98c0437f197842c269ac5682 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Thu, 7 May 2026 12:49:15 +0000 Subject: [PATCH] =?UTF-8?q?MagicPlan=20HTTP=20client=20with=20auth=20heade?= =?UTF-8?q?rs=20and=20response=20parsing=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/test_magic_plan_client.py | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 backend/magic_plan/tests/test_magic_plan_client.py diff --git a/backend/magic_plan/tests/test_magic_plan_client.py b/backend/magic_plan/tests/test_magic_plan_client.py new file mode 100644 index 00000000..f8f35432 --- /dev/null +++ b/backend/magic_plan/tests/test_magic_plan_client.py @@ -0,0 +1,133 @@ +import json +from pathlib import Path +from typing import Any +from unittest.mock import MagicMock, patch + +import pytest +import requests + +from backend.magic_plan.magic_plan_client import MagicPlanClient +from datatypes.magicplan.api.response import MagicPlan, PlansListResponse + +FIXTURE_DIR = Path(__file__).parents[2] / "magic_plan" +BASE_URL = "https://cloud.magicplan.app/api/v2" +CUSTOMER_ID = "test-customer" +API_KEY = "test-key" + + +def _load_fixture(name: str) -> dict[str, Any]: + return json.loads((FIXTURE_DIR / name).read_text()) + + +def _make_client(mock_session: MagicMock) -> MagicPlanClient: + with patch("backend.magic_plan.magic_plan_client.requests.Session", return_value=mock_session): + return MagicPlanClient(customer_id=CUSTOMER_ID, api_key=API_KEY) + + +@pytest.fixture() +def mock_session() -> MagicMock: + return MagicMock(spec=requests.Session) + + +@pytest.fixture() +def client(mock_session: MagicMock) -> MagicPlanClient: + return _make_client(mock_session) + + +# --- constructor --- + + +def test_customer_header_set_on_session(mock_session: MagicMock) -> None: + # Act + _make_client(mock_session) + # Assert + mock_session.headers.update.assert_called_once_with({"customer": CUSTOMER_ID}) + + +# --- get_plans --- + + +def test_get_plans_calls_correct_url(client: MagicPlanClient, mock_session: MagicMock) -> None: + # Arrange + plans_data = _load_fixture("magicplan_api_plans_response_example.json")["data"] + mock_session.get.return_value.json.return_value = {"message": "OK", "data": plans_data} + # Act + client.get_plans() + # Assert + mock_session.get.assert_called_once_with( + f"{BASE_URL}/plans", params={"key": API_KEY} + ) + + +def test_get_plans_calls_raise_for_status(client: MagicPlanClient, mock_session: MagicMock) -> None: + # Arrange + plans_data = _load_fixture("magicplan_api_plans_response_example.json")["data"] + mock_session.get.return_value.json.return_value = {"message": "OK", "data": plans_data} + # Act + client.get_plans() + # Assert + mock_session.get.return_value.raise_for_status.assert_called_once() + + +def test_get_plans_returns_plans_list_response(client: MagicPlanClient, mock_session: MagicMock) -> None: + # Arrange + plans_data = _load_fixture("magicplan_api_plans_response_example.json")["data"] + mock_session.get.return_value.json.return_value = {"message": "OK", "data": plans_data} + # Act + result = client.get_plans() + # Assert + assert isinstance(result, PlansListResponse) + assert len(result.plans) == 1 + + +def test_get_plans_propagates_http_error(client: MagicPlanClient, mock_session: MagicMock) -> None: + # Arrange + mock_session.get.return_value.raise_for_status.side_effect = requests.HTTPError("404") + # Act / Assert + with pytest.raises(requests.HTTPError): + client.get_plans() + + +# --- get_plan --- + + +def test_get_plan_calls_correct_url(client: MagicPlanClient, mock_session: MagicMock) -> None: + # Arrange + plan_data = _load_fixture("magicplan_api_plan_response_example.json")["data"] + mock_session.get.return_value.json.return_value = {"message": "OK", "data": plan_data} + plan_id = "a7285ed1-878d-47eb-8aa6-85ef9e187516" + # Act + client.get_plan(plan_id) + # Assert + mock_session.get.assert_called_once_with( + f"{BASE_URL}/plans/{plan_id}", params={"key": API_KEY} + ) + + +def test_get_plan_calls_raise_for_status(client: MagicPlanClient, mock_session: MagicMock) -> None: + # Arrange + plan_data = _load_fixture("magicplan_api_plan_response_example.json")["data"] + mock_session.get.return_value.json.return_value = {"message": "OK", "data": plan_data} + # Act + client.get_plan("a7285ed1-878d-47eb-8aa6-85ef9e187516") + # Assert + mock_session.get.return_value.raise_for_status.assert_called_once() + + +def test_get_plan_returns_magic_plan(client: MagicPlanClient, mock_session: MagicMock) -> None: + # Arrange + plan_data = _load_fixture("magicplan_api_plan_response_example.json")["data"] + mock_session.get.return_value.json.return_value = {"message": "OK", "data": plan_data} + # Act + result = client.get_plan("a7285ed1-878d-47eb-8aa6-85ef9e187516") + # Assert + assert isinstance(result, MagicPlan) + assert result.plan.id == "a7285ed1-878d-47eb-8aa6-85ef9e187516" + + +def test_get_plan_propagates_http_error(client: MagicPlanClient, mock_session: MagicMock) -> None: + # Arrange + mock_session.get.return_value.raise_for_status.side_effect = requests.HTTPError("500") + # Act / Assert + with pytest.raises(requests.HTTPError): + client.get_plan("some-id")