mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-06-21 15:36:37 +08:00
193 lines
5.9 KiB
Python
193 lines
5.9 KiB
Python
import asyncio
|
||
import json
|
||
from unittest.mock import MagicMock, patch
|
||
|
||
from app.agent.tools.factory import MoviePilotToolFactory
|
||
from app.agent.tools.impl.update_download_tasks import UpdateDownloadTasksTool
|
||
from app.schemas import DownloaderTorrent
|
||
|
||
|
||
def test_update_download_tasks_resolves_downloader_and_updates_all_supported_fields():
|
||
"""
|
||
未显式传下载器时,应先按 Hash 解析任务所属下载器,再一次性执行多项修改。
|
||
"""
|
||
hash_value = "a" * 40
|
||
download_chain = MagicMock()
|
||
download_chain.list_torrents.return_value = [
|
||
DownloaderTorrent(downloader="qb", hash=hash_value, title="Demo")
|
||
]
|
||
download_chain.set_torrents_tag.return_value = True
|
||
download_chain.set_downloading.return_value = True
|
||
download_chain.update_torrent.return_value = {
|
||
"limits": True,
|
||
"trackers": True,
|
||
"save_path": True,
|
||
"category": True,
|
||
}
|
||
|
||
with patch(
|
||
"app.agent.tools.impl.update_download_tasks.DownloadChain",
|
||
return_value=download_chain,
|
||
):
|
||
result = UpdateDownloadTasksTool._update_download_sync(
|
||
hash_value=hash_value,
|
||
action="stop",
|
||
tags=["movie", "hd"],
|
||
download_limit=1024,
|
||
upload_limit=512,
|
||
trackers=["https://tracker.example/announce"],
|
||
save_path="/downloads/new",
|
||
category="电影",
|
||
ratio_limit=2.5,
|
||
seeding_time_limit=1440,
|
||
)
|
||
|
||
assert result["downloader"] == "qb"
|
||
assert {item["operation"] for item in result["results"]} == {
|
||
"tags",
|
||
"stop",
|
||
"limits",
|
||
"trackers",
|
||
"save_path",
|
||
"category",
|
||
}
|
||
assert all(item["success"] for item in result["results"])
|
||
download_chain.list_torrents.assert_called_once_with(
|
||
hashs=[hash_value],
|
||
include_all_tags=True,
|
||
)
|
||
download_chain.set_torrents_tag.assert_called_once_with(
|
||
hashs=[hash_value],
|
||
tags=["movie", "hd"],
|
||
downloader="qb",
|
||
)
|
||
download_chain.set_downloading.assert_called_once_with(
|
||
hash_str=hash_value,
|
||
oper="stop",
|
||
name="qb",
|
||
)
|
||
download_chain.update_torrent.assert_called_once_with(
|
||
hash_string=hash_value,
|
||
downloader="qb",
|
||
download_limit=1024,
|
||
upload_limit=512,
|
||
tracker_list=["https://tracker.example/announce"],
|
||
save_path="/downloads/new",
|
||
category="电影",
|
||
ratio_limit=2.5,
|
||
seeding_time_limit=1440,
|
||
)
|
||
|
||
|
||
def test_update_download_tasks_skips_property_update_when_only_action_is_requested():
|
||
"""
|
||
仅开始或暂停任务时,不应额外调用属性修改接口。
|
||
"""
|
||
hash_value = "e" * 40
|
||
download_chain = MagicMock()
|
||
download_chain.list_torrents.return_value = [
|
||
DownloaderTorrent(downloader="tr", hash=hash_value, title="Demo")
|
||
]
|
||
download_chain.set_downloading.return_value = True
|
||
|
||
with patch(
|
||
"app.agent.tools.impl.update_download_tasks.DownloadChain",
|
||
return_value=download_chain,
|
||
):
|
||
result = UpdateDownloadTasksTool._update_download_sync(
|
||
hash_value=hash_value,
|
||
action="start",
|
||
)
|
||
|
||
assert result["results"] == [
|
||
{"operation": "start", "success": True, "message": "成功开始下载任务"}
|
||
]
|
||
download_chain.update_torrent.assert_not_called()
|
||
|
||
|
||
def test_update_download_tasks_reports_missing_task_when_downloader_cannot_be_resolved():
|
||
"""
|
||
找不到任务时,应返回明确的解析失败结果。
|
||
"""
|
||
hash_value = "b" * 40
|
||
download_chain = MagicMock()
|
||
download_chain.list_torrents.return_value = []
|
||
|
||
with patch(
|
||
"app.agent.tools.impl.update_download_tasks.DownloadChain",
|
||
return_value=download_chain,
|
||
):
|
||
result = UpdateDownloadTasksTool._update_download_sync(
|
||
hash_value=hash_value,
|
||
action="start",
|
||
)
|
||
|
||
assert result["results"] == [
|
||
{
|
||
"operation": "resolve_downloader",
|
||
"success": False,
|
||
"message": "未找到下载任务或下载器不可用",
|
||
}
|
||
]
|
||
download_chain.set_downloading.assert_not_called()
|
||
download_chain.update_torrent.assert_not_called()
|
||
|
||
|
||
def test_update_download_tasks_run_rejects_empty_update():
|
||
"""
|
||
没有任何修改字段时,应拒绝调用下载器。
|
||
"""
|
||
result = asyncio.run(
|
||
UpdateDownloadTasksTool(session_id="session-1", user_id="10001").run(
|
||
hash="c" * 40
|
||
)
|
||
)
|
||
|
||
assert "至少需要指定一个要更新的字段" in result
|
||
|
||
|
||
def test_update_download_tasks_run_outputs_structured_result():
|
||
"""
|
||
工具运行结果应是结构化 JSON,方便 Agent 判断每项修改是否成功。
|
||
"""
|
||
with patch.object(
|
||
UpdateDownloadTasksTool,
|
||
"_update_download_sync",
|
||
return_value={
|
||
"hash": "d" * 40,
|
||
"downloader": "tr",
|
||
"results": [
|
||
{"operation": "limits", "success": True, "message": "限速/做种策略修改成功"}
|
||
],
|
||
},
|
||
):
|
||
result = asyncio.run(
|
||
UpdateDownloadTasksTool(session_id="session-1", user_id="10001").run(
|
||
hash="d" * 40,
|
||
download_limit=100,
|
||
)
|
||
)
|
||
|
||
payload = json.loads(result)
|
||
assert payload["downloader"] == "tr"
|
||
assert payload["results"][0]["operation"] == "limits"
|
||
|
||
|
||
def test_factory_registers_update_download_tasks_without_old_modify_name():
|
||
"""
|
||
工具工厂应只暴露统一后的下载任务更新工具名。
|
||
"""
|
||
with patch(
|
||
"app.agent.tools.factory.PluginManager.get_plugin_agent_tools",
|
||
return_value=[],
|
||
):
|
||
tools = MoviePilotToolFactory.create_tools(
|
||
session_id="download-task-session",
|
||
user_id="10001",
|
||
)
|
||
|
||
tool_names = {tool.name for tool in tools}
|
||
assert "query_download_tasks" in tool_names
|
||
assert "update_download_tasks" in tool_names
|
||
assert "modify_download" not in tool_names
|