新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块 同时在测试配置中添加了测试用的OpenAI API密钥环境变量
216 lines
6.9 KiB
Python
216 lines
6.9 KiB
Python
from __future__ import annotations
|
|
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from yuxi.channels.adapters.irc.accounts import has_configured_state
|
|
from yuxi.channels.adapters.irc.commands import sender_allowed_for_commands
|
|
from yuxi.channels.adapters.irc.runtime import (
|
|
clear_irc_runtime,
|
|
get_irc_runtime,
|
|
record_irc_outbound_activity,
|
|
set_irc_runtime,
|
|
)
|
|
from yuxi.channels.adapters.irc.security import (
|
|
escape_irc_regex_literal,
|
|
resolve_irc_group_sender_allowed,
|
|
)
|
|
from yuxi.channels.adapters.irc.splits import split_irc_text
|
|
|
|
|
|
class TestEscapeIrcRegexLiteral:
|
|
"""验证 escape_irc_regex_literal 安全正则构造"""
|
|
|
|
def test_escapes_special_chars(self):
|
|
escaped = escape_irc_regex_literal("nick[name]")
|
|
assert "\\[" in escaped
|
|
assert "\\]" in escaped
|
|
|
|
def test_escapes_dot(self):
|
|
escaped = escape_irc_regex_literal("bot.test")
|
|
assert "\\." in escaped
|
|
|
|
def test_handles_empty_string(self):
|
|
assert escape_irc_regex_literal("") == ""
|
|
|
|
def test_simple_nick_unchanged(self):
|
|
escaped = escape_irc_regex_literal("MyBot")
|
|
assert escaped == "MyBot"
|
|
|
|
def test_escapes_parens(self):
|
|
escaped = escape_irc_regex_literal("bot(test)")
|
|
assert "\\(" in escaped
|
|
assert "\\)" in escaped
|
|
|
|
def test_escapes_star(self):
|
|
escaped = escape_irc_regex_literal("bot*")
|
|
assert "\\*" in escaped
|
|
|
|
def test_escapes_plus(self):
|
|
escaped = escape_irc_regex_literal("bot+")
|
|
assert "\\+" in escaped
|
|
|
|
def test_escapes_question(self):
|
|
escaped = escape_irc_regex_literal("bot?")
|
|
assert "\\?" in escaped
|
|
|
|
def test_escapes_backslash(self):
|
|
escaped = escape_irc_regex_literal("bot\\test")
|
|
assert "\\\\" in escaped
|
|
|
|
|
|
class TestSenderAllowedForCommands:
|
|
"""验证 sender_allowed_for_commands 命令白名单匹配"""
|
|
|
|
def test_exact_nick_match(self):
|
|
assert sender_allowed_for_commands(
|
|
"trusted!user@host", "trusted", ["trusted"]
|
|
) is True
|
|
|
|
def test_full_mask_match(self):
|
|
assert sender_allowed_for_commands(
|
|
"trusted!user@host", "trusted", ["trusted!user@host"]
|
|
) is True
|
|
|
|
def test_no_match(self):
|
|
assert sender_allowed_for_commands(
|
|
"untrusted!user@host", "untrusted", ["trusted"]
|
|
) is False
|
|
|
|
def test_empty_allowlist(self):
|
|
assert sender_allowed_for_commands(
|
|
"anyone!user@host", "anyone", []
|
|
) is False
|
|
|
|
def test_wildcard_match(self):
|
|
assert sender_allowed_for_commands(
|
|
"anyone!user@host", "anyone", ["*"]
|
|
) is True
|
|
|
|
def test_nick_host_match(self):
|
|
assert sender_allowed_for_commands(
|
|
"someone!user@example.com", "someone", ["someone@example.com"]
|
|
) is True
|
|
|
|
|
|
class TestHasConfiguredState:
|
|
"""验证 has_configured_state 环境变量判定"""
|
|
|
|
def test_returns_false_when_no_env(self):
|
|
with patch("os.environ", {}):
|
|
assert has_configured_state() is False
|
|
|
|
def test_returns_true_when_both_set(self):
|
|
env = {"IRC_HOST": "irc.libera.chat", "IRC_NICK": "MyBot"}
|
|
with patch("os.environ", env):
|
|
assert has_configured_state() is True
|
|
|
|
def test_returns_false_when_only_host(self):
|
|
env = {"IRC_HOST": "irc.libera.chat"}
|
|
with patch("os.environ", env):
|
|
assert has_configured_state() is False
|
|
|
|
def test_returns_false_when_only_nick(self):
|
|
env = {"IRC_NICK": "MyBot"}
|
|
with patch("os.environ", env):
|
|
assert has_configured_state() is False
|
|
|
|
|
|
class TestResolveIrcGroupSenderAllowed:
|
|
"""验证 resolve_irc_group_sender_allowed 三层回退"""
|
|
|
|
def test_inner_allowlist_match(self):
|
|
groups = {"#test": {"allowFrom": ["trusted!user@host"]}}
|
|
allowed, layer, match = resolve_irc_group_sender_allowed(
|
|
"#test", "trusted!user@host", "trusted", groups, [], "allowlist"
|
|
)
|
|
assert allowed is True
|
|
assert layer == "group_inner_allowlist"
|
|
assert match == "trusted!user@host"
|
|
|
|
def test_outer_allowlist_match(self):
|
|
allowed, layer, match = resolve_irc_group_sender_allowed(
|
|
"#test", "trusted!user@host", "trusted", {}, ["trusted!user@host"], "allowlist"
|
|
)
|
|
assert allowed is True
|
|
assert layer == "group_outer_allowlist"
|
|
assert match == "trusted!user@host"
|
|
|
|
def test_group_policy_open(self):
|
|
allowed, layer, match = resolve_irc_group_sender_allowed(
|
|
"#test", "anyone!user@host", "anyone", {}, [], "open"
|
|
)
|
|
assert allowed is True
|
|
assert layer == "group_policy_open"
|
|
assert match is None
|
|
|
|
def test_channel_policy_open(self):
|
|
groups = {"#test": {"groupPolicy": "open"}}
|
|
allowed, layer, match = resolve_irc_group_sender_allowed(
|
|
"#test", "anyone!user@host", "anyone", groups, [], "allowlist"
|
|
)
|
|
assert allowed is True
|
|
assert layer == "channel_policy_open"
|
|
assert match is None
|
|
|
|
def test_blocked(self):
|
|
allowed, layer, match = resolve_irc_group_sender_allowed(
|
|
"#test", "untrusted!user@host", "untrusted", {}, [], "allowlist"
|
|
)
|
|
assert allowed is False
|
|
assert layer == "blocked"
|
|
assert match is None
|
|
|
|
|
|
class TestRuntimeStore:
|
|
"""验证 Runtime Store 机制"""
|
|
|
|
def test_set_and_get(self):
|
|
store = {"key": "value"}
|
|
set_irc_runtime(store)
|
|
assert get_irc_runtime() is store
|
|
|
|
def test_clear(self):
|
|
set_irc_runtime({"key": "value"})
|
|
clear_irc_runtime()
|
|
assert get_irc_runtime() is None
|
|
|
|
def test_get_when_not_set(self):
|
|
clear_irc_runtime()
|
|
assert get_irc_runtime() is None
|
|
|
|
def test_record_activity_adds_entry(self):
|
|
store: dict = {}
|
|
set_irc_runtime(store)
|
|
record_irc_outbound_activity("#test", "hello world")
|
|
assert "outbound_activity" in store
|
|
assert len(store["outbound_activity"]) == 1
|
|
assert store["outbound_activity"][0]["target"] == "#test"
|
|
assert store["outbound_activity"][0]["text"] == "hello world"
|
|
|
|
def test_record_activity_uninitialized(self):
|
|
clear_irc_runtime()
|
|
record_irc_outbound_activity("#test", "hello world")
|
|
assert get_irc_runtime() is None
|
|
|
|
|
|
class TestSplitIrcText:
|
|
"""验证 split_irc_text 独立工具函数"""
|
|
|
|
def test_short_text_unchanged(self):
|
|
result = split_irc_text("hello", max_chars=350)
|
|
assert result == ["hello"]
|
|
|
|
def test_long_text_split(self):
|
|
long_text = "A" * 2000
|
|
result = split_irc_text(long_text, max_chars=50)
|
|
assert len(result) > 1
|
|
assert all(len(chunk.encode("utf-8")) <= 200 for chunk in result)
|
|
|
|
def test_empty_text(self):
|
|
assert split_irc_text("") == [""]
|
|
|
|
def test_max_chars_zero(self):
|
|
result = split_irc_text("hello", max_chars=0)
|
|
assert "hello" in result[0] |