51 lines
2.4 KiB
Python
51 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
from yuxi.channel.security.bot_loop import BotLoopDetector
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestBotLoopDetector:
|
|
def test_defaults_allow_within_limit(self) -> None:
|
|
detector = BotLoopDetector()
|
|
for _ in range(3):
|
|
assert detector.is_allowed("user_1", is_bot=True) is True
|
|
|
|
def test_blocks_when_bot_replies_exceed_limit(self) -> None:
|
|
detector = BotLoopDetector(window_size=5, max_bot_replies=2)
|
|
assert detector.is_allowed("user_1", is_bot=True) is True
|
|
assert detector.is_allowed("user_1", is_bot=True) is True
|
|
assert detector.is_allowed("user_1", is_bot=True) is False
|
|
|
|
def test_non_bot_messages_do_not_increase_bot_count(self) -> None:
|
|
detector = BotLoopDetector(window_size=5, max_bot_replies=2)
|
|
assert detector.is_allowed("user_1", is_bot=True) is True
|
|
assert detector.is_allowed("user_1", is_bot=False) is True
|
|
assert detector.is_allowed("user_1", is_bot=True) is True
|
|
assert detector.is_allowed("user_1", is_bot=True) is False
|
|
|
|
def test_window_slides_with_maxlen(self) -> None:
|
|
detector = BotLoopDetector(window_size=3, max_bot_replies=2)
|
|
assert detector.is_allowed("user_1", is_bot=True) is True
|
|
assert detector.is_allowed("user_1", is_bot=True) is True
|
|
# 3rd bot still allowed because limit is 2 and count is 2
|
|
assert detector.is_allowed("user_1", is_bot=True) is False
|
|
# Adding non-bot drops oldest bot, count becomes 2 -> allowed
|
|
assert detector.is_allowed("user_1", is_bot=False) is True
|
|
|
|
def test_per_sender_isolation(self) -> None:
|
|
detector = BotLoopDetector(window_size=3, max_bot_replies=1)
|
|
assert detector.is_allowed("user_1", is_bot=True) is True
|
|
assert detector.is_allowed("user_2", is_bot=True) is True
|
|
assert detector.is_allowed("user_1", is_bot=True) is False
|
|
assert detector.is_allowed("user_2", is_bot=True) is False
|
|
|
|
def test_all_non_bot_allowed(self) -> None:
|
|
detector = BotLoopDetector(window_size=3, max_bot_replies=0)
|
|
for _ in range(5):
|
|
assert detector.is_allowed("user_1", is_bot=False) is True
|
|
|
|
def test_zero_max_bot_replies_blocks_any_bot(self) -> None:
|
|
detector = BotLoopDetector(window_size=3, max_bot_replies=0)
|
|
assert detector.is_allowed("user_1", is_bot=True) is False
|