first commit

This commit is contained in:
strepsiades
2026-03-09 16:31:42 +08:00
commit 4a9c444b10
486 changed files with 52479 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
from __future__ import annotations
from pathlib import Path
import pytest
from sync_state_machine.common.binding import BindingManager
from sync_state_machine.common.collection import DataCollection
from sync_state_machine.common.persistence import PersistenceBackend
from sync_state_machine.common.types import BindingStatus, SyncAction, SyncStatus
from sync_state_machine.datasource.datasource import BaseDataSource
from sync_state_machine.engine.state_apply import apply_state_to_node
from sync_state_machine.pipeline.full_sync_pipeline import FullSyncPipeline
from tests.mocks.mock_datasource import MockSyncNode
class _DS(BaseDataSource):
pass
def _set_state(pipeline: FullSyncPipeline, node: MockSyncNode, state_id: str, *, data_id: str) -> None:
apply_state_to_node(
pipeline.sm_runtime,
node,
state_id=state_id,
reason="test setup",
fields={"binding_status", "action", "status"},
)
apply_state_to_node(
pipeline.sm_runtime,
node,
state_id=state_id,
reason="test setup",
fields={"data_id"},
data_id=data_id,
)
async def _new_pipeline(tmp_path: Path) -> tuple[FullSyncPipeline, DataCollection, DataCollection, BindingManager, PersistenceBackend]:
db_path = tmp_path / "state.db"
persistence = PersistenceBackend(str(db_path))
await persistence.initialize()
local_collection = DataCollection("local", persistence=persistence, auto_persist=False)
remote_collection = DataCollection("remote", persistence=persistence, auto_persist=False)
binding_manager = BindingManager(persistence, auto_persist=False)
pipeline = FullSyncPipeline(
local_datasource=_DS(),
remote_datasource=_DS(),
strategies=[],
persistence=persistence,
local_collection=local_collection,
remote_collection=remote_collection,
binding_manager=binding_manager,
node_types=["mock"],
load_order=["mock"],
sync_order=["mock"],
enable_persistence=False,
)
return pipeline, local_collection, remote_collection, binding_manager, persistence
@pytest.mark.asyncio
async def test_peer_creating_source_moves_to_s01_when_target_create_success(tmp_path: Path) -> None:
pipeline, local, remote, bm, persistence = await _new_pipeline(tmp_path)
src = MockSyncNode(
node_id="src_local",
data_id="SRC-ID",
data={"id": "SRC-ID", "name": "src"},
action=SyncAction.NONE,
status=SyncStatus.PENDING,
binding_status=BindingStatus.UNCHECKED,
)
target = MockSyncNode(
node_id="tgt_remote",
data_id="TGT-ID",
data={"id": "TGT-ID", "name": "tgt"},
action=SyncAction.CREATE,
status=SyncStatus.SUCCESS,
binding_status=BindingStatus.NORMAL,
)
await local.add(src)
await remote.add(target)
_set_state(pipeline, src, "S13", data_id="SRC-ID")
_set_state(pipeline, target, "S11C", data_id="TGT-ID")
await bm.bind("mock", "src_local", "tgt_remote")
await pipeline._finalize_peer_creating_sources("mock")
assert src.binding_status == BindingStatus.NORMAL
assert src.action == SyncAction.NONE
assert src.status == SyncStatus.PENDING
await pipeline.close()
await persistence.close()
@pytest.mark.asyncio
async def test_peer_creating_source_stays_s13_when_target_create_failed(tmp_path: Path) -> None:
pipeline, local, remote, bm, persistence = await _new_pipeline(tmp_path)
src = MockSyncNode(
node_id="src_local_fail",
data_id="SRC-ID-FAIL",
data={"id": "SRC-ID-FAIL", "name": "src-fail"},
action=SyncAction.NONE,
status=SyncStatus.PENDING,
binding_status=BindingStatus.UNCHECKED,
)
target = MockSyncNode(
node_id="tgt_remote_fail",
data_id="",
data={"id": "", "name": "tgt-fail"},
action=SyncAction.CREATE,
status=SyncStatus.FAILED,
binding_status=BindingStatus.NORMAL,
)
await local.add(src)
await remote.add(target)
_set_state(pipeline, src, "S13", data_id="SRC-ID-FAIL")
_set_state(pipeline, target, "S12C", data_id="")
target.error = "remote create failed"
await bm.bind("mock", "src_local_fail", "tgt_remote_fail")
await pipeline._finalize_peer_creating_sources("mock")
assert src.binding_status == BindingStatus.PEER_CREATING
assert src.action == SyncAction.NONE
assert src.status == SyncStatus.PENDING
assert "peer_create_failed" in (src.error or "")
await pipeline.close()
await persistence.close()