feat: complete transfer and eviction execution
This commit is contained in:
+61
-3
@@ -6,7 +6,11 @@ from unittest.mock import Mock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
from archive_clients.bencode import encode
|
||||
from archive_clients.jobs import ClientJobExecutor, JobExecutionError
|
||||
from archive_clients.jobs import (
|
||||
ClientJobExecutor,
|
||||
JobExecutionError,
|
||||
_resource_fingerprint,
|
||||
)
|
||||
from archive_clients.resources import normalize_resource
|
||||
from archive_clients.state import ClientStore
|
||||
from archive_control.v1 import control_pb2, job_pb2
|
||||
@@ -90,6 +94,9 @@ class ClientJobHappyPathTests(unittest.TestCase):
|
||||
definition.created_at.GetCurrentTime()
|
||||
definition.transfer.requested_files.ranges.add(first=0, last=0)
|
||||
definition.transfer.transfer_delta_files.ranges.add(first=0, last=0)
|
||||
definition.transfer.source_fingerprint.CopyFrom(
|
||||
_resource_fingerprint(resource, source_id)
|
||||
)
|
||||
|
||||
source_store = ClientStore(root / "source.db")
|
||||
target_store = ClientStore(root / "target.db")
|
||||
@@ -98,7 +105,7 @@ class ClientJobHappyPathTests(unittest.TestCase):
|
||||
source_qb = Mock()
|
||||
source_qb.get_resource.return_value = resource
|
||||
target_qb = Mock()
|
||||
target_qb.get_resource.side_effect = [None, resource]
|
||||
target_qb.get_resource.side_effect = [None, None, resource]
|
||||
syncthing = CompleteSyncthing()
|
||||
source = ClientJobExecutor(
|
||||
client_id=source_id,
|
||||
@@ -156,7 +163,10 @@ class ClientJobHappyPathTests(unittest.TestCase):
|
||||
))
|
||||
self.assertEqual(
|
||||
[event.sequence for event in events],
|
||||
[cursor_sequence + 1, cursor_sequence + 2],
|
||||
list(range(
|
||||
cursor_sequence + 1,
|
||||
cursor_sequence + len(events) + 1,
|
||||
)),
|
||||
)
|
||||
cursor_sequence = events[-1].sequence
|
||||
cursor_revision = events[-1].job_revision
|
||||
@@ -250,6 +260,54 @@ class ClientJobHappyPathTests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(store.list_active_job_cursors(), [])
|
||||
|
||||
def test_active_source_cancellation_reports_durable_rollback(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
store = ClientStore(root / "client.db")
|
||||
store.initialize()
|
||||
definition = job_pb2.JobDefinition(
|
||||
job_id=str(uuid4()),
|
||||
idempotency_key=str(uuid4()),
|
||||
operation=job_pb2.JOB_OPERATION_ARCHIVE,
|
||||
resource_display_name="fixture",
|
||||
transfer={
|
||||
"source_client_id": "cache-1",
|
||||
"target_client_id": "archive-1",
|
||||
"route_id": "route-1",
|
||||
},
|
||||
)
|
||||
definition.resource_id.info_hash_v1_hex = "a" * 40
|
||||
definition.created_at.GetCurrentTime()
|
||||
executor = ClientJobExecutor(
|
||||
client_id="cache-1",
|
||||
qbittorrent=Mock(),
|
||||
store=store,
|
||||
qb_root=root,
|
||||
qb_api_root=Path("/downloads"),
|
||||
route_path=lambda _: root / "route",
|
||||
syncthing_transport=Mock(),
|
||||
sparse_supported=True,
|
||||
poll_interval=0,
|
||||
)
|
||||
executor.assign(control_pb2.AssignJobCommand(
|
||||
job=definition,
|
||||
expected_job_revision=1,
|
||||
expected_last_event_sequence=0,
|
||||
))
|
||||
events = executor.cancel(control_pb2.CancelJobCommand(
|
||||
job_id=definition.job_id,
|
||||
expected_job_revision=2,
|
||||
expected_last_event_sequence=1,
|
||||
reason="test",
|
||||
))
|
||||
self.assertEqual(
|
||||
events[-1].type,
|
||||
control_pb2.JOB_EVENT_TYPE_ROLLBACK_SUCCEEDED,
|
||||
)
|
||||
self.assertEqual(
|
||||
events[-1].state, job_pb2.JOB_STATE_ROLLING_BACK
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user