Files
GuardDen/tests/test_verification.py
latte 4e16777f25 Implement GuardDen Discord moderation bot
Features:
- Core moderation: warn, kick, ban, timeout, strike system
- Automod: banned words filter, scam detection, anti-spam, link filtering
- AI moderation: Claude/OpenAI integration, NSFW detection, phishing analysis
- Verification system: button, captcha, math, emoji challenges
- Rate limiting system with configurable scopes
- Event logging: joins, leaves, message edits/deletes, voice activity
- Per-guild configuration with caching
- Docker deployment support

Bug fixes applied:
- Fixed await on session.delete() in guild_config.py
- Fixed memory leak in AI moderation message tracking (use deque)
- Added error handling to bot shutdown
- Added error handling to timeout command
- Removed unused Literal import
- Added prefix validation
- Added image analysis limit (3 per message)
- Fixed test mock for SQLAlchemy model
2026-01-16 19:27:48 +01:00

143 lines
4.9 KiB
Python

"""Tests for verification service."""
import pytest
from guardden.services.verification import (
ButtonChallengeGenerator,
CaptchaChallengeGenerator,
ChallengeType,
EmojiChallengeGenerator,
MathChallengeGenerator,
VerificationService,
)
class TestChallengeGenerators:
"""Tests for challenge generators."""
def test_button_challenge(self) -> None:
"""Test button challenge generation."""
gen = ButtonChallengeGenerator()
challenge = gen.generate()
assert challenge.challenge_type == ChallengeType.BUTTON
assert challenge.answer == "verified"
def test_captcha_challenge(self) -> None:
"""Test captcha challenge generation."""
gen = CaptchaChallengeGenerator(length=6)
challenge = gen.generate()
assert challenge.challenge_type == ChallengeType.CAPTCHA
assert len(challenge.answer) == 6
assert challenge.answer.isalnum()
def test_math_challenge(self) -> None:
"""Test math challenge generation."""
gen = MathChallengeGenerator()
challenge = gen.generate()
assert challenge.challenge_type == ChallengeType.MATH
# Answer should be a number
assert challenge.answer.lstrip("-").isdigit()
def test_emoji_challenge(self) -> None:
"""Test emoji challenge generation."""
gen = EmojiChallengeGenerator()
challenge = gen.generate()
assert challenge.challenge_type == ChallengeType.EMOJI
assert len(challenge.options) > 0
assert challenge.answer in challenge.options
class TestVerificationService:
"""Tests for verification service."""
@pytest.fixture
def service(self) -> VerificationService:
return VerificationService()
def test_create_challenge(self, service: VerificationService) -> None:
"""Test creating a challenge."""
pending = service.create_challenge(
user_id=123,
guild_id=456,
challenge_type=ChallengeType.BUTTON,
)
assert pending.user_id == 123
assert pending.guild_id == 456
assert pending.challenge.challenge_type == ChallengeType.BUTTON
def test_get_pending(self, service: VerificationService) -> None:
"""Test retrieving pending verification."""
service.create_challenge(123, 456, ChallengeType.BUTTON)
pending = service.get_pending(456, 123)
assert pending is not None
assert pending.user_id == 123
# Non-existent should return None
assert service.get_pending(456, 999) is None
def test_verify_correct(self, service: VerificationService) -> None:
"""Test successful verification."""
service.create_challenge(123, 456, ChallengeType.BUTTON)
success, message = service.verify(456, 123, "verified")
assert success is True
assert "successful" in message.lower()
# Should be removed after success
assert service.get_pending(456, 123) is None
def test_verify_incorrect(self, service: VerificationService) -> None:
"""Test failed verification."""
service.create_challenge(123, 456, ChallengeType.BUTTON)
success, message = service.verify(456, 123, "wrong")
assert success is False
assert "incorrect" in message.lower()
# Should still exist
assert service.get_pending(456, 123) is not None
def test_verify_max_attempts(self, service: VerificationService) -> None:
"""Test max attempts exceeded."""
service.create_challenge(123, 456, ChallengeType.BUTTON)
# Use up all attempts
for _ in range(3):
service.verify(456, 123, "wrong")
success, message = service.verify(456, 123, "verified")
assert success is False
assert "too many" in message.lower()
def test_verify_no_pending(self, service: VerificationService) -> None:
"""Test verification with no pending challenge."""
success, message = service.verify(456, 123, "verified")
assert success is False
assert "no pending" in message.lower()
def test_cancel(self, service: VerificationService) -> None:
"""Test canceling verification."""
service.create_challenge(123, 456, ChallengeType.BUTTON)
assert service.cancel(456, 123) is True
assert service.get_pending(456, 123) is None
# Cancel non-existent returns False
assert service.cancel(456, 123) is False
def test_pending_count(self, service: VerificationService) -> None:
"""Test pending count per guild."""
service.create_challenge(1, 456, ChallengeType.BUTTON)
service.create_challenge(2, 456, ChallengeType.BUTTON)
service.create_challenge(3, 789, ChallengeType.BUTTON)
assert service.get_pending_count(456) == 2
assert service.get_pending_count(789) == 1
assert service.get_pending_count(999) == 0