mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-30 04:27:40 +08:00
- 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 命名边界规范
161 lines
6.0 KiB
Python
161 lines
6.0 KiB
Python
"""
|
|
通知渠道通用管理契约(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} |