ForcePilot/backend/test/unit/channels/test_mattermost_adapter.py
Kris 3264900bc9 test: 新增多渠道单元测试用例并配置测试环境变量
新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块
同时在测试配置中添加了测试用的OpenAI API密钥环境变量
2026-05-12 00:56:47 +08:00

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"]