ForcePilot/backend/test/unit/channel/security/test_checker_registry.py
Kris bab30f2715
Some checks failed
Deploy VitePress site to Pages / build (push) Has been cancelled
Ruff Format Check / Ruff Format & Lint (push) Has been cancelled
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
feat:0715
2026-07-15 12:30:58 +08:00

193 lines
5.9 KiB
Python

from __future__ import annotations
from unittest.mock import MagicMock
import pytest
from yuxi.channel.security.registry import (
SecurityCheckerRegistry,
SecurityContext,
)
class _DummyChecker:
name = "dummy"
default_priority = 100
async def check(self, ctx: SecurityContext) -> object | None:
return None
class _AlphaChecker:
name = "alpha"
default_priority = 200
async def check(self, ctx: SecurityContext) -> object | None:
return None
class _BetaChecker:
name = "beta"
default_priority = 100
async def check(self, ctx: SecurityContext) -> object | None:
return None
@pytest.fixture
def registry() -> SecurityCheckerRegistry:
return SecurityCheckerRegistry()
@pytest.mark.unit
class TestSecurityCheckerRegistry:
def test_register_and_unregister(self, registry: SecurityCheckerRegistry) -> None:
checker = _DummyChecker()
registry.register(checker)
assert registry.get("dummy") is checker
removed = registry.unregister("dummy")
assert removed is checker
assert registry.get("dummy") is None
def test_register_requires_name(self, registry: SecurityCheckerRegistry) -> None:
class NoName:
name = ""
async def check(self, ctx: SecurityContext) -> None:
return None
with pytest.raises(ValueError):
registry.register(NoName())
def test_default_chain_sorted_by_priority(self, registry: SecurityCheckerRegistry) -> None:
alpha = _AlphaChecker()
beta = _BetaChecker()
registry.register(alpha)
registry.register(beta)
chain = registry.resolve_chain({})
assert [c.name for c in chain] == ["beta", "alpha"]
def test_configured_chain_enabled_and_sorted(self, registry: SecurityCheckerRegistry) -> None:
alpha = _AlphaChecker()
beta = _BetaChecker()
registry.register(alpha)
registry.register(beta)
config = {
"channel_type": "feishu",
"account_id": "a1",
"security_checkers": [
{"name": "alpha", "enabled": True, "priority": 50},
{"name": "beta", "enabled": True, "priority": 150},
],
}
chain = registry.resolve_chain(config)
assert [c.name for c in chain] == ["alpha", "beta"]
def test_configured_chain_uses_default_priority_when_missing(self, registry: SecurityCheckerRegistry) -> None:
alpha = _AlphaChecker()
beta = _BetaChecker()
registry.register(alpha)
registry.register(beta)
config = {
"security_checkers": [
{"name": "alpha", "enabled": True},
],
}
chain = registry.resolve_chain(config)
assert [c.name for c in chain] == ["alpha"]
def test_configured_chain_disabled_ignored(self, registry: SecurityCheckerRegistry) -> None:
alpha = _AlphaChecker()
beta = _BetaChecker()
registry.register(alpha)
registry.register(beta)
config = {
"security_checkers": [
{"name": "alpha", "enabled": False, "priority": 1},
{"name": "beta", "enabled": True, "priority": 2},
],
}
chain = registry.resolve_chain(config)
assert [c.name for c in chain] == ["beta"]
def test_configured_chain_unknown_name_warns_and_ignored(
self, registry: SecurityCheckerRegistry, monkeypatch: pytest.MonkeyPatch
) -> None:
registry.register(_AlphaChecker())
warn_mock = MagicMock()
monkeypatch.setattr("yuxi.channel.security.registry.logger", type("L", (), {"warning": warn_mock})())
config = {
"security_checkers": [
{"name": "unknown", "enabled": True},
{"name": "alpha", "enabled": True},
],
}
chain = registry.resolve_chain(config)
assert [c.name for c in chain] == ["alpha"]
warn_mock.assert_called_once()
def test_resolve_chain_caches_by_config_hash(self, registry: SecurityCheckerRegistry) -> None:
checker = _AlphaChecker()
registry.register(checker)
config = {
"channel_type": "feishu",
"account_id": "a1",
"security_checkers": [
{"name": "alpha", "enabled": True, "priority": 50},
],
}
chain1 = registry.resolve_chain(config)
chain2 = registry.resolve_chain(config)
assert chain1 is chain2
def test_invalidate_removes_cache_for_account(self, registry: SecurityCheckerRegistry) -> None:
checker = _AlphaChecker()
registry.register(checker)
config1 = {
"channel_type": "feishu",
"account_id": "a1",
"security_checkers": [{"name": "alpha", "enabled": True}],
}
config2 = {
"channel_type": "feishu",
"account_id": "a2",
"security_checkers": [{"name": "alpha", "enabled": True}],
}
chain1 = registry.resolve_chain(config1)
chain2 = registry.resolve_chain(config2)
registry.invalidate("feishu", "a1")
assert registry.resolve_chain(config1) is not chain1
assert registry.resolve_chain(config2) is chain2
async def test_start_all_and_stop_all(self, registry: SecurityCheckerRegistry) -> None:
start = MagicMock()
stop = MagicMock()
class _LifecycleChecker:
name = "lifecycle"
default_priority = 0
async def check(self, ctx: SecurityContext) -> None:
return None
async def start(self) -> None:
start()
async def stop(self) -> None:
stop()
registry.register(_LifecycleChecker())
await registry.start_all()
await registry.stop_all()
start.assert_called_once()
stop.assert_called_once()