Add comprehensive test suite with 97% code coverage
Implemented full test coverage for core bot components: Test Coverage: - MentionDetector: 98% (28 tests) - mention detection, nickname handling, content extraction - Logger: 98% (15 tests) - file logging, log levels, unicode support - ConversationMemory: 91% (25 tests) - history management, time filtering, JSON persistence - PerplexityProvider: 100% (22 tests) - API calls, error handling, statistics - Config: 100% (26 tests) - env loading, validation, JSON config Infrastructure: - Added pytest, pytest-asyncio, pytest-mock, pytest-cov to requirements.txt - Created pytest.ini with coverage configuration - Created .coveragerc to exclude non-production files - Added conftest.py with shared fixtures and test isolation Test Features: - 116 total tests, all passing - Isolated test environment (clean env vars, logging handlers) - Async testing support for PerplexityProvider - Mocked HTTP requests to avoid real API calls - Comprehensive edge case coverage - Unicode/German character support testing Total: 97% code coverage across 273 statements
This commit is contained in:
parent
db467d774c
commit
f6813b5fa5
10 changed files with 1760 additions and 0 deletions
172
tests/conftest.py
Normal file
172
tests/conftest.py
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
"""
|
||||
Shared pytest fixtures for Eugen Bot tests
|
||||
"""
|
||||
import pytest
|
||||
import tempfile
|
||||
import shutil
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clean_env():
|
||||
"""Clean environment variables and logging handlers before each test"""
|
||||
import logging
|
||||
|
||||
# Clean environment variables
|
||||
env_vars_to_clean = [
|
||||
'TWITCH_OAUTH_TOKEN', 'TWITCH_CHANNEL', 'TWITCH_BOT_NICKNAME',
|
||||
'PERPLEXITY_API_KEY', 'PERPLEXITY_MODEL', 'MAX_TOKENS',
|
||||
'DEBUG_MODE', 'AUTO_RECONNECT', 'RECONNECT_DELAY',
|
||||
'CONTEXT_RETENTION_HOURS', 'DATA_DIR', 'LOG_DIR'
|
||||
]
|
||||
|
||||
# Save original values
|
||||
original_env = {key: os.environ.get(key) for key in env_vars_to_clean}
|
||||
|
||||
# Clear them
|
||||
for key in env_vars_to_clean:
|
||||
os.environ.pop(key, None)
|
||||
|
||||
yield
|
||||
|
||||
# Restore original values
|
||||
for key in env_vars_to_clean:
|
||||
os.environ.pop(key, None)
|
||||
if original_env[key] is not None:
|
||||
os.environ[key] = original_env[key]
|
||||
|
||||
# Clean up logging handlers to prevent test interference
|
||||
for logger_name in ['eugen_main', 'eugen_api']:
|
||||
logger = logging.getLogger(logger_name)
|
||||
logger.handlers.clear()
|
||||
logger.setLevel(logging.NOTSET)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_dir():
|
||||
"""Create a temporary directory for tests"""
|
||||
temp_path = Path(tempfile.mkdtemp())
|
||||
yield temp_path
|
||||
# Cleanup
|
||||
if temp_path.exists():
|
||||
shutil.rmtree(temp_path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_env_file(temp_dir):
|
||||
"""Create a mock .env file for testing"""
|
||||
env_path = temp_dir / ".env"
|
||||
env_content = """TWITCH_OAUTH_TOKEN=oauth:test_token_12345
|
||||
TWITCH_CHANNEL=#test_channel
|
||||
TWITCH_BOT_NICKNAME=TestBot
|
||||
PERPLEXITY_API_KEY=pplx-test-key-12345
|
||||
PERPLEXITY_MODEL=sonar-pro
|
||||
MAX_TOKENS=450
|
||||
DEBUG_MODE=true
|
||||
AUTO_RECONNECT=true
|
||||
RECONNECT_DELAY=10
|
||||
CONTEXT_RETENTION_HOURS=1
|
||||
"""
|
||||
env_path.write_text(env_content)
|
||||
return env_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_json(temp_dir):
|
||||
"""Create a mock config.json file for testing"""
|
||||
config_path = temp_dir / "config.json"
|
||||
config_data = {
|
||||
"bot_name": "TestBot",
|
||||
"model": "sonar-pro",
|
||||
"max_tokens": 450,
|
||||
"debug_mode": True,
|
||||
"auto_reconnect": True,
|
||||
"reconnect_delay": 10,
|
||||
"context_retention_hours": 1
|
||||
}
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(config_path, 'w') as f:
|
||||
json.dump(config_data, f)
|
||||
return config_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_conversation_history():
|
||||
"""Sample conversation history for testing"""
|
||||
now = datetime.now()
|
||||
return [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello bot!",
|
||||
"timestamp": (now - timedelta(minutes=10)).isoformat()
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Hi! How can I help?",
|
||||
"timestamp": (now - timedelta(minutes=9)).isoformat()
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What's the weather?",
|
||||
"timestamp": (now - timedelta(minutes=5)).isoformat()
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I don't have weather info.",
|
||||
"timestamp": (now - timedelta(minutes=4)).isoformat()
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def old_conversation_history():
|
||||
"""Old conversation history (beyond retention) for testing"""
|
||||
old_time = datetime.now() - timedelta(hours=2)
|
||||
return [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Old message",
|
||||
"timestamp": old_time.isoformat()
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Old response",
|
||||
"timestamp": (old_time + timedelta(minutes=1)).isoformat()
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_perplexity_response():
|
||||
"""Mock successful Perplexity API response"""
|
||||
return {
|
||||
"id": "test-completion-123",
|
||||
"model": "sonar-pro",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "This is a test response from the AI."
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 50,
|
||||
"completion_tokens": 20,
|
||||
"total_tokens": 70
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_messages():
|
||||
"""Sample messages for API testing"""
|
||||
return [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Hello, how are you?"}
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue