mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-30 03:48:03 +08:00
131 lines
5.2 KiB
Python
131 lines
5.2 KiB
Python
"""
|
||
整理目录未匹配时的自定义路径告警与自动匹配拒绝边界。
|
||
"""
|
||
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
from unittest.mock import Mock
|
||
|
||
import pytest
|
||
|
||
from app.application.transfer.execution import TransferPlanningRejectedError
|
||
from app.domain.context import MediaInfo
|
||
from app.domain.meta.metabase import MetaBase
|
||
from app.domain.metainfo import MetaInfo
|
||
from app.modules.filemanager.module import FileManagerModule
|
||
from app.schemas.category import ClassificationResult, ClassificationSelection
|
||
from app.schemas.file import FileItem
|
||
from app.schemas.system import TransferDirectoryConf
|
||
from app.schemas.types import MediaType
|
||
|
||
# 源文件、识别信息和可观察边界共同组成一次隔离规划上下文。
|
||
TransferContext = tuple[FileItem, MetaBase, MediaInfo, Mock, Mock]
|
||
|
||
|
||
@pytest.fixture
|
||
def transfer_context(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> TransferContext:
|
||
"""
|
||
提供隔离源文件与分类快照,记录告警并禁止规划产生存储写入。
|
||
"""
|
||
source = tmp_path / "downloads" / "Movie.2021.mkv"
|
||
source.parent.mkdir()
|
||
source.write_bytes(b"video")
|
||
fileitem = FileItem(
|
||
storage="local", path=source.as_posix(), type="file",
|
||
name=source.name, basename=source.stem, extension="mkv", size=5,
|
||
)
|
||
media = MediaInfo(
|
||
type=MediaType.MOVIE, title="乌海", year="2021",
|
||
classification=ClassificationResult(effective=ClassificationSelection(
|
||
category_path=["华语电影"], source="automatic",
|
||
)),
|
||
)
|
||
warning = Mock()
|
||
monkeypatch.setattr("app.modules.filemanager.module.logger.warning", warning)
|
||
return fileitem, MetaInfo(source.name), media, Mock(), warning
|
||
|
||
|
||
@pytest.mark.parametrize("target_storage", [None, "smb"])
|
||
@pytest.mark.parametrize("preview", [False, True])
|
||
@pytest.mark.parametrize(
|
||
("type_folder", "category_folder", "relative_path"),
|
||
[(True, True, "电影/华语电影"), (False, True, "华语电影"), (False, False, "")],
|
||
)
|
||
def test_unmatched_explicit_target_warns_and_preserves_requested_path(
|
||
transfer_context: TransferContext, tmp_path: Path, target_storage: Optional[str],
|
||
preview: bool, type_folder: bool, category_folder: bool, relative_path: str,
|
||
) -> None:
|
||
"""
|
||
未匹配配置时保留自定义路径合同,并在预览和实际规划中明确告警。
|
||
"""
|
||
fileitem, meta, media, source_oper, warning = transfer_context
|
||
explicit_root = tmp_path / "media"
|
||
expected_root = explicit_root / relative_path
|
||
module = FileManagerModule()
|
||
|
||
checkpoint = module.plan_transfer(
|
||
fileitem=fileitem, meta=meta, mediainfo=media,
|
||
target_storage=target_storage, target_path=explicit_root,
|
||
transfer_type="copy", library_type_folder=type_folder,
|
||
library_category_folder=category_folder, source_oper=source_oper,
|
||
preview=preview,
|
||
)
|
||
|
||
assert checkpoint.root_target_path == expected_root.as_posix()
|
||
assert Path(checkpoint.final_target_path).is_relative_to(expected_root)
|
||
assert checkpoint.target_storage == (target_storage or "local")
|
||
warning.assert_called_once()
|
||
message = warning.call_args.args[0]
|
||
assert "未匹配到媒体库目录配置" in message
|
||
assert f"【{target_storage or 'local'}】{explicit_root}" in message
|
||
assert f"计算后的目标路径:{expected_root}" in message
|
||
assert f"源路径:【local】{fileitem.path}" in message
|
||
assert "自动匹配" in message
|
||
assert source_oper.mock_calls == []
|
||
assert not explicit_root.exists()
|
||
|
||
|
||
def test_matched_directory_keeps_library_path_without_custom_target_warning(
|
||
transfer_context: TransferContext, tmp_path: Path,
|
||
) -> None:
|
||
"""
|
||
命中目录配置时应保留 links 层级,并且不能误报自定义路径告警。
|
||
"""
|
||
fileitem, meta, media, source_oper, warning = transfer_context
|
||
library_root = tmp_path / "media" / "links" / "电影"
|
||
directory = TransferDirectoryConf(
|
||
name="电影", library_path=library_root.as_posix(),
|
||
library_storage="local", transfer_type="copy", media_type="电影",
|
||
library_type_folder=False, library_category_folder=True,
|
||
)
|
||
|
||
checkpoint = FileManagerModule().plan_transfer(
|
||
fileitem=fileitem, meta=meta, mediainfo=media,
|
||
target_directory=directory, target_path=tmp_path / "media",
|
||
source_oper=source_oper,
|
||
)
|
||
|
||
assert checkpoint.root_target_path == (library_root / "华语电影").as_posix()
|
||
warning.assert_not_called()
|
||
assert source_oper.mock_calls == []
|
||
assert not library_root.exists()
|
||
|
||
|
||
def test_unmatched_automatic_target_rejects_without_storage_root_fallback(
|
||
transfer_context: TransferContext,
|
||
) -> None:
|
||
"""
|
||
没有显式目标路径时必须业务拒绝,不能自行生成存储根下的整理计划。
|
||
"""
|
||
fileitem, meta, media, source_oper, warning = transfer_context
|
||
|
||
with pytest.raises(TransferPlanningRejectedError, match="未找到有效的媒体库目录"):
|
||
FileManagerModule().plan_transfer(
|
||
fileitem=fileitem, meta=meta, mediainfo=media,
|
||
transfer_type="copy", library_type_folder=True,
|
||
library_category_folder=True, source_oper=source_oper,
|
||
)
|
||
|
||
warning.assert_not_called()
|
||
assert source_oper.mock_calls == []
|