Files
ecm_sync_system/tests/unit/test_update_ops.py
T
2026-04-01 11:52:57 +08:00

158 lines
5.0 KiB
Python

import pytest
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch, MagicMock
from sync_state_machine.domain.company.sync_strategy import CompanySyncStrategy
from sync_state_machine.sync_system.strategy_ops.update_ops import BoundNodePair, build_merged_update_payload, run_update
from sync_state_machine.domain.construction.sync_strategy import ConstructionTaskSyncStrategy
from sync_state_machine.domain.project_detail.sync_strategy import ProjectDetailSyncStrategy
from schemas.project_extensions.project_detail import ProjectDetailKey
@pytest.mark.asyncio
async def test_run_update_delegates_to_strategy_pair_planner():
local_node = SimpleNamespace(node_id="local-1")
remote_node = SimpleNamespace(node_id="remote-1")
strategy = MagicMock()
strategy.node_type = "TestNode"
strategy.local_collection = MagicMock()
strategy.remote_collection = MagicMock()
strategy.binding_manager = MagicMock()
strategy.reset_schema_diff_validator = MagicMock()
strategy.emit_schema_diff_report = MagicMock()
updated_node = SimpleNamespace(node_id="target-1")
strategy.update_pair = AsyncMock(return_value=[updated_node])
with patch(
'sync_state_machine.sync_system.strategy_ops.update_ops.build_data_id_normalization_map',
new=AsyncMock(return_value={"project": "remote-project"}),
), patch(
'sync_state_machine.sync_system.strategy_ops.update_ops.collect_bound_node_pairs',
new=AsyncMock(return_value=[BoundNodePair(local_node=local_node, remote_node=remote_node)]),
):
updated = await run_update(strategy)
assert updated == [updated_node]
strategy.update_pair.assert_awaited_once_with(
local_node,
remote_node,
{"project": "remote-project"},
)
def test_build_merged_update_payload_supports_include_and_exclude_fields():
source_data = {
"id": "remote-1",
"project_name": "new-name",
"special": "from-source",
}
target_data = {
"id": "remote-1",
"project_name": "old-name",
"special": "from-target",
}
include_only = build_merged_update_payload(
source_data=source_data,
target_data=target_data,
include_fields=["special"],
)
exclude_special = build_merged_update_payload(
source_data=source_data,
target_data=target_data,
exclude_fields=["special"],
)
assert include_only == {
"id": "remote-1",
"project_name": "old-name",
"special": "from-source",
}
assert exclude_special == {
"id": "remote-1",
"project_name": "new-name",
"special": "from-target",
}
def test_construction_task_compare_payload_ignores_plan_node_is_audit():
strategy = ConstructionTaskSyncStrategy(
"construction_task",
MagicMock(),
MagicMock(),
MagicMock(),
)
local_payload = {
"plan_node": [{"name": "A", "is_audit": True}],
"task_type": "demo",
}
remote_payload = {
"plan_node": [{"name": "A", "is_audit": False}],
"task_type": "demo",
}
assert strategy.normalize_compare_payload(local_payload) == strategy.normalize_compare_payload(remote_payload)
def test_default_strategy_exposes_normalize_compare_payload():
strategy = CompanySyncStrategy(
"company",
MagicMock(),
MagicMock(),
MagicMock(),
)
local_payload = {
"id": "remote-company-id",
"code": "COMP-001",
}
remote_payload = {
"id": "local-company-id",
"code": "COMP-001",
}
assert strategy.normalize_compare_payload(local_payload) == strategy.normalize_compare_payload(remote_payload)
def test_project_detail_compare_payload_ignores_response_only_year_and_is_audit_noise():
strategy = ProjectDetailSyncStrategy(
"project_detail_data",
MagicMock(),
MagicMock(),
MagicMock(),
)
local_payload = {
"project_id": "project-1",
"key": "actual_construction",
"value": [{"date": "2023-08-23", "capacity": 4.0, "is_audit": False, "year": None}],
}
remote_payload = {
"project_id": "project-1",
"key": "actual_construction",
"value": [{"date": "2023-08-23", "capacity": 4.0, "is_audit": True, "year": 2023}],
}
assert strategy.normalize_compare_payload(local_payload) == strategy.normalize_compare_payload(remote_payload)
def test_project_detail_compare_payload_normalizes_enum_key_values():
strategy = ProjectDetailSyncStrategy(
"project_detail_data",
MagicMock(),
MagicMock(),
MagicMock(),
)
remote_payload = {
"project_id": "project-1",
"key": ProjectDetailKey.PLAN_CONSTRUCTION,
"value": [{"date": "2023-06-28", "capacity": 4.0, "year": 2023, "is_audit": True}],
}
normalized = strategy.normalize_compare_payload(remote_payload)
assert normalized["key"] == "plan_construction"
assert normalized["value"] == [{"date": "2023-06-28", "capacity": 4.0}]