Files
MoviePilot/tests/test_notification_channel_manage.py
jxxghp 240a4dffe6 refactor(schemas): 统一 message/notification 命名边界,旧名收敛至兼容映射表
- notification 域:渠道能力(MessageChannel→NotificationChannel、ChannelCapability* 迁入 notification.py)
- message 域:消息收发(Notification→Message、NotificationType→MessageType、CommingMessage→IncomingMessage、NotificationHistoryItem→MessageHistoryItem、NotificationClear*→MessageClear*)
- Agent 工具契约:send_notification_message→send_message、notification_callback→message_callback
- 源码不保留旧名物理别名,旧导入经 app/runtime/compat/manifest.py SYMBOL_ALIASES 惰性解析
- API 路径与持久化键冻结不变,前端零改动
- 新增兼容守护测试与 docs/rules/07 命名边界规范
2026-08-16 19:32:20 +08:00

161 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
通知渠道通用管理契约(channel_manage)守护测试
验证通用模式的三条核心性质:
1. 按渠道名路由:非本渠道直接返回 None,交由分发机制继续执行其它模块
2. 动作词汇表由 schemas 契约层统一定义,未支持动作返回统一错误结构
3. 临时参数初始化封闭在模块内:无已保存配置时可基于表单参数构造临时客户端
"""
from types import SimpleNamespace
import pytest
from app.chain.notification import NotificationChain
from app.modules.wechatclawbot import WechatClawBotModule
from app.schemas.types import NotificationChannel, NotificationAction
@pytest.fixture
def module():
return WechatClawBotModule()
def test_channel_manage_routes_only_matching_channel(module):
"""非本渠道的管理请求返回 None,run_module 分发将继续执行其它模块。"""
result = module.channel_manage(
channel=NotificationChannel.Telegram,
action=NotificationAction.STATUS,
)
assert result is None
def test_channel_manage_rejects_unknown_action(module):
"""动作词汇表之外的请求返回统一错误结构。"""
result = module.channel_manage(
channel=NotificationChannel.WechatClawBot,
action="not_an_action",
)
assert result["success"] is False
assert "不支持" in result["message"]
def test_channel_manage_requires_saved_config_without_form_params(module, monkeypatch):
"""无任何配置且未提供表单参数时,返回提示保存配置的错误。"""
monkeypatch.setattr(module, "get_instance", lambda name=None: None)
result = module.channel_manage(
channel=NotificationChannel.WechatClawBot,
action=NotificationAction.TEST_CONNECTION,
)
assert result["success"] is False
assert "保存" in result["message"]
def test_channel_manage_builds_temporary_client_from_form_params(module, monkeypatch):
"""无已保存配置时基于表单参数构造临时客户端,实现未保存即预览。"""
monkeypatch.setattr(module, "get_instance", lambda name=None: None)
captured = {}
class _FakeClient:
def test_connection(self):
captured["called"] = True
return False, "未登录,请先扫码完成绑定"
monkeypatch.setattr(
"app.modules.wechatclawbot.WechatClawBot",
lambda **kwargs: (captured.update(kwargs=kwargs), _FakeClient())[1],
)
result = module.channel_manage(
channel=NotificationChannel.WechatClawBot,
action=NotificationAction.TEST_CONNECTION,
source="预览渠道",
WECHATCLAWBOT_BASE_URL="http://127.0.0.1:1",
)
assert captured["called"] is True
assert captured["kwargs"]["name"] == "预览渠道"
assert captured["kwargs"]["auto_start_polling"] is False
assert result["success"] is False
assert "未登录" in result["message"]
def test_channel_manage_prefers_saved_instance(module, monkeypatch):
"""存在已保存配置实例时优先使用,不构造临时客户端。"""
saved = SimpleNamespace()
saved.get_status = lambda refresh_remote, auto_generate_qrcode: {
"success": True,
"connected": True,
}
monkeypatch.setattr(module, "get_config", lambda name=None: SimpleNamespace(name="已保存"))
monkeypatch.setattr(module, "get_instance", lambda name=None: saved)
result = module.channel_manage(
channel=NotificationChannel.WechatClawBot,
action=NotificationAction.STATUS,
source="已保存",
)
assert result["success"] is True
assert result["data"]["connected"] is True
def test_channel_manage_migrate_cache_dispatches_without_client(module, monkeypatch):
"""缓存迁移动作不依赖客户端实例,直接走静态迁移逻辑。"""
def _fake_migrate(old_name, new_name, cleanup_old, overwrite):
assert old_name == "旧名"
assert new_name == "新名"
return True, "迁移成功"
monkeypatch.setattr(
"app.modules.wechatclawbot.WechatClawBot.migrate_cached_state",
staticmethod(_fake_migrate),
)
result = module.channel_manage(
channel=NotificationChannel.WechatClawBot,
action=NotificationAction.MIGRATE_CACHE,
old_name="旧名",
new_name="新名",
)
assert result == {"success": True, "message": "迁移成功"}
def test_notification_chain_forwards_target_action_and_params(monkeypatch):
"""链层接受字符串标识与透传参数,按 channel_manage 契约原样转发。"""
captured = {}
def fake_run_module(self, method, **kwargs):
captured.update(method=method, kwargs=kwargs)
return {"success": True, "data": {"connected": True}}
monkeypatch.setattr(NotificationChain, "run_module", fake_run_module)
chain = NotificationChain.__new__(NotificationChain)
result = chain.manage_channel(channel="WechatClawBot", action="status", source="预览")
assert captured["method"] == "channel_manage"
assert captured["kwargs"] == {
"channel": "WechatClawBot",
"action": "status",
"source": "预览",
}
assert result["success"] is True
def test_notification_chain_reports_missing_module(monkeypatch):
"""无模块实现 channel_manage 时返回统一失败结构。"""
monkeypatch.setattr(NotificationChain, "run_module", lambda self, method, **kwargs: None)
chain = NotificationChain.__new__(NotificationChain)
result = chain.manage_channel(channel="unknown", action="status")
assert result["success"] is False
assert result["message"]
def test_channel_manage_accepts_plain_string_identifiers(module, monkeypatch):
"""端点层透传的原始字符串渠道名与动作名可被模块正确路由与解释。"""
saved = SimpleNamespace()
saved.test_connection = lambda: (True, None)
monkeypatch.setattr(module, "get_instance", lambda name=None: saved)
result = module.channel_manage(channel="WechatClawBot", action="test_connection")
assert result == {"success": True, "message": None}