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

218 lines
6.9 KiB
Python

from __future__ import annotations
from yuxi.channels.adapters.irc._isupport import ISupport
from yuxi.channels.adapters.irc.normalizer import _parse_irc_time_tag, normalize_inbound
from yuxi.channels.models import ChannelType
EXAMPLE_ISUPPORT = (
":irc.example.com 005 mybot CASEMAPPING=rfc1459 CHANMODES=b,k,l,imnpst "
"CHANTYPES=# PREFIX=(ov)@+ NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 "
"KICKLEN=400 AWAYLEN=200 MODES=3 MAXLIST=b:60,e:60,I:60 "
"STATUSMSG=@+ EXCEPTS=e INVEX=I MONITOR=100 "
":are supported by this server"
)
class TestISupport:
def test_parse_prefix_map(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.prefix_map == {"@": "o", "+": "v"}
assert isup.mode_to_prefix("o") == "@"
assert isup.mode_to_prefix("v") == "+"
assert isup.prefix_to_mode("@") == "o"
assert isup.prefix_to_mode("+") == "v"
def test_parse_casemapping(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.casemapping == "rfc1459"
def test_parse_nicklen(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.nicklen == 16
def test_parse_chantypes(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.chantypes == "#"
def test_parse_chanmodes(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
d = isup.to_dict()
assert d["chanmodes"]["a"] == "b"
assert d["chanmodes"]["b"] == "k"
assert d["chanmodes"]["c"] == "l"
assert d["chanmodes"]["d"] == "imnpst"
def test_parse_maxlist(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
d = isup.to_dict()
assert d["has_excepts"] is True
assert d["has_invex"] is True
def test_parse_monitor(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.monitor == 100
def test_parse_modes(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.modes == 3
def test_parse_topiclen(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.topiclen == 390
def test_parse_kicklen(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.kicklen == 400
def test_parse_awaylen(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.awaylen == 200
def test_parse_statusmsg(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.prefixes == ["@", "+"]
def test_strip_prefixes(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.strip_prefixes("@nick") == "nick"
assert isup.strip_prefixes("+nick") == "nick"
assert isup.strip_prefixes("nick") == "nick"
assert isup.strip_prefixes("") == ""
def test_prefix_to_mode_unknown(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.prefix_to_mode("~") is None
def test_mode_to_prefix_unknown(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.mode_to_prefix("q") is None
def test_to_dict_includes_all_sections(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
d = isup.to_dict()
for key in [
"casemapping", "chantypes", "nicklen", "channellen",
"topiclen", "kicklen", "awaylen", "modes", "monitor",
"prefix_map", "chanmodes", "has_excepts", "has_invex", "raw",
]:
assert key in d, f"Missing key: {key}"
def test_get_raw_token(self):
isup = ISupport()
isup.feed_line(EXAMPLE_ISUPPORT)
assert isup.get("NICKLEN") == "16"
assert isup.get("NOT_EXISTS", "fallback") == "fallback"
def test_feed_multiple_lines(self):
isup = ISupport()
isup.feed_line(":irc.test 005 bot CASEMAPPING=ascii NICKLEN=9 :first")
isup.feed_line(":irc.test 005 bot CHANTYPES=&# CHANNELLEN=32 :second")
assert isup.casemapping == "ascii"
assert isup.nicklen == 9
assert isup.chantypes == "&#"
assert isup.channellen == 32
def test_feed_token_without_value(self):
isup = ISupport()
isup.feed_line(":irc.test 005 bot SAFELIST :are supported")
assert isup.get("SAFELIST") == ""
def test_channellen_default(self):
isup = ISupport()
assert isup.channellen == 200
def test_default_prefix_map(self):
isup = ISupport()
assert isup.prefix_map == {"@": "o", "+": "v"}
class TestServerTimeParsing:
def test_parse_valid_iso_time(self):
ts = _parse_irc_time_tag("2024-06-15T12:30:45.123Z")
assert ts > 0
def test_parse_time_without_ms(self):
ts = _parse_irc_time_tag("2024-06-15T12:30:45Z")
assert ts > 0
def test_parse_invalid_returns_current(self):
import time
now = int(time.time() * 1000)
ts = _parse_irc_time_tag("not-a-time")
assert abs(ts - now) < 1000
def test_parse_empty_returns_current(self):
import time
now = int(time.time() * 1000)
ts = _parse_irc_time_tag("")
assert abs(ts - now) < 1000
class TestNormalizeInboundWithServerTime:
def test_uses_server_time_when_present(self):
raw = {
"sender_nick": "user1",
"target": "#test",
"text": "hello",
"time": "2024-06-15T12:00:00.000Z",
}
msg = normalize_inbound(
raw, "irc-channel-id", ChannelType.IRC, "mybot"
)
assert msg.identity.channel_message_id.startswith("user1:")
assert "1718452800000" in msg.identity.channel_message_id
def test_uses_local_time_when_no_time_tag(self):
raw = {
"sender_nick": "user1",
"target": "#test",
"text": "hello",
}
msg = normalize_inbound(
raw, "irc-channel-id", ChannelType.IRC, "mybot"
)
assert msg.identity.channel_message_id.startswith("user1:")
def test_channel_message_mentions(self):
raw = {
"sender_nick": "user1",
"target": "#test",
"text": "hello @mybot how are you",
}
msg = normalize_inbound(
raw, "irc-channel-id", ChannelType.IRC, "mybot"
)
assert msg.mentions.is_bot_mentioned is True
assert "mybot" in msg.mentions.mentioned_user_ids
def test_direct_message_normalization(self):
raw = {
"sender_nick": "alice",
"target": "mybot",
"text": "hi there",
}
msg = normalize_inbound(
raw, "irc-channel-id", ChannelType.IRC, "mybot"
)
assert msg.chat_type.value == "direct"
assert msg.identity.channel_chat_id == "dm_alice"