整体同步代码

This commit is contained in:
strepsiades
2026-03-26 14:10:47 +08:00
parent 4b81b83fd8
commit f50dc1aed0
9 changed files with 188 additions and 32 deletions
@@ -8,6 +8,7 @@ import pytest
from sync_state_machine.common.binding import BindingManager
from sync_state_machine.common.collection import DataCollection
from sync_state_machine.datasource.jsonl import JsonlDataSource
import sync_state_machine.pipeline.full_sync_pipeline as full_sync_pipeline_module
from sync_state_machine.pipeline.full_sync_pipeline import FullSyncPipeline
@@ -30,7 +31,12 @@ class _StubStrategy:
def __init__(self, node_type: str):
self.node_type = node_type
self.skip_sync = False
self.config = SimpleNamespace()
self.config = SimpleNamespace(
depend_fields={},
update_direction=None,
compare_ignore_fields=[],
post_check_ignore_fields=[],
)
self.sm_runtime = None
async def bind(self):
@@ -143,4 +149,48 @@ async def test_phase_sync_skips_reload_for_jsonl_datasources(
assert reload_calls == [
("project", "pre-strategy refresh"),
]
]
@pytest.mark.asyncio
async def test_evaluate_consistency_merges_compare_and_post_check_ignore_fields() -> None:
project_strategy = _StubStrategy("project")
project_strategy.config.compare_ignore_fields = ["updated_at", "code"]
project_strategy.config.post_check_ignore_fields = ["code", "audit_status"]
pipeline = FullSyncPipeline(
local_datasource=_FakeDataSource(),
remote_datasource=_FakeDataSource(),
strategies=[project_strategy],
persistence=_FakePersistence(),
local_collection=DataCollection("local"),
remote_collection=DataCollection("remote"),
binding_manager=BindingManager(_FakePersistence(), auto_persist=False),
node_types=["project"],
load_order=["project"],
sync_order=["project"],
enable_persistence=False,
)
captured: dict[str, object] = {}
async def fake_evaluate_consistency_for_node_type(**kwargs):
captured.update(kwargs)
return {
"ok": True,
"checked_pairs": 0,
"mismatch_count": 0,
"fields": [],
"schema_name": "project",
"ignore_fields": kwargs["ignore_fields"],
}
original = full_sync_pipeline_module.evaluate_consistency_for_node_type
full_sync_pipeline_module.evaluate_consistency_for_node_type = fake_evaluate_consistency_for_node_type
try:
result = await pipeline._evaluate_consistency_for_node_type("project")
finally:
full_sync_pipeline_module.evaluate_consistency_for_node_type = original
assert captured["ignore_fields"] == ["updated_at", "code", "audit_status"]
assert result["ignore_fields"] == ["updated_at", "code", "audit_status"]
@@ -0,0 +1,37 @@
from __future__ import annotations
from sync_state_machine.config.builder import _apply_strategy_overrides
from sync_state_machine.config.strategy_config import StrategyConfig, StrategyRuntimeConfig
def test_strategy_runtime_config_skip_sync_defaults_false() -> None:
runtime_config = StrategyRuntimeConfig(node_type="project")
assert runtime_config.skip_sync is False
def test_apply_strategy_overrides_allows_skip_sync_override() -> None:
resolved = _apply_strategy_overrides(
node_types=["project"],
strategy_overrides={
"project": {
"skip_sync": True,
}
},
)
assert len(resolved) == 1
assert resolved[0].node_type == "project"
assert resolved[0].skip_sync is True
def test_strategy_config_supports_both_post_check_ignore_fields() -> None:
config = StrategyConfig.model_validate(
{
"post_check_ignore_fields": ["code"],
"post_check_ignore_list_item_fields": {"plan_node": ["is_audit"]},
}
)
assert config.post_check_ignore_fields == ["code"]
assert config.post_check_ignore_list_item_fields == {"plan_node": ["is_audit"]}
+21
View File
@@ -1,5 +1,6 @@
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from sync_state_machine.domain.company.sync_strategy import CompanySyncStrategy
from sync_state_machine.sync_system.config import UpdateDirection
from sync_state_machine.sync_system.strategy_ops.update_ops import run_update
from sync_state_machine.domain.construction.sync_strategy import ConstructionTaskSyncStrategy
@@ -86,3 +87,23 @@ def test_construction_task_compare_payload_ignores_plan_node_is_audit():
}
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)