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
This commit is contained in:
142
tests/test_verification.py
Normal file
142
tests/test_verification.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user