ForcePilot/backend/test/unit/channels/test_channels_imessage_security.py
Kris 69fe97a90d test: 批量修复并新增单元测试用例
1. 移除Telegram格式化测试中未使用的导入项
2. 修复Teams测试用例,添加monkeypatch参数并配置通配符开关
3. 更新钉钉适配器测试,替换弃用的流属性检查
4. 修正Twitch规范化测试,更新ROOMSTATE测试逻辑
5. 重构会话映射测试,完善数据库执行结果模拟
6. 格式化Slack块构建测试的长参数调用
7. 修复LINE适配器测试,更新能力断言和异步锁使用
8. 修正Slack会话解析测试,修复聊天类型判断错误
9. 更新能力测试,补充缺失的字段检查
10. 修复Matrix适配器测试,修正位置参数和配置校验逻辑
11. 为飞书分析模块测试添加跳过标记
12. 新增微信能力、限流、链接格式、会话路由等模块的单元测试
13. 修复Twitch适配器导入路径和测试断言
14. 新增Discord Webhook、Nextcloud Talk、Signal多账户等模块的单元测试
15. 修复Manager阶段测试的导入路径
16. 新增iMessage异常和命令处理的单元测试
17. 新增Nostr健康检查和相关模块的单元测试
18. 新增Signal守护进程和SSE重连相关测试
2026-05-13 16:43:01 +08:00

346 lines
13 KiB
Python

from __future__ import annotations
import pytest
from yuxi.channels.adapters.imessage.security import (
DmPolicy,
GroupPolicy,
IMessageSecurityPolicy,
SecurityCheckResult,
resolve_dm_policy,
resolve_group_policy,
_match_entry,
_normalize_handle,
_strip_prefix,
)
class TestResolveDmPolicy:
def test_explicit_pairing(self):
assert resolve_dm_policy({"dmPolicy": "pairing"}) == DmPolicy.PAIRING
def test_explicit_open(self):
assert resolve_dm_policy({"dm_policy": "open"}) == DmPolicy.OPEN
def test_explicit_disabled(self):
assert resolve_dm_policy({"dmPolicy": "disabled"}) == DmPolicy.DISABLED
def test_explicit_allowlist(self):
assert resolve_dm_policy({"dm_policy": "allowlist"}) == DmPolicy.ALLOWLIST
def test_invalid_explicit_falls_back(self):
assert resolve_dm_policy({"dm_policy": "invalid"}) == DmPolicy.OPEN
def test_allow_from_with_wildcard(self):
assert resolve_dm_policy({"allowFrom": ["*"]}) == DmPolicy.OPEN
def test_allow_from_with_entries(self):
assert resolve_dm_policy({"allowFrom": ["+8613800138000"]}) == DmPolicy.ALLOWLIST
def test_empty_allow_from(self):
assert resolve_dm_policy({"allowFrom": []}) == DmPolicy.OPEN
def test_no_policy_specified(self):
assert resolve_dm_policy({}) == DmPolicy.OPEN
class TestResolveGroupPolicy:
def test_explicit_open(self):
assert resolve_group_policy({"groupPolicy": "open"}) == GroupPolicy.OPEN
def test_explicit_disabled(self):
assert resolve_group_policy({"group_policy": "disabled"}) == GroupPolicy.DISABLED
def test_explicit_allowlist(self):
assert resolve_group_policy({"groupPolicy": "allowlist"}) == GroupPolicy.ALLOWLIST
def test_invalid_explicit_falls_back(self):
assert resolve_group_policy({"groupPolicy": "invalid"}) == GroupPolicy.ALLOWLIST
def test_no_policy_specified(self):
assert resolve_group_policy({}) == GroupPolicy.ALLOWLIST
class TestNormalizeHandle:
def test_removes_plus(self):
assert _normalize_handle("+8613800138000") == "8613800138000"
def test_removes_spaces(self):
assert _normalize_handle(" +86 138 0013 8000 ") == "8613800138000"
def test_email_preserved(self):
assert _normalize_handle("test@example.com") == "test@example.com"
class TestStripPrefix:
def test_no_prefix(self):
assert _strip_prefix("+8613800138000") == ("+8613800138000", None)
def test_imessage_prefix(self):
assert _strip_prefix("imessage:+8613800138000") == ("+8613800138000", "imessage")
def test_sms_prefix(self):
assert _strip_prefix("sms:+8613800138000") == ("+8613800138000", "sms")
def test_chat_id_prefix(self):
assert _strip_prefix("chat_id:test123") == ("test123", "chat_id")
def test_chat_guid_prefix(self):
assert _strip_prefix("chat_guid:test456") == ("test456", "chat_guid")
def test_prefix_case_insensitive(self):
assert _strip_prefix("IMESSAGE:+8613800138000") == ("+8613800138000", "imessage")
class TestMatchEntry:
def test_wildcard(self):
assert _match_entry("anyone", ["*"]) is True
def test_exact_simple_match(self):
assert _match_entry("+8613800138000", ["+8613800138000"]) is True
def test_exact_match_with_imessage_prefix(self):
assert _match_entry("+8613800138000", ["imessage:+8613800138000"]) is True
def test_exact_match_with_sms_prefix(self):
assert _match_entry("+8613800138000", ["sms:+8613800138000"]) is True
def test_no_match(self):
assert _match_entry("+8613800138000", ["+8613900139000"]) is False
def test_email_match(self):
assert _match_entry("test@example.com", ["test@example.com"]) is True
def test_chat_guid_match(self):
assert _match_entry("iMessage;-;group001", ["chat_guid:iMessage;-;group001"]) is True
def test_empty_allow_list(self):
assert _match_entry("+8613800138000", []) is False
class TestSecurityCheckResult:
def test_allowed(self):
r = SecurityCheckResult(allowed=True)
assert r.allowed is True
assert r.reject_reason is None
assert r.reply is None
def test_blocked_with_reason(self):
r = SecurityCheckResult(allowed=False, reject_reason="not_in_allowlist", reply="Unauthorized")
assert r.allowed is False
assert r.reject_reason == "not_in_allowlist"
assert r.reply == "Unauthorized"
class TestIMessageSecurityPolicy:
def make_policy(self, **overrides):
config = {
"dmPolicy": "pairing",
"groupPolicy": "allowlist",
"allowFrom": ["+8613800138000"],
"groupAllowFrom": ["iMessage;-;group001"],
**overrides,
}
return IMessageSecurityPolicy(config)
def test_initialization(self):
policy = self.make_policy()
assert policy.dm_policy == DmPolicy.PAIRING
assert policy.group_policy == GroupPolicy.ALLOWLIST
assert len(policy.allow_list) > 0
assert len(policy.group_allow_list) > 0
def test_check_dm_open(self):
policy = self.make_policy(dmPolicy="open")
result = policy.check_dm_access("+8613900139000")
assert result.allowed is True
def test_check_dm_disabled(self):
policy = self.make_policy(dmPolicy="disabled")
result = policy.check_dm_access("+8613800138000")
assert result.allowed is False
assert "disabled" in str(result.reject_reason)
def test_check_dm_allowlist_allowed(self):
policy = self.make_policy(dmPolicy="allowlist", allowFrom=["+8613800138000"])
result = policy.check_dm_access("+8613800138000")
assert result.allowed is True
def test_check_dm_allowlist_blocked(self):
policy = self.make_policy(dmPolicy="allowlist", allowFrom=["+8613800138000"])
result = policy.check_dm_access("+8613900139000")
assert result.allowed is False
assert "not_in_dm_allowlist" in str(result.reject_reason)
def test_check_dm_pairing(self):
policy = self.make_policy(dmPolicy="pairing")
result = policy.check_dm_access("+8613900139000")
assert result.allowed is True
def test_check_group_open(self):
policy = self.make_policy(groupPolicy="open")
result = policy.check_group_access("iMessage;-;group002")
assert result.allowed is True
def test_check_group_disabled(self):
policy = self.make_policy(groupPolicy="disabled")
result = policy.check_group_access("iMessage;-;group001")
assert result.allowed is False
def test_check_group_allowlist_allowed(self):
policy = self.make_policy(groupPolicy="allowlist", groupAllowFrom=["iMessage;-;group001"])
result = policy.check_group_access("iMessage;-;group001")
assert result.allowed is True
def test_check_group_allowlist_blocked(self):
policy = self.make_policy(groupPolicy="allowlist", groupAllowFrom=["iMessage;-;group001"])
result = policy.check_group_access("iMessage;-;group002")
assert result.allowed is False
def test_check_group_override_disabled(self):
policy = self.make_policy(
groupPolicy="open",
groups={"iMessage;-;group001": {"enabled": False}},
)
result = policy.check_group_access("iMessage;-;group001")
assert result.allowed is False
def test_check_mention_required_not_required(self):
policy = self.make_policy(requireMention=False)
result = policy.check_mention_required("iMessage;-;group001", None, "")
assert result.allowed is True
def test_check_mention_required_bot_mentioned(self):
policy = self.make_policy(requireMention=True)
from yuxi.channels.models import MentionsInfo
mentions = MentionsInfo(mentioned_user_ids=["+8613800138000"])
result = policy.check_mention_required(
"iMessage;-;group001", mentions, "+8613800138000"
)
assert result.allowed is True
def test_check_mention_required_bot_not_mentioned(self):
policy = self.make_policy(requireMention=True)
from yuxi.channels.models import MentionsInfo
mentions = MentionsInfo(mentioned_user_ids=["+8613900139000"])
result = policy.check_mention_required(
"iMessage;-;group001", mentions, "+8613800138000"
)
assert result.allowed is False
def test_add_to_allow_list(self):
policy = self.make_policy(allowFrom=["+8613800138000"])
policy.add_to_allow_list("+8613900139000")
assert "8613900139000" in str(policy.allow_list)
def test_add_duplicate_noop(self):
policy = self.make_policy(allowFrom=["+8613800138000"])
length_before = len(policy.allow_list)
policy.add_to_allow_list("+8613800138000")
assert len(policy.allow_list) == length_before
def test_remove_from_allow_list(self):
policy = self.make_policy(allowFrom=["+8613800138000", "+8613900139000"])
assert policy.remove_from_allow_list("+8613800138000") is True
assert policy.remove_from_allow_list("nonexistent") is False
def test_add_to_group_allow_list(self):
policy = self.make_policy(groupAllowFrom=[])
policy.add_to_group_allow_list("iMessage;-;group_new")
assert "iMessage;-;group_new" in policy.group_allow_list
def test_remove_from_group_allow_list(self):
policy = self.make_policy(
groupAllowFrom=["iMessage;-;group001", "iMessage;-;group002"]
)
assert policy.remove_from_group_allow_list("iMessage;-;group001") is True
assert policy.remove_from_group_allow_list("nonexistent") is False
def test_collect_warnings_dm_open(self):
policy = self.make_policy(dmPolicy="open")
warnings = policy.collect_warnings()
assert any("open" in w.lower() for w in warnings)
def test_collect_warnings_allowlist_empty(self):
policy = self.make_policy(dmPolicy="allowlist", allowFrom=[])
warnings = policy.collect_warnings()
assert any("empty" in w.lower() for w in warnings)
def test_collect_warnings_group_allowlist_empty(self):
policy = self.make_policy(groupPolicy="allowlist", groupAllowFrom=[])
warnings = policy.collect_warnings()
assert any("empty" in w.lower() for w in warnings)
def test_collect_warnings_group_open(self):
policy = self.make_policy(groupPolicy="open", groupAllowFrom=[])
warnings = policy.collect_warnings()
assert len(warnings) >= 1
def test_collect_warnings_fallback_enabled(self):
policy = self.make_policy(groupAllowFromFallback=True)
warnings = policy.collect_warnings()
assert any("fallback" in w.lower() for w in warnings)
def test_resolve_runtime_group_policy_explicit(self):
policy = self.make_policy(
groups={"iMessage;-;group001": {"group_policy": "open"}}
)
result = policy.resolve_runtime_group_policy("iMessage;-;group001")
assert result == GroupPolicy.OPEN
def test_resolve_runtime_group_policy_default(self):
policy = self.make_policy(groupPolicy="allowlist")
result = policy.resolve_runtime_group_policy("iMessage;-;nonexistent")
assert result == GroupPolicy.ALLOWLIST
def test_resolve_pinned_dm_owner(self):
policy = self.make_policy(allowFrom=["+8613800138000"])
owner = policy.resolve_pinned_dm_owner()
assert owner == "8613800138000"
def test_resolve_pinned_dm_owner_with_prefix(self):
policy = self.make_policy(allowFrom=["imsg:+8613800138000"])
owner = policy.resolve_pinned_dm_owner()
assert owner is not None
def test_context_visibility_open(self):
policy = self.make_policy(contextVisibilityMode="open")
assert policy.evaluate_context_visibility(
"iMessage;+;chat001", "+8613900139000"
) is True
def test_context_visibility_disabled(self):
policy = self.make_policy(contextVisibilityMode="disabled")
assert policy.evaluate_context_visibility(
"iMessage;+;chat001", "+8613800138000"
) is False
def test_context_visibility_allowlist_matched(self):
policy = self.make_policy(
contextVisibilityMode="allowlist", allowFrom=["+8613800138000"]
)
assert policy.evaluate_context_visibility(
"iMessage;+;chat001", "+8613800138000"
) is True
def test_context_visibility_allowlist_not_matched(self):
policy = self.make_policy(
contextVisibilityMode="allowlist", allowFrom=["+8613800138000"]
)
assert policy.evaluate_context_visibility(
"iMessage;+;chat001", "+8613900139000"
) is False
def test_require_mention_property(self):
policy = self.make_policy(requireMention=True)
assert policy.require_mention is True
def test_should_update_last_route(self):
policy = self.make_policy(allowFrom=["+8613800138000"])
assert policy.should_update_last_route("+8613800138000") is True
def test_should_update_last_route_no_owner(self):
policy = self.make_policy(allowFrom=[])
assert policy.should_update_last_route("+8613800138000") is True