新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块 同时在测试配置中添加了测试用的OpenAI API密钥环境变量
211 lines
6.9 KiB
Python
211 lines
6.9 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from yuxi.channels.adapters.mattermost.adapter import MattermostAdapter
|
|
from yuxi.channels.models import (
|
|
ChannelIdentity,
|
|
ChannelMessage,
|
|
ChannelResponse,
|
|
ChannelStatus,
|
|
ChannelType,
|
|
ChatType,
|
|
EventType,
|
|
HealthStatus,
|
|
MessageType,
|
|
)
|
|
|
|
|
|
def make_ws_data(event="posted", post=None, channel_data=None, broadcast=None):
|
|
data = {}
|
|
if post:
|
|
data["post"] = '{"id":"m1","channel_id":"ch1","user_id":"u1","message":"hello"}'
|
|
if channel_data:
|
|
data["channel"] = '{"type":"D","display_name":"tester"}'
|
|
return {
|
|
"event": event,
|
|
"data": data,
|
|
"broadcast": broadcast or {},
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def adapter():
|
|
config = {
|
|
"bot_token": "test-token",
|
|
"server_url": "https://mattermost.example.com",
|
|
}
|
|
return MattermostAdapter(config=config)
|
|
|
|
|
|
class TestMattermostAdapterMeta:
|
|
def test_channel_id(self, adapter):
|
|
assert adapter.channel_id == "mattermost"
|
|
|
|
def test_channel_type(self, adapter):
|
|
assert adapter.channel_type == ChannelType.MATTERMOST
|
|
|
|
def test_capabilities(self, adapter):
|
|
assert adapter.supports_streaming is True
|
|
assert adapter.supports_markdown is True
|
|
assert adapter.text_chunk_limit == 16383
|
|
assert adapter.max_media_size_mb == 100
|
|
assert adapter.streaming_modes == ["off", "partial", "block", "progress"]
|
|
|
|
def test_initial_status(self, adapter):
|
|
assert adapter._status == ChannelStatus.DISCONNECTED
|
|
|
|
def test_webhook_path_is_none(self, adapter):
|
|
assert adapter.webhook_path is None
|
|
|
|
|
|
class TestNormalizeInbound:
|
|
def test_normalize_text_message(self, adapter):
|
|
adapter._bot_username = "yuxi"
|
|
raw = make_ws_data(post=True, channel_data=True)
|
|
raw["data"]["channel"] = '{"type":"O","display_name":"general"}'
|
|
|
|
result = adapter.normalize_inbound(raw)
|
|
|
|
assert isinstance(result, ChannelMessage)
|
|
assert result.content == "hello"
|
|
assert result.identity.channel_id == "mattermost"
|
|
assert result.identity.channel_type == ChannelType.MATTERMOST
|
|
assert result.identity.channel_user_id == "u1"
|
|
assert result.identity.channel_chat_id == "channel_ch1"
|
|
assert result.identity.channel_message_id == "m1"
|
|
assert result.event_type == EventType.MESSAGE_RECEIVED
|
|
assert result.message_type == MessageType.TEXT
|
|
assert result.chat_type == ChatType.GROUP
|
|
|
|
def test_normalize_with_bot_mention(self, adapter):
|
|
adapter._bot_username = "yuxi"
|
|
raw = make_ws_data(
|
|
post=True,
|
|
channel_data=True,
|
|
)
|
|
import json
|
|
|
|
raw["data"]["post"] = json.dumps({"id": "m1", "channel_id": "ch1", "user_id": "u1", "message": "hey @yuxi help"})
|
|
|
|
result = adapter.normalize_inbound(raw)
|
|
assert result.mentions is not None
|
|
assert result.mentions.is_bot_mentioned is True
|
|
assert "yuxi" in result.mentions.mentioned_user_ids
|
|
|
|
def test_normalize_post_edited(self, adapter):
|
|
adapter._bot_username = "yuxi"
|
|
raw = make_ws_data(event="post_edited", post=True, channel_data=True)
|
|
|
|
result = adapter.normalize_inbound(raw)
|
|
assert result.event_type == EventType.MESSAGE_UPDATED
|
|
|
|
def test_normalize_command(self, adapter):
|
|
import json
|
|
|
|
adapter._bot_username = "yuxi"
|
|
raw = make_ws_data(post=True, channel_data=True)
|
|
raw["data"]["post"] = json.dumps({"id": "m1", "channel_id": "ch1", "user_id": "u1", "message": "/reset"})
|
|
|
|
result = adapter.normalize_inbound(raw)
|
|
assert result.message_type == MessageType.COMMAND
|
|
|
|
def test_normalize_group_channel(self, adapter):
|
|
import json
|
|
|
|
adapter._bot_username = "yuxi"
|
|
raw = make_ws_data(post=True, channel_data=True)
|
|
raw["data"]["channel"] = '{"type":"O","display_name":"general"}'
|
|
|
|
result = adapter.normalize_inbound(raw)
|
|
assert result.chat_type == ChatType.GROUP
|
|
|
|
def test_normalize_thread_message(self, adapter):
|
|
import json
|
|
|
|
adapter._bot_username = "yuxi"
|
|
raw = make_ws_data(post=True, channel_data=True)
|
|
raw["data"]["post"] = json.dumps({"id": "m1", "channel_id": "ch1", "user_id": "u1", "message": "reply", "root_id": "root99"})
|
|
|
|
result = adapter.normalize_inbound(raw)
|
|
assert result.chat_type == ChatType.THREAD
|
|
assert result.reply_to_message_id == "root99"
|
|
|
|
def test_normalize_metadata(self, adapter):
|
|
import json
|
|
|
|
adapter._bot_username = "yuxi"
|
|
raw = make_ws_data(
|
|
post=True,
|
|
channel_data=True,
|
|
broadcast={"channel_id": "ch1", "team_id": "team_xxx"},
|
|
)
|
|
raw["data"]["channel"] = '{"type":"D","display_name":"tester_dm"}'
|
|
|
|
result = adapter.normalize_inbound(raw)
|
|
assert result.metadata["team_id"] == "team_xxx"
|
|
assert result.metadata["channel_name"] == "tester_dm"
|
|
|
|
|
|
class TestFormatOutbound:
|
|
def test_format_text_message(self, adapter):
|
|
identity = ChannelIdentity(
|
|
channel_id="mattermost",
|
|
channel_type=ChannelType.MATTERMOST,
|
|
channel_user_id="u1",
|
|
channel_chat_id="ch1",
|
|
)
|
|
response = ChannelResponse(identity=identity, content="hello world")
|
|
payload = adapter.format_outbound(response)
|
|
|
|
assert payload["channel_id"] == "ch1"
|
|
assert payload["message"] == "hello world"
|
|
assert payload["root_id"] == ""
|
|
|
|
def test_format_with_reply(self, adapter):
|
|
identity = ChannelIdentity(
|
|
channel_id="mattermost",
|
|
channel_type=ChannelType.MATTERMOST,
|
|
channel_user_id="u1",
|
|
channel_chat_id="ch1",
|
|
)
|
|
response = ChannelResponse(identity=identity, content="reply", reply_to_message_id="root1")
|
|
payload = adapter.format_outbound(response)
|
|
|
|
assert payload["root_id"] == "root1"
|
|
|
|
def test_format_with_attachments(self, adapter):
|
|
identity = ChannelIdentity(
|
|
channel_id="mattermost",
|
|
channel_type=ChannelType.MATTERMOST,
|
|
channel_user_id="u1",
|
|
channel_chat_id="ch1",
|
|
)
|
|
response = ChannelResponse(
|
|
identity=identity,
|
|
content="check this",
|
|
metadata={"file_ids": ["f1", "f2"]},
|
|
)
|
|
payload = adapter.format_outbound(response)
|
|
|
|
assert payload["file_ids"] == ["f1", "f2"]
|
|
|
|
|
|
class TestHealthCheck:
|
|
def test_health_check_disconnected(self, adapter):
|
|
import asyncio
|
|
|
|
async def _check():
|
|
result = await adapter.health_check()
|
|
assert result.status == "unhealthy"
|
|
|
|
asyncio.run(_check())
|
|
|
|
|
|
class TestPreConnect:
|
|
@pytest.mark.asyncio
|
|
async def test_pre_connect_missing_config(self):
|
|
adapter = MattermostAdapter(config={})
|
|
result = await adapter.pre_connect()
|
|
assert result["status"] == "error"
|
|
assert "Missing" in result["message"] |