250 lines
8.9 KiB
Python
250 lines
8.9 KiB
Python
|
|
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
|