""" 通知渠道通用管理契约(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}