142 lines
4.6 KiB
Python
142 lines
4.6 KiB
Python
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.sqlite_backend import SQLitePersistenceBackend
|
|
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 = SQLitePersistenceBackend(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()
|