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")