158 lines
5.0 KiB
Python
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}]
|