Files
MoviePilot/tests/test_discord_command_registration.py
2026-06-15 08:03:29 +08:00

119 lines
4.3 KiB
Python

import asyncio
from types import SimpleNamespace
from unittest.mock import AsyncMock, Mock, patch
from app.modules.discord import DiscordModule
from app.modules.discord.discord import Discord
from app.schemas import CommandRegisterEventData
def test_discord_module_register_commands_filters_event_subset():
"""Discord 模块注册命令时应复用渠道级 CommandRegister 事件过滤结果。"""
module = DiscordModule()
client = SimpleNamespace(register_commands=Mock(), delete_commands=Mock())
original_commands = {
"/sites": {"description": "管理站点"},
"/version": {"description": "当前版本"},
}
event = SimpleNamespace(
event_data=CommandRegisterEventData(
commands={"/sites": {"description": "管理站点"}, "/unknown": {"description": "无效"}},
origin="DiscordFilter",
service="discord-main",
)
)
with (
patch.object(
module,
"get_configs",
return_value={"discord-main": SimpleNamespace(name="discord-main", config={})},
),
patch.object(module, "get_instance", return_value=client),
patch("app.modules.discord.eventmanager.send_event", return_value=event),
):
module.register_commands(original_commands)
client.register_commands.assert_called_once_with(
{"/sites": {"description": "管理站点"}}
)
client.delete_commands.assert_not_called()
def test_discord_module_register_commands_deletes_when_event_canceled():
"""Discord 模块注册命令被事件取消时应清理应用命令。"""
module = DiscordModule()
client = SimpleNamespace(register_commands=Mock(), delete_commands=Mock())
event = SimpleNamespace(
event_data=CommandRegisterEventData(
commands={"/sites": {"description": "管理站点"}},
origin="DiscordFilter",
service="discord-main",
cancel=True,
)
)
with (
patch.object(
module,
"get_configs",
return_value={"discord-main": SimpleNamespace(name="discord-main", config={})},
),
patch.object(module, "get_instance", return_value=client),
patch("app.modules.discord.eventmanager.send_event", return_value=event),
):
module.register_commands({"/sites": {"description": "管理站点"}})
client.delete_commands.assert_called_once_with()
client.register_commands.assert_not_called()
def test_discord_normalizes_slash_command_names():
"""Discord 命令名称应符合平台只允许小写字母数字下划线连字符的约束。"""
assert Discord._normalize_slash_command_name("/sites") == "sites"
assert Discord._normalize_slash_command_name("/clear_cache") == "clear_cache"
assert Discord._normalize_slash_command_name("/INVALID") == "invalid"
assert Discord._normalize_slash_command_name("/中文") == ""
assert Discord._normalize_slash_command_name("/" + "a" * 33) == ""
def test_discord_handle_slash_command_forwards_to_message_chain():
"""Discord 斜杠命令回调应转发为统一消息入口可识别的命令文本。"""
client = Discord.__new__(Discord)
client._update_user_chat_mapping = Mock()
client._post_to_ds = AsyncMock()
user = SimpleNamespace(id=10001, display_name="tester", global_name=None, name="tester")
channel = SimpleNamespace(id=20001)
interaction = SimpleNamespace(
id=30001,
user=user,
channel=channel,
response=SimpleNamespace(
defer=AsyncMock(),
is_done=Mock(return_value=True),
send_message=AsyncMock(),
),
followup=SimpleNamespace(send=AsyncMock()),
)
asyncio.run(client._handle_slash_command(interaction, "/sites", "refresh"))
client._update_user_chat_mapping.assert_called_once_with("10001", "20001")
client._post_to_ds.assert_awaited_once_with(
{
"type": "message",
"userid": "10001",
"username": "tester",
"user_tag": str(user),
"text": "/sites refresh",
"message_id": "30001",
"chat_id": "20001",
"channel_type": "guild",
}
)
interaction.followup.send.assert_awaited_once_with(
"命令已提交,请稍等...",
ephemeral=True,
)