mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-01 12:56:34 +08:00
fix(workflow): accept RSS domain context when adding downloads (#6947)
This commit is contained in:
@@ -132,11 +132,14 @@ class AddDownloadAction(BaseAction):
|
||||
return context
|
||||
|
||||
@staticmethod
|
||||
def _to_domain_context(context: WorkflowTorrentContext) -> DownloadContext:
|
||||
"""把工作流传输模型转换为下载链使用的领域上下文和媒体类型。"""
|
||||
def _to_domain_context(context: WorkflowTorrentContext | DownloadContext) -> DownloadContext:
|
||||
"""复用 RSS 的领域上下文,或把工作流传输模型还原为下载链领域对象。"""
|
||||
if context.media_info is None or context.torrent_info is None:
|
||||
raise ValueError("工作流下载上下文缺少媒体或种子信息")
|
||||
|
||||
if isinstance(context, DownloadContext):
|
||||
return context
|
||||
|
||||
context_data: dict[str, Any] = context.model_dump()
|
||||
media_data = context_data.get("media_info")
|
||||
if not isinstance(media_data, dict):
|
||||
|
||||
117
tests/test_workflow_add_download.py
Normal file
117
tests/test_workflow_add_download.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""覆盖 RSS 领域上下文通过工作流动作桥接后提交下载的契约。"""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from app.domain.context import Context, MediaInfo, TorrentInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.schemas.types import MediaType
|
||||
from app.schemas.workflow import ActionContext
|
||||
from app.workflow.actions import add_download, fetch_rss, filter_torrents
|
||||
from app.workflow.actions.add_download import AddDownloadAction
|
||||
from app.workflow.actions.fetch_rss import FetchRssAction
|
||||
from app.workflow.actions.filter_torrents import FilterTorrentsAction
|
||||
|
||||
|
||||
@pytest.mark.parametrize("match_media", [False, True])
|
||||
@pytest.mark.parametrize("only_lack", [False, True])
|
||||
def test_rss_filter_add_download_accepts_domain_context(monkeypatch, match_media, only_lack):
|
||||
"""RSS 无论是否预先识别,都应经资源过滤及动作输入桥接正常提交下载。"""
|
||||
media = MediaInfo(title="Show", type=MediaType.TV, tmdb_id=123)
|
||||
rss_helper = Mock()
|
||||
rss_helper.parse.return_value = [{
|
||||
"title": "Show S01E03",
|
||||
"enclosure": "https://example.com/show.torrent",
|
||||
"link": "https://example.com/details/1",
|
||||
"size": 1024,
|
||||
}]
|
||||
media_chain = Mock()
|
||||
media_chain.recognize_by_meta.return_value = media
|
||||
download_chain = Mock()
|
||||
download_chain.media_exists.return_value = None
|
||||
download_chain.download_single.return_value = "hash-show"
|
||||
torrent_helper = Mock()
|
||||
torrent_helper.filter_torrent.return_value = True
|
||||
filter_chain = Mock()
|
||||
filter_chain.filter_torrents.return_value = True
|
||||
monkeypatch.setattr(fetch_rss, "RssHelper", lambda: rss_helper)
|
||||
monkeypatch.setattr(fetch_rss, "MediaChain", lambda: media_chain)
|
||||
monkeypatch.setattr(add_download, "MediaChain", lambda: media_chain)
|
||||
monkeypatch.setattr(add_download, "DownloadChain", lambda: download_chain)
|
||||
monkeypatch.setattr(filter_torrents, "TorrentHelper", lambda: torrent_helper)
|
||||
monkeypatch.setattr(filter_torrents, "ActionChain", lambda: filter_chain)
|
||||
monkeypatch.setattr(fetch_rss.runtime_stop_state, "is_workflow_stopped", lambda _wid: False)
|
||||
|
||||
rss_result = FetchRssAction("rss").execute_with_inputs(
|
||||
workflow_id=1,
|
||||
params={"url": "https://example.com/rss.xml", "match_media": match_media},
|
||||
inputs={},
|
||||
runtime={},
|
||||
context=ActionContext(),
|
||||
)
|
||||
source = rss_result.outputs["torrents"][0]
|
||||
assert isinstance(source, Context)
|
||||
assert source.media_info is (media if match_media else None)
|
||||
|
||||
filter_result = FilterTorrentsAction("filter").execute_with_inputs(
|
||||
workflow_id=1,
|
||||
params={},
|
||||
inputs=rss_result.outputs,
|
||||
runtime={},
|
||||
context=ActionContext(),
|
||||
)
|
||||
action = AddDownloadAction("download")
|
||||
saved_cache = []
|
||||
action.check_cache = lambda _wid, _key: False
|
||||
action.save_cache = lambda _wid, key: saved_cache.append(key)
|
||||
result = action.execute_with_inputs(
|
||||
workflow_id=1,
|
||||
params={"only_lack": only_lack, "downloader": "qb", "save_path": "/downloads", "labels": "rss"},
|
||||
inputs=filter_result.outputs,
|
||||
runtime={},
|
||||
context=ActionContext(),
|
||||
)
|
||||
|
||||
assert result.success is True
|
||||
assert action.done is True
|
||||
download_chain.download_single.assert_called_once_with(
|
||||
context=source, downloader="qb", save_path="/downloads", label="rss",
|
||||
)
|
||||
assert source.media_info is media
|
||||
assert isinstance(source.meta_info, MetaBase)
|
||||
assert isinstance(source.torrent_info, TorrentInfo)
|
||||
assert media_chain.recognize_by_meta.call_count == 1
|
||||
assert download_chain.media_exists.call_count == int(only_lack)
|
||||
assert saved_cache == ["None-Show S01E03"]
|
||||
assert result.outputs["downloads"][0].download_id == "hash-show"
|
||||
assert result.context.downloads[0].downloader == "qb"
|
||||
|
||||
|
||||
def test_add_download_reuses_complete_domain_context():
|
||||
"""领域对象应原样交给下载链,保留剧集限制、识别状态及原始媒体信息。"""
|
||||
context = Context(
|
||||
meta_info=MetaInfo("Show S01E03"),
|
||||
media_info=MediaInfo(title="Show", type=MediaType.TV, tmdb_id=123),
|
||||
torrent_info=TorrentInfo(title="Show S01E03"),
|
||||
media_recognize_fail_count=2,
|
||||
allowed_episodes={3},
|
||||
selected_episodes=[3],
|
||||
resource_source="rss",
|
||||
)
|
||||
|
||||
assert AddDownloadAction._to_domain_context(context) is context
|
||||
|
||||
|
||||
@pytest.mark.parametrize("missing", ["media_info", "torrent_info"])
|
||||
def test_add_download_rejects_incomplete_domain_context(missing):
|
||||
"""原生领域输入仍须满足下载动作的媒体及种子信息完整性要求。"""
|
||||
context = Context(
|
||||
media_info=MediaInfo(title="Show", type=MediaType.TV),
|
||||
torrent_info=TorrentInfo(title="Show S01E03"),
|
||||
)
|
||||
setattr(context, missing, None)
|
||||
|
||||
with pytest.raises(ValueError, match="缺少媒体或种子信息"):
|
||||
AddDownloadAction._to_domain_context(context)
|
||||
Reference in New Issue
Block a user