解决了部分detail load效率问题和load失败终止pipeline问题

This commit is contained in:
strepsiades
2026-04-02 14:35:28 +08:00
parent bb8ea931b5
commit 74df53889c
14 changed files with 626 additions and 64 deletions
@@ -5,8 +5,10 @@ from typing import Any
import pytest
from pydantic import BaseModel
from sync_state_machine.common.collection import DataCollection
from sync_state_machine.common.sync_node import SyncNode
from sync_state_machine.domain.material_detail.api_handler import MaterialDetailApiHandler
from sync_state_machine.domain.project.api_handler import ProjectApiHandler
from sync_state_machine.datasource.task_result import TaskStatus
@@ -25,6 +27,15 @@ class _MaterialDetailNode(SyncNode[_MaterialDetailSchema]):
schema = _MaterialDetailSchema
class _ProjectSchema(BaseModel):
id: str
class _ProjectNode(SyncNode[_ProjectSchema]):
node_type = "project"
schema = _ProjectSchema
class _MockApiClient:
def __init__(self) -> None:
self.requests: list[dict[str, Any]] = []
@@ -127,3 +138,97 @@ async def test_material_detail_update_failure_preserves_push_id_for_trace_lookup
assert results[0].status == TaskStatus.FAILED
assert results[0].push_id
assert handler.api_client.requests[0]["endpoint"] == "/material/detail/update"
@pytest.mark.asyncio
async def test_material_detail_load_uses_project_aggregate_when_enabled(monkeypatch: pytest.MonkeyPatch) -> None:
collection = DataCollection("remote")
project = _ProjectNode(
node_id="project-node-1",
data_id="project-1",
data={"id": "project-1"},
)
await collection.add(project)
handler = MaterialDetailApiHandler()
handler.set_collection(collection)
handler.set_handler_config({"load_method": "aggregate"})
handler.api_client = _MockApiClient()
async def _fake_get_aggregate(client, project_id: str, *, domains: list[str], limit: int = 10000):
assert project_id == "project-1"
assert domains == ["material_details"]
assert limit == 10000
return {
"meta": {
"project_id": project_id,
"requested_domains": domains,
"domain_limit": limit,
"truncated_domains": [],
},
"data": {
"material_details": [
{
"id": "detail-1",
"project_id": project_id,
"parent_id": "material-1",
"date": "2025-03-01",
"quantity": 2.0,
"remark": None,
"is_complete": False,
}
],
"construction_details": [],
"contract_settlement_details": [],
"power_details": [],
},
}
monkeypatch.setattr(ProjectApiHandler, "get_aggregate", _fake_get_aggregate)
nodes = await handler.load()
assert len(nodes) == 1
assert nodes[0].data_id == "detail-1"
detail_data = nodes[0].get_data()
assert detail_data is not None
assert detail_data["project_id"] == "project-1"
assert handler.api_client.requests == []
@pytest.mark.asyncio
async def test_material_detail_load_aggregate_raises_on_truncated_domain(monkeypatch: pytest.MonkeyPatch) -> None:
collection = DataCollection("remote")
project = _ProjectNode(
node_id="project-node-1",
data_id="project-1",
data={"id": "project-1"},
)
await collection.add(project)
handler = MaterialDetailApiHandler()
handler.set_collection(collection)
handler.set_handler_config({"load_method": "aggregate"})
handler.api_client = _MockApiClient()
async def _fake_get_aggregate(client, project_id: str, *, domains: list[str], limit: int = 10000):
assert domains == ["material_details"]
return {
"meta": {
"project_id": project_id,
"requested_domains": domains,
"domain_limit": limit,
"truncated_domains": ["material_details"],
},
"data": {
"material_details": [],
"construction_details": [],
"contract_settlement_details": [],
"power_details": [],
},
}
monkeypatch.setattr(ProjectApiHandler, "get_aggregate", _fake_get_aggregate)
with pytest.raises(RuntimeError, match="truncated domain material_details"):
await handler.load()