refactor(transfer): complete durable recovery state machine

This commit is contained in:
jxxghp
2026-08-28 00:03:36 +08:00
parent 513d867dd6
commit e9de149dbf
48 changed files with 5421 additions and 1140 deletions
+39 -8
View File
@@ -15,11 +15,12 @@ from app.application.transfer.execution import (
TransferExecutionCheckpoint,
TransferSettlementResult,
)
from app.chain.transfer import TransferChain
from app.chain.transfer import TransferChain, _DurableTransferStepRunner
from app.schemas.transfer import TransferInfo
from app.schemas.types import EventType
from tests.test_transfer_job_manager import (
FakeMedia,
bind_terminal_checkpoint,
make_fileitem,
make_task,
make_transfer_chain,
@@ -130,16 +131,19 @@ def test_overwrite_declined_uses_successful_durable_settlement():
task = make_task(1)
task.bind_admission_task_id("task-overwrite-declined")
task.bind_execution_lease(owner_id="worker", lease_token="lease")
task.bind_execution_checkpoint(TransferExecutionCheckpoint.create(
payload={"outcome": "overwrite_skipped"},
operation_ids=(),
skip_reason="overwrite_declined",
))
transferinfo = TransferInfo(
success=False,
overwrite_skipped=True,
message="目标已存在,按覆盖策略跳过覆盖",
)
task.bind_execution_checkpoint(TransferExecutionCheckpoint.create(
payload={
"outcome": "overwrite_skipped",
"transferinfo": transferinfo.model_dump(mode="json"),
},
operation_ids=(),
skip_reason="overwrite_declined",
))
settlement = TransferChain._TransferChain__build_transfer_result_settlement(
task,
@@ -152,6 +156,27 @@ def test_overwrite_declined_uses_successful_durable_settlement():
assert settlement.error is None
def test_durable_step_runner_records_overwrite_skip_as_explicit_outcome():
"""生产 runner 必须冻结覆盖跳过事实,不能提前把它记成普通失败。"""
runner = object.__new__(_DurableTransferStepRunner)
runner._task_id = "task-overwrite-skipped"
runner._lease_token = "lease"
runner._operation_ids = []
runner._command = MagicMock()
runner._command.checkpoint.side_effect = lambda **kwargs: SimpleNamespace(
checkpoint=kwargs["checkpoint"]
)
transferinfo = TransferInfo(
success=False,
overwrite_skipped=True,
message="目标已存在,按覆盖策略跳过覆盖",
)
checkpoint = runner.checkpoint(transferinfo)
assert checkpoint.payload["outcome"] == "overwrite_skipped"
def test_overwrite_skip_without_success_history_uses_failed_settlement():
"""未核实既有成功历史时,覆盖跳过标志不能伪造成功终态。"""
task = make_task(1)
@@ -216,6 +241,7 @@ def test_default_callback_skips_history_and_notification_when_overwrite_declined
overwrite_skipped=True,
need_notify=False,
)
bind_terminal_checkpoint(task, transferinfo)
with patch(
"app.chain.transfer.get_chain_transfer_history_port",
@@ -262,6 +288,7 @@ def test_default_callback_keeps_original_failure_semantics_without_success_histo
overwrite_skipped=True,
need_notify=False,
)
bind_terminal_checkpoint(task, transferinfo)
with patch(
"app.chain.transfer.get_chain_transfer_history_port",
@@ -312,7 +339,6 @@ def test_durable_callback_settles_overwrite_skip_without_history_as_failed():
overwrite_skipped=True,
need_notify=False,
)
def durable_transfer_result(**kwargs):
"""执行失败历史暂存并返回 task-aware 结算回执。"""
history = kwargs["stage_history"](SimpleNamespace())
@@ -363,6 +389,7 @@ def test_default_callback_delegates_primary_failure_to_durable_writer():
transfer_type="copy",
need_notify=False,
)
bind_terminal_checkpoint(task, transferinfo)
def durable_transfer_result(**kwargs):
"""执行 writer 收到的历史暂存与提交后发布回调。"""
@@ -371,7 +398,11 @@ def test_default_callback_delegates_primary_failure_to_durable_writer():
payload["transfer_history_id"] = history.id
payload["idempotency_key"] = f"transfer.failed:{history.id}:v1"
kwargs["publish"](payload)
return history
return TransferSettlementResult(
history_id=history.id,
settlement_revision=1,
pending_deleted=True,
)
chain.durable_event_writer.transfer_result.side_effect = durable_transfer_result
with patch(