Files
ecm_sync_system/tests/unit/test_material_detail_api_handler.py
T
2026-04-02 11:14:44 +08:00

130 lines
3.8 KiB
Python

from __future__ import annotations
from typing import Any
import pytest
from pydantic import BaseModel
from sync_state_machine.common.sync_node import SyncNode
from sync_state_machine.domain.material_detail.api_handler import MaterialDetailApiHandler
from sync_state_machine.datasource.task_result import TaskStatus
class _MaterialDetailSchema(BaseModel):
id: str
project_id: str | None = None
parent_id: str
date: str | None = None
quantity: float
remark: str | None = None
is_complete: bool | None = None
class _MaterialDetailNode(SyncNode[_MaterialDetailSchema]):
node_type = "material_detail"
schema = _MaterialDetailSchema
class _MockApiClient:
def __init__(self) -> None:
self.requests: list[dict[str, Any]] = []
async def request(self, method: str, endpoint: str, **kwargs: Any) -> dict[str, Any]:
self.requests.append({"method": method, "endpoint": endpoint, **kwargs})
return {"code": 200, "message": "OK", "data": {"biz_id": kwargs.get("data", {}).get("biz_id")}}
class _FailingApiClient(_MockApiClient):
async def request(self, method: str, endpoint: str, **kwargs: Any) -> dict[str, Any]:
self.requests.append({"method": method, "endpoint": endpoint, **kwargs})
raise Exception("API error: failed request")
@pytest.mark.asyncio
async def test_material_detail_update_returns_in_progress_with_shared_push_id() -> None:
handler = MaterialDetailApiHandler()
handler.api_client = _MockApiClient()
node = _MaterialDetailNode(
node_id="detail-node-1",
data_id="detail-1",
data={
"id": "detail-1",
"parent_id": "material-1",
"quantity": 10.0,
"remark": "new",
},
origin_data={
"id": "detail-1",
"parent_id": "material-1",
"quantity": 5.0,
"remark": None,
},
context={"material_id": "material-1"},
)
results = await handler.update_all([node])
assert len(results) == 1
assert results[0].status == TaskStatus.IN_PROGRESS
assert results[0].task_id
assert handler.api_client.requests[0]["endpoint"] == "/material/detail/update"
@pytest.mark.asyncio
async def test_material_detail_create_failure_preserves_push_id_for_trace_lookup() -> None:
handler = MaterialDetailApiHandler()
handler.api_client = _FailingApiClient()
node = _MaterialDetailNode(
node_id="detail-node-create-1",
data_id="",
data={
"id": "runtime-detail-1",
"project_id": "project-1",
"parent_id": "material-1",
"date": "2025-03-01",
"quantity": 4.0,
"remark": None,
"is_complete": False,
},
)
results = await handler.create_all([node])
assert len(results) == 1
assert results[0].status == TaskStatus.FAILED
assert results[0].push_id
assert handler.api_client.requests[0]["endpoint"] == "/material/detail/create"
@pytest.mark.asyncio
async def test_material_detail_update_failure_preserves_push_id_for_trace_lookup() -> None:
handler = MaterialDetailApiHandler()
handler.api_client = _FailingApiClient()
node = _MaterialDetailNode(
node_id="detail-node-update-fail-1",
data_id="detail-1",
data={
"id": "detail-1",
"parent_id": "material-1",
"quantity": 10.0,
"remark": "new",
},
origin_data={
"id": "detail-1",
"parent_id": "material-1",
"quantity": 5.0,
"remark": None,
},
context={"material_id": "material-1"},
)
results = await handler.update_all([node])
assert len(results) == 1
assert results[0].status == TaskStatus.FAILED
assert results[0].push_id
assert handler.api_client.requests[0]["endpoint"] == "/material/detail/update"