mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-03 22:04:57 +08:00
fix(transfer): 跳过整理的文件不再记录为入库成功 (#7283)
* fix(transfer): avoid successful history for skipped files * test(transfer): fix regression test import order
This commit is contained in:
@@ -1843,12 +1843,16 @@ class TransHandler:
|
||||
|
||||
if checkpoint.skip_reason:
|
||||
logger.info(f"文件 {fileitem.path} 跳过整理:{checkpoint.skip_reason}")
|
||||
# 没有执行文件转移,不能生成成功入库历史或触发成功后的清理。
|
||||
return TransferInfo(
|
||||
success=True,
|
||||
success=False,
|
||||
message=checkpoint.skip_reason,
|
||||
fileitem=fileitem,
|
||||
fail_list=[fileitem.path] if fileitem.path else [],
|
||||
transfer_type=transfer_type,
|
||||
need_notify=False,
|
||||
failure_stage="planning",
|
||||
recovery_action="检查源目录;特典请核对季集编号并补充文件名中的 SxxExx 标记后重新整理",
|
||||
)
|
||||
|
||||
if checkpoint.preview:
|
||||
|
||||
@@ -660,7 +660,11 @@ def test_empty_directory_plan_is_zero_operation_without_intercept_or_cleanup(
|
||||
cleanup_media_file=lambda _fileitem: calls.append("cleanup") or True,
|
||||
)
|
||||
|
||||
assert result.success is True
|
||||
assert result.success is False
|
||||
assert result.message == "源目录中没有可整理文件"
|
||||
assert result.failure_stage == "planning"
|
||||
assert result.target_item is None
|
||||
assert result.need_notify is False
|
||||
assert calls == []
|
||||
|
||||
|
||||
|
||||
@@ -3,17 +3,21 @@
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from app.application.transfer.execution import TransferSettlementResult
|
||||
from app.application.transfer.workflow import TransferPlanningInput
|
||||
from app.chain.transfer.execution import _DurableTransferStepRunner
|
||||
from app.domain.context import MediaInfo
|
||||
from app.domain.meta.metavideo import MetaVideo
|
||||
from app.modules.filemanager.transhandler import TransHandler
|
||||
from app.schemas.file import FileItem
|
||||
from app.schemas.transfer import TransferInfo
|
||||
from app.schemas.types import MediaType
|
||||
from tests.test_transfer_job_manager import FakeMedia, make_task, make_transfer_chain
|
||||
|
||||
|
||||
def _transfer_without_episode(file_name: str) -> tuple[TransferInfo, MagicMock, MagicMock]:
|
||||
@@ -92,15 +96,25 @@ def _transfer_without_episode(file_name: str) -> tuple[TransferInfo, MagicMock,
|
||||
"Test.Show.NCOP.mkv",
|
||||
"Test.Show.Menu.mkv",
|
||||
"Test.Show.Featurette.mkv",
|
||||
*[
|
||||
f"[DMG&KNA&LKSUB&LoliHouse] Re Zero kara Hajimeru Isekai Seikatsu - SP{episode} "
|
||||
"[WebRip 1080p HEVC-10bit AAC ASSx2].mkv"
|
||||
for episode in range(26, 39)
|
||||
],
|
||||
],
|
||||
)
|
||||
def test_tv_special_extra_without_episode_skips_transfer(file_name: str) -> None:
|
||||
"""
|
||||
未识别出集数的已知特典文件应静默跳过并返回成功
|
||||
未识别出集数的特典应保留跳过原因并返回未入库,且不执行存储操作。
|
||||
"""
|
||||
result, source_oper, target_oper = _transfer_without_episode(file_name)
|
||||
|
||||
assert result.success is True
|
||||
assert result.success is False
|
||||
assert result.message == "未识别到文件集数,识别为特典/附加视频文件"
|
||||
assert result.failure_stage == "planning"
|
||||
assert "SxxExx" in result.recovery_action
|
||||
assert result.target_item is None
|
||||
assert result.fail_list == [f"/downloads/{file_name}"]
|
||||
assert result.need_notify is False
|
||||
assert result.fileitem.path == f"/downloads/{file_name}"
|
||||
assert source_oper.mock_calls == []
|
||||
@@ -119,3 +133,72 @@ def test_regular_tv_file_without_episode_remains_failure() -> None:
|
||||
assert result.fail_list == ["/downloads/Test.Show.Bonus.mkv"]
|
||||
assert source_oper.mock_calls == []
|
||||
assert target_oper.mock_calls == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("legacy", [False, True])
|
||||
def test_skipped_extra_settles_as_unsuccessful_history(monkeypatch, legacy: bool) -> None:
|
||||
"""
|
||||
特典的零操作执行结果经普通及兼容结算均写未入库历史,且不触发成功副作用。
|
||||
"""
|
||||
result, _, _ = _transfer_without_episode("Test.Show.SP33.mkv")
|
||||
result.transfer_type = "move"
|
||||
task = make_task(1)
|
||||
task.fileitem = result.fileitem
|
||||
task.meta = MetaVideo("Test.Show.SP33.mkv")
|
||||
task.mediainfo = FakeMedia()
|
||||
task.bind_admission_task_id("special-extra")
|
||||
task.bind_execution_lease(owner_id="worker", lease_token="lease")
|
||||
runner = object.__new__(_DurableTransferStepRunner)
|
||||
runner._task_id = "special-extra"
|
||||
runner._lease_token = "lease"
|
||||
runner._operation_ids = []
|
||||
runner._command = MagicMock()
|
||||
runner._command.checkpoint.side_effect = lambda **kwargs: SimpleNamespace(
|
||||
checkpoint=kwargs["checkpoint"]
|
||||
)
|
||||
checkpoint = runner.checkpoint(result)
|
||||
assert checkpoint.payload["outcome"] == "failed"
|
||||
assert checkpoint.operation_ids == ()
|
||||
assert checkpoint.skip_reason == result.message
|
||||
task.bind_execution_checkpoint(checkpoint)
|
||||
|
||||
chain = make_transfer_chain()
|
||||
chain.jobview.add_task(task)
|
||||
chain.transfer_history_repository.get_by_src.return_value = None
|
||||
chain.queue_failed_transfer_notification = MagicMock()
|
||||
chain._schedule_automatic_ai_takeover = MagicMock()
|
||||
chain._TransferChain__forget_owned_lease = MagicMock()
|
||||
chain._record_scrape_target = MagicMock()
|
||||
chain.notify_successful_transfer = MagicMock()
|
||||
chain._cleanup_successful_transfer = MagicMock()
|
||||
chain.eventmanager = MagicMock()
|
||||
staging = MagicMock()
|
||||
staging.replace.return_value = SimpleNamespace(id=1)
|
||||
|
||||
def settle(**kwargs):
|
||||
"""
|
||||
执行真实历史字段投影,只替换数据库及事件写入边界。
|
||||
"""
|
||||
assert kwargs["settlement"].outcome == "failed"
|
||||
kwargs["stage_history"](staging)
|
||||
return TransferSettlementResult(history_id=1, settlement_revision=1, pending_deleted=True)
|
||||
|
||||
chain.durable_event_writer.transfer_result.side_effect = settle
|
||||
monkeypatch.setattr("app.chain.transfer.settlement.record_transfer_failure", lambda *args, **kwargs: None)
|
||||
if legacy:
|
||||
chain._TransferChain__settle_legacy_transfer_result(task, result)
|
||||
else:
|
||||
state, message = chain._TransferChain__default_callback(task, result)
|
||||
assert state is False
|
||||
assert message == result.message
|
||||
history = staging.replace.call_args.args[0]
|
||||
assert history.status is False
|
||||
assert history.errmsg == result.message
|
||||
assert history.failure_stage == "planning"
|
||||
assert history.recovery_action == result.recovery_action
|
||||
assert history.dest is None
|
||||
assert task.terminal_settled
|
||||
chain.eventmanager.send_event.assert_not_called()
|
||||
chain._record_scrape_target.assert_not_called()
|
||||
chain.notify_successful_transfer.assert_not_called()
|
||||
chain._cleanup_successful_transfer.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user