mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
195 lines
8.1 KiB
Python
195 lines
8.1 KiB
Python
import json
|
|
import xml.etree.ElementTree as ET
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
import requests
|
|
|
|
from backend.magic_plan.magic_plan_client import MagicPlanClient
|
|
from backend.magic_plan.models import MagicPlanXMLDetail, MagicPlanXMLPlan, MagicPlanXMLSummary
|
|
|
|
FIXTURES = Path(__file__).parent.parent
|
|
API_KEY = "test-api-key"
|
|
PLAN_ID = "b66bb427-33fe-4865-8d57-bad7d9d3f2e5"
|
|
PROJECT_ID = "test-project-123"
|
|
BASE_URL = "https://cloud.magicplan.app/api/v2"
|
|
|
|
|
|
def _load_xml() -> str:
|
|
content = (FIXTURES / "xml_example.xml").read_text().strip()
|
|
try:
|
|
ET.fromstring(content)
|
|
return content
|
|
except ET.ParseError:
|
|
return json.loads(content) # type: ignore[return-value]
|
|
|
|
|
|
def _summary_payload(plan_id: str = PLAN_ID, index: int = 0) -> dict[str, str]:
|
|
return {
|
|
"id": plan_id,
|
|
"project_id": PROJECT_ID,
|
|
"name": f"Plan {index}",
|
|
"address": f"Address {index}",
|
|
"creation_date": "2026-04-20",
|
|
"update_date": "2026-04-20",
|
|
"workgroup_id": "wg-123",
|
|
"team_id": "team-123",
|
|
"created_by": "user-123",
|
|
"thumbnail_url": "https://example.com/thumb.jpg",
|
|
"public_url": "https://example.com/plan",
|
|
"cloud_url": "https://example.com/cloud",
|
|
"3d_url": "https://example.com/3d",
|
|
}
|
|
|
|
|
|
def _get_plan_response(xml_str: str) -> dict: # type: ignore[type-arg]
|
|
return {
|
|
"message": "success",
|
|
"data": {
|
|
"plan": _summary_payload(),
|
|
"plan_detail": {
|
|
"magicplan_format_xml": xml_str,
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def _get_plans_response(plan_ids: list[str]) -> dict: # type: ignore[type-arg]
|
|
return {
|
|
"message": "success",
|
|
"data": [_summary_payload(pid, i) for i, pid in enumerate(plan_ids)],
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_session() -> MagicMock:
|
|
with patch("backend.magic_plan.magic_plan_client.requests.Session") as MockSession:
|
|
session: MagicMock = MagicMock()
|
|
MockSession.return_value = session
|
|
yield session
|
|
|
|
|
|
@pytest.fixture
|
|
def client(mock_session: MagicMock) -> MagicPlanClient:
|
|
return MagicPlanClient(api_key=API_KEY)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Initialisation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestInit:
|
|
|
|
def test_sets_bearer_auth_header(self, mock_session: MagicMock) -> None:
|
|
MagicPlanClient(api_key=API_KEY)
|
|
mock_session.headers.update.assert_called_once_with(
|
|
{"Authorization": f"Bearer {API_KEY}"}
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_plan_xml
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestGetPlanXml:
|
|
|
|
def test_calls_correct_url(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
|
|
client.get_plan_xml(PLAN_ID)
|
|
mock_session.get.assert_called_once_with(f"{BASE_URL}/plans/get/{PLAN_ID}")
|
|
|
|
def test_calls_raise_for_status(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
|
|
client.get_plan_xml(PLAN_ID)
|
|
mock_session.get.return_value.raise_for_status.assert_called_once()
|
|
|
|
def test_returns_magic_plan_detail(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
|
|
result = client.get_plan_xml(PLAN_ID)
|
|
assert isinstance(result, MagicPlanXMLDetail)
|
|
|
|
def test_detail_contains_summary(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
|
|
result = client.get_plan_xml(PLAN_ID)
|
|
assert isinstance(result.summary, MagicPlanXMLSummary)
|
|
|
|
def test_detail_summary_id(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
|
|
result = client.get_plan_xml(PLAN_ID)
|
|
assert result.summary.id == PLAN_ID
|
|
|
|
def test_detail_summary_project_id(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
|
|
result = client.get_plan_xml(PLAN_ID)
|
|
assert result.summary.project_id == PROJECT_ID
|
|
|
|
def test_detail_contains_parsed_plan(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
|
|
result = client.get_plan_xml(PLAN_ID)
|
|
assert isinstance(result.plan_xml, MagicPlanXMLPlan)
|
|
|
|
def test_plan_xml_id_matches_xml(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
|
|
result = client.get_plan_xml(PLAN_ID)
|
|
assert result.plan_xml.id == PLAN_ID
|
|
|
|
def test_plan_xml_floor_count(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
|
|
result = client.get_plan_xml(PLAN_ID)
|
|
assert len(result.plan_xml.floors) == 2
|
|
|
|
def test_raises_on_http_error(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.raise_for_status.side_effect = requests.HTTPError("404")
|
|
with pytest.raises(requests.HTTPError):
|
|
client.get_plan_xml(PLAN_ID)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_plans
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestGetPlans:
|
|
|
|
def test_calls_correct_url(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plans_response([])
|
|
client.get_plans(PROJECT_ID)
|
|
mock_session.get.assert_called_once_with(
|
|
f"{BASE_URL}/plans", params={"project_id": PROJECT_ID}
|
|
)
|
|
|
|
def test_calls_raise_for_status(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plans_response([])
|
|
client.get_plans(PROJECT_ID)
|
|
mock_session.get.return_value.raise_for_status.assert_called_once()
|
|
|
|
def test_returns_list_of_summaries(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plans_response([PLAN_ID, "other-id"])
|
|
result = client.get_plans(PROJECT_ID)
|
|
assert len(result) == 2
|
|
assert all(isinstance(s, MagicPlanXMLSummary) for s in result)
|
|
|
|
def test_summary_id(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plans_response([PLAN_ID])
|
|
result = client.get_plans(PROJECT_ID)
|
|
assert result[0].id == PLAN_ID
|
|
|
|
def test_summary_project_id(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plans_response([PLAN_ID])
|
|
result = client.get_plans(PROJECT_ID)
|
|
assert result[0].project_id == PROJECT_ID
|
|
|
|
def test_summary_name(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plans_response([PLAN_ID])
|
|
result = client.get_plans(PROJECT_ID)
|
|
assert result[0].name == "Plan 0"
|
|
|
|
def test_returns_empty_list_when_no_plans(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.json.return_value = _get_plans_response([])
|
|
result = client.get_plans(PROJECT_ID)
|
|
assert result == []
|
|
|
|
def test_raises_on_http_error(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
|
|
mock_session.get.return_value.raise_for_status.side_effect = requests.HTTPError("401")
|
|
with pytest.raises(requests.HTTPError):
|
|
client.get_plans(PROJECT_ID)
|