from __future__ import annotations from unittest.mock import AsyncMock import pytest class TestWeChatAccountPlugin: def test_plugin_attributes(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatAccountPlugin plugin = WeChatAccountPlugin() assert plugin.plugin_id == "wechat-account" assert plugin.plugin_type == "account" def test_init_with_config(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatAccountPlugin config = {"corp_id": "test_corp", "corp_secret": "test_secret"} plugin = WeChatAccountPlugin(config) assert plugin._config == config def test_init_with_no_config(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatAccountPlugin plugin = WeChatAccountPlugin() assert plugin._config == {} @pytest.mark.asyncio async def test_register(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatAccountPlugin plugin = WeChatAccountPlugin() registry = AsyncMock() await plugin.register(registry) @pytest.mark.asyncio async def test_unregister(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatAccountPlugin plugin = WeChatAccountPlugin() registry = AsyncMock() await plugin.unregister(registry) def test_detect_legacy_state_migrations_wechat_token(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatAccountPlugin plugin = WeChatAccountPlugin() old_config = {"wechat_token": "legacy_token_value"} migrated = plugin.detect_legacy_state_migrations(old_config) assert "corp_secret" in migrated assert migrated["corp_secret"] == "legacy_token_value" assert "wechat_token" not in migrated def test_detect_legacy_state_migrations_no_legacy(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatAccountPlugin plugin = WeChatAccountPlugin() old_config = {"corp_secret": "modern_value", "corp_id": "test"} migrated = plugin.detect_legacy_state_migrations(old_config) assert migrated["corp_secret"] == "modern_value" assert "wechat_token" not in migrated def test_detect_legacy_state_migrations_preserves_other_keys(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatAccountPlugin plugin = WeChatAccountPlugin() old_config = {"wechat_token": "old", "corp_id": "test_corp", "agent_id": "1000001"} migrated = plugin.detect_legacy_state_migrations(old_config) assert migrated["corp_id"] == "test_corp" assert migrated["agent_id"] == "1000001" assert migrated["corp_secret"] == "old" class TestWeChatSetupPlugin: def test_plugin_attributes(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatSetupPlugin plugin = WeChatSetupPlugin() assert plugin.plugin_id == "wechat-setup" assert plugin.plugin_type == "setup" def test_init_with_config(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatSetupPlugin config = {"app_id": "wx_test", "app_secret": "test_secret"} plugin = WeChatSetupPlugin(config) assert plugin._config == config @pytest.mark.asyncio async def test_register(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatSetupPlugin plugin = WeChatSetupPlugin() registry = AsyncMock() await plugin.register(registry) @pytest.mark.asyncio async def test_unregister(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatSetupPlugin plugin = WeChatSetupPlugin() registry = AsyncMock() await plugin.unregister(registry) def test_detect_legacy_state_migrations_aes_key(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatSetupPlugin plugin = WeChatSetupPlugin() old_config = {"aes_key": "legacy_aes_key_value"} migrated = plugin.detect_legacy_state_migrations(old_config) assert "encoding_aes_key" in migrated assert migrated["encoding_aes_key"] == "legacy_aes_key_value" assert "aes_key" not in migrated def test_detect_legacy_state_migrations_no_legacy(self): from yuxi.channels.adapters.wechat.dual_plugin import WeChatSetupPlugin plugin = WeChatSetupPlugin() old_config = {"encoding_aes_key": "modern_value", "token": "test_token"} migrated = plugin.detect_legacy_state_migrations(old_config) assert migrated["encoding_aes_key"] == "modern_value" assert "aes_key" not in migrated class TestSetupContract: def test_get_setup_contract(self): from yuxi.channels.adapters.wechat.setup_contract import get_setup_contract contract = get_setup_contract() assert contract["channel_id"] == "wechat" assert "setup_steps" in contract assert "health_checks" in contract def test_setup_steps_count(self): from yuxi.channels.adapters.wechat.setup_contract import get_setup_contract contract = get_setup_contract() steps = contract["setup_steps"] assert len(steps) == 5 def test_mode_selection_step(self): from yuxi.channels.adapters.wechat.setup_contract import get_setup_contract contract = get_setup_contract() mode_step = contract["setup_steps"][0] assert mode_step["id"] == "mode_selection" assert len(mode_step["options"]) == 3 modes = {o["id"] for o in mode_step["options"]} assert modes == {"wecom", "mp", "personal"} def test_wecom_option_required_fields(self): from yuxi.channels.adapters.wechat.setup_contract import get_setup_contract contract = get_setup_contract() wecom_option = contract["setup_steps"][0]["options"][0] assert wecom_option["required_fields"] == ["corp_id", "corp_secret", "agent_id"] def test_mp_option_required_fields(self): from yuxi.channels.adapters.wechat.setup_contract import get_setup_contract contract = get_setup_contract() mp_option = contract["setup_steps"][0]["options"][1] assert mp_option["required_fields"] == ["app_id", "app_secret"] def test_personal_option_required_fields(self): from yuxi.channels.adapters.wechat.setup_contract import get_setup_contract contract = get_setup_contract() personal_option = contract["setup_steps"][0]["options"][2] assert personal_option["required_fields"] == ["bridge_url"] def test_health_checks_count(self): from yuxi.channels.adapters.wechat.setup_contract import get_setup_contract contract = get_setup_contract() checks = contract["health_checks"] assert len(checks) == 3 check_ids = {c["id"] for c in checks} assert "wecom_token_valid" in check_ids assert "mp_token_valid" in check_ids assert "bridge_healthy" in check_ids def test_contract_returns_copy(self): from yuxi.channels.adapters.wechat.setup_contract import ( WECHAT_SETUP_CONTRACT, get_setup_contract, ) contract1 = get_setup_contract() contract2 = get_setup_contract() assert contract1 == contract2 assert contract1 is not contract2 class TestDirectoryContract: def test_get_directory_contract(self): from yuxi.channels.adapters.wechat.directory_contract import get_directory_contract contract = get_directory_contract() assert contract["channel_id"] == "wechat" assert "listing_methods" in contract assert "identity_format" in contract def test_listing_methods_peers(self): from yuxi.channels.adapters.wechat.directory_contract import get_directory_contract contract = get_directory_contract() peers = contract["listing_methods"]["peers"] assert "wecom" in peers assert "mp" in peers assert "personal" in peers def test_listing_methods_groups(self): from yuxi.channels.adapters.wechat.directory_contract import get_directory_contract contract = get_directory_contract() groups = contract["listing_methods"]["groups"] assert "wecom" in groups assert "mp" in groups assert "personal" in groups def test_identity_format(self): from yuxi.channels.adapters.wechat.directory_contract import get_directory_contract contract = get_directory_contract() identity = contract["identity_format"] assert identity["user_id"] == "wx:openid" assert identity["group_id"] == "wx:chat_id" def test_contract_returns_copy(self): from yuxi.channels.adapters.wechat.directory_contract import ( WECHAT_DIRECTORY_CONTRACT, get_directory_contract, ) contract1 = get_directory_contract() contract2 = get_directory_contract() assert contract1 == contract2 assert contract1 is not contract2