135 lines
4.6 KiB
Python
135 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
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.engine.dispatcher import StateMachineRuntime
|
|
from sync_state_machine.engine.model import StateMachineConfig
|
|
from tests.mocks.mock_datasource import (
|
|
HandlerScript,
|
|
MockDataSource,
|
|
MockSyncNode,
|
|
ScriptedMockHandler,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def datasource_bundle():
|
|
db = Path(tempfile.mkdtemp(prefix="sync_state_machine_mock_ds_")) / "state.db"
|
|
persistence = PersistenceBackend(str(db))
|
|
cfg_path = Path(__file__).resolve().parents[2] / "sync_state_machine" / "config" / "node_state_machine.yaml"
|
|
runtime = StateMachineRuntime(StateMachineConfig.load(cfg_path))
|
|
collection = DataCollection("mock", persistence=None, auto_persist=False)
|
|
collection.set_state_machine_runtime(runtime)
|
|
datasource = MockDataSource(poll_max_retries=2, poll_interval=0)
|
|
datasource.set_state_machine_runtime(runtime)
|
|
return datasource, collection
|
|
|
|
|
|
def _new_node(node_id: str, action: SyncAction) -> MockSyncNode:
|
|
data_id = "" if action == SyncAction.CREATE else "mock-data-id"
|
|
return MockSyncNode(
|
|
node_id=node_id,
|
|
data_id=data_id,
|
|
data={"id": node_id, "name": "mock"},
|
|
action=action,
|
|
status=SyncStatus.PENDING,
|
|
binding_status=BindingStatus.NORMAL,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_success_to_s11(datasource_bundle):
|
|
datasource, collection = datasource_bundle
|
|
node = _new_node("n1", SyncAction.CREATE)
|
|
await collection.add(node)
|
|
|
|
datasource.register_handler(ScriptedMockHandler(HandlerScript(create="success")))
|
|
datasource.set_collection(collection)
|
|
await datasource.sync_all(data_type="mock")
|
|
|
|
assert node.status == SyncStatus.SUCCESS
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_skipped_is_rejected(datasource_bundle):
|
|
datasource, collection = datasource_bundle
|
|
node = _new_node("n2", SyncAction.CREATE)
|
|
await collection.add(node)
|
|
|
|
datasource.register_handler(ScriptedMockHandler(HandlerScript(create="skipped")))
|
|
datasource.set_collection(collection)
|
|
await datasource.sync_all(data_type="mock")
|
|
|
|
assert node.status == SyncStatus.FAILED
|
|
assert node.action == SyncAction.CREATE
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_skip_downgrade_to_s14(datasource_bundle):
|
|
datasource, collection = datasource_bundle
|
|
node = _new_node("n3", SyncAction.UPDATE)
|
|
await collection.add(node)
|
|
|
|
datasource.register_handler(
|
|
ScriptedMockHandler(HandlerScript(update="skipped"))
|
|
)
|
|
datasource.set_collection(collection)
|
|
await datasource.sync_all(data_type="mock")
|
|
|
|
assert node.status == SyncStatus.SKIPPED
|
|
assert node.action == SyncAction.NONE
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_success_writes_back_origin_data(datasource_bundle):
|
|
datasource, collection = datasource_bundle
|
|
node = _new_node("n3b", SyncAction.UPDATE)
|
|
node.set_origin_data({"id": "n3b", "name": "before"})
|
|
node.set_data({"id": "n3b", "name": "after"})
|
|
await collection.add(node)
|
|
|
|
datasource.register_handler(ScriptedMockHandler(HandlerScript(update="success")))
|
|
datasource.set_collection(collection)
|
|
await datasource.sync_all(data_type="mock")
|
|
|
|
assert node.status == SyncStatus.SUCCESS
|
|
assert node.action == SyncAction.UPDATE
|
|
assert node.get_origin_data() == node.get_data()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_in_progress_poll_success_to_s11(datasource_bundle):
|
|
datasource, collection = datasource_bundle
|
|
node = _new_node("n4", SyncAction.CREATE)
|
|
await collection.add(node)
|
|
|
|
datasource.register_handler(
|
|
ScriptedMockHandler(HandlerScript(create="in_progress", poll_sequence=["success"]))
|
|
)
|
|
datasource.set_collection(collection)
|
|
await datasource.sync_all(data_type="mock", poll_async_tasks=True)
|
|
|
|
assert node.status == SyncStatus.SUCCESS
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_in_progress_poll_timeout_to_s12(datasource_bundle):
|
|
datasource, collection = datasource_bundle
|
|
node = _new_node("n5", SyncAction.CREATE)
|
|
await collection.add(node)
|
|
|
|
datasource.register_handler(
|
|
ScriptedMockHandler(HandlerScript(create="in_progress", poll_sequence=["in_progress", "in_progress"]))
|
|
)
|
|
datasource.set_collection(collection)
|
|
await datasource.sync_all(data_type="mock", poll_async_tasks=True)
|
|
|
|
assert node.status == SyncStatus.FAILED
|
|
assert "timeout" in (node.error or "").lower()
|