acb / tests /test_error_handler.py
Kagan Tek
merge
79d4fd5
Raw
History Blame Contribute Delete
15.5 kB
# Tests for error handler module
import pytest
from unittest.mock import Mock, patch
from src.integration.error_handler import (
ErrorHandler,
ErrorType,
ErrorContext,
get_error_handler,
reset_error_handler,
handle_error,
)
class TestErrorType:
"""Tests for ErrorType enum."""
def test_error_type_values(self):
"""Test error type enum values."""
assert ErrorType.DATABASE_CONNECTION.value == "database_connection"
assert ErrorType.DATABASE_QUERY.value == "database_query"
assert ErrorType.SQL_VALIDATION.value == "sql_validation"
assert ErrorType.SQL_INJECTION.value == "sql_injection"
assert ErrorType.DOCUMENT_RETRIEVAL.value == "document_retrieval"
assert ErrorType.LLM_GENERATION.value == "llm_generation"
assert ErrorType.TIMEOUT.value == "timeout"
assert ErrorType.UNKNOWN.value == "unknown"
def test_error_type_membership(self):
"""Test all expected error types exist."""
types = [t.value for t in ErrorType]
assert "database_connection" in types
assert "sql_injection" in types
assert "timeout" in types
assert "unknown" in types
class TestErrorContext:
"""Tests for ErrorContext dataclass."""
def test_error_context_creation(self):
"""Test basic error context creation."""
context = ErrorContext(
error_type=ErrorType.DATABASE_QUERY,
message="Query failed",
)
assert context.error_type == ErrorType.DATABASE_QUERY
assert context.message == "Query failed"
assert context.original_error is None
assert context.query is None
def test_error_context_with_all_fields(self):
"""Test error context with all fields."""
original = ValueError("test error")
context = ErrorContext(
error_type=ErrorType.SQL_VALIDATION,
message="Validation failed",
original_error=original,
query="SELECT * FROM table",
additional_info={"key": "value"},
)
assert context.original_error == original
assert context.query == "SELECT * FROM table"
assert context.additional_info["key"] == "value"
def test_error_context_to_dict(self):
"""Test conversion to dictionary."""
original = ValueError("test")
context = ErrorContext(
error_type=ErrorType.TIMEOUT,
message="Timed out",
original_error=original,
query="test query",
additional_info={"timeout": 30},
)
result = context.to_dict()
assert result["error_type"] == "timeout"
assert result["message"] == "Timed out"
assert result["query"] == "test query"
assert result["original_error"] == "test"
assert result["additional_info"]["timeout"] == 30
def test_error_context_to_dict_no_original(self):
"""Test to_dict with no original error."""
context = ErrorContext(
error_type=ErrorType.UNKNOWN,
message="Unknown error",
)
result = context.to_dict()
assert result["original_error"] is None
class TestErrorHandler:
"""Tests for ErrorHandler class."""
@pytest.fixture(autouse=True)
def reset_handler(self):
"""Reset error handler before each test."""
reset_error_handler()
yield
reset_error_handler()
def test_error_handler_creation(self):
"""Test basic handler creation."""
handler = ErrorHandler()
assert handler.enable_fallback is True
assert all(count == 0 for count in handler.error_counts.values())
def test_error_handler_fallback_disabled(self):
"""Test handler with fallback disabled."""
handler = ErrorHandler(enable_fallback=False)
assert handler.enable_fallback is False
def test_set_enable_fallback(self):
"""Test setting fallback enabled state."""
handler = ErrorHandler()
handler.enable_fallback = False
assert handler.enable_fallback is False
def test_error_counts_returns_copy(self):
"""Test that error_counts returns a copy."""
handler = ErrorHandler()
counts = handler.error_counts
counts[ErrorType.UNKNOWN] = 999
assert handler.error_counts[ErrorType.UNKNOWN] == 0
def test_reset_counts(self):
"""Test resetting error counts."""
handler = ErrorHandler()
handler.handle(ValueError("test"))
assert sum(handler.error_counts.values()) > 0
handler.reset_counts()
assert all(count == 0 for count in handler.error_counts.values())
def test_handle_basic_error(self):
"""Test handling basic error."""
handler = ErrorHandler()
error = ValueError("test error")
context = handler.handle(error)
assert isinstance(context, ErrorContext)
assert context.original_error == error
assert context.message != ""
def test_handle_with_error_type(self):
"""Test handling error with specified type."""
handler = ErrorHandler()
error = Exception("test")
context = handler.handle(error, error_type=ErrorType.DATABASE_CONNECTION)
assert context.error_type == ErrorType.DATABASE_CONNECTION
assert handler.error_counts[ErrorType.DATABASE_CONNECTION] == 1
def test_handle_with_query(self):
"""Test handling error with query context."""
handler = ErrorHandler()
error = Exception("test")
context = handler.handle(error, query="SELECT * FROM users")
assert context.query == "SELECT * FROM users"
def test_handle_increments_count(self):
"""Test that handling increments error count."""
handler = ErrorHandler()
handler.handle(Exception("test1"), ErrorType.TIMEOUT)
handler.handle(Exception("test2"), ErrorType.TIMEOUT)
handler.handle(Exception("test3"), ErrorType.DATABASE_QUERY)
assert handler.error_counts[ErrorType.TIMEOUT] == 2
assert handler.error_counts[ErrorType.DATABASE_QUERY] == 1
def test_get_user_message(self):
"""Test getting user-friendly messages."""
handler = ErrorHandler()
message = handler.get_user_message(ErrorType.DATABASE_CONNECTION)
assert "bağlantı" in message.lower()
message = handler.get_user_message(ErrorType.TIMEOUT)
assert "zaman" in message.lower()
def test_get_most_frequent_error_none(self):
"""Test most frequent error when no errors."""
handler = ErrorHandler()
assert handler.get_most_frequent_error() is None
def test_get_most_frequent_error(self):
"""Test getting most frequent error type."""
handler = ErrorHandler()
handler.handle(Exception("1"), ErrorType.TIMEOUT)
handler.handle(Exception("2"), ErrorType.TIMEOUT)
handler.handle(Exception("3"), ErrorType.TIMEOUT)
handler.handle(Exception("4"), ErrorType.DATABASE_QUERY)
assert handler.get_most_frequent_error() == ErrorType.TIMEOUT
def test_register_fallback(self):
"""Test registering fallback handler."""
handler = ErrorHandler()
fallback_fn = Mock(return_value="fallback result")
handler.register_fallback(ErrorType.DATABASE_QUERY, fallback_fn)
assert ErrorType.DATABASE_QUERY in handler._fallback_handlers
def test_execute_with_fallback_success(self):
"""Test execute_with_fallback on success."""
handler = ErrorHandler()
def success_fn():
return "success"
result = handler.execute_with_fallback(
success_fn,
ErrorType.DATABASE_QUERY,
fallback_value="fallback",
)
assert result == "success"
def test_execute_with_fallback_uses_fallback_value(self):
"""Test execute_with_fallback uses fallback value on error."""
handler = ErrorHandler()
def failing_fn():
raise ValueError("test error")
result = handler.execute_with_fallback(
failing_fn,
ErrorType.DATABASE_QUERY,
fallback_value="fallback result",
)
assert result == "fallback result"
def test_execute_with_fallback_uses_registered_handler(self):
"""Test execute_with_fallback uses registered handler."""
handler = ErrorHandler()
fallback_fn = Mock(return_value="custom fallback")
handler.register_fallback(ErrorType.DATABASE_QUERY, fallback_fn)
def failing_fn():
raise ValueError("test")
result = handler.execute_with_fallback(
failing_fn,
ErrorType.DATABASE_QUERY,
fallback_value="default",
query="test query",
)
assert result == "custom fallback"
fallback_fn.assert_called_once()
def test_execute_with_fallback_disabled(self):
"""Test execute_with_fallback when fallback disabled."""
handler = ErrorHandler(enable_fallback=False)
fallback_fn = Mock(return_value="custom")
handler.register_fallback(ErrorType.DATABASE_QUERY, fallback_fn)
def failing_fn():
raise ValueError("test")
result = handler.execute_with_fallback(
failing_fn,
ErrorType.DATABASE_QUERY,
fallback_value="default",
)
assert result == "default"
fallback_fn.assert_not_called()
class TestErrorTypeDetection:
"""Tests for automatic error type detection."""
@pytest.fixture(autouse=True)
def reset_handler(self):
"""Reset error handler before each test."""
reset_error_handler()
yield
reset_error_handler()
def test_detect_connection_error(self):
"""Test detection of connection errors."""
handler = ErrorHandler()
error = Exception("Connection refused")
context = handler.handle(error)
assert context.error_type == ErrorType.DATABASE_CONNECTION
def test_detect_timeout_error(self):
"""Test detection of timeout errors."""
handler = ErrorHandler()
error = Exception("Query timed out after 30 seconds")
context = handler.handle(error)
assert context.error_type == ErrorType.TIMEOUT
def test_detect_injection_error(self):
"""Test detection of SQL injection errors."""
handler = ErrorHandler()
error = Exception("SQL injection detected, forbidden pattern")
context = handler.handle(error)
assert context.error_type == ErrorType.SQL_INJECTION
def test_detect_validation_error(self):
"""Test detection of validation errors."""
handler = ErrorHandler()
error = Exception("Validation failed: invalid data")
context = handler.handle(error)
assert context.error_type == ErrorType.SQL_VALIDATION
def test_detect_retrieval_error(self):
"""Test detection of document retrieval errors."""
handler = ErrorHandler()
error = Exception("Failed to retrieve documents")
context = handler.handle(error)
assert context.error_type == ErrorType.DOCUMENT_RETRIEVAL
def test_detect_llm_error(self):
"""Test detection of LLM errors."""
handler = ErrorHandler()
error = Exception("LLM generation failed")
context = handler.handle(error)
assert context.error_type == ErrorType.LLM_GENERATION
def test_detect_unknown_error(self):
"""Test detection of unknown errors."""
handler = ErrorHandler()
error = Exception("Some random error")
context = handler.handle(error)
assert context.error_type == ErrorType.UNKNOWN
class TestErrorHandlerSingleton:
"""Tests for error handler singleton functions."""
@pytest.fixture(autouse=True)
def reset_singleton(self):
"""Reset singleton before each test."""
reset_error_handler()
yield
reset_error_handler()
def test_get_error_handler_creates_instance(self):
"""Test get_error_handler creates new instance."""
handler = get_error_handler()
assert handler is not None
assert isinstance(handler, ErrorHandler)
def test_get_error_handler_returns_same_instance(self):
"""Test get_error_handler returns singleton."""
handler1 = get_error_handler()
handler2 = get_error_handler()
assert handler1 is handler2
def test_reset_error_handler(self):
"""Test reset_error_handler clears singleton."""
handler1 = get_error_handler()
reset_error_handler()
handler2 = get_error_handler()
assert handler1 is not handler2
def test_handle_error_convenience_function(self):
"""Test handle_error convenience function."""
error = ValueError("test")
context = handle_error(error, ErrorType.DATABASE_QUERY, "test query")
assert context.error_type == ErrorType.DATABASE_QUERY
assert context.query == "test query"
def test_handle_error_uses_singleton(self):
"""Test handle_error uses singleton handler."""
handle_error(ValueError("test1"), ErrorType.TIMEOUT)
handle_error(ValueError("test2"), ErrorType.TIMEOUT)
handler = get_error_handler()
assert handler.error_counts[ErrorType.TIMEOUT] == 2
class TestErrorMessages:
"""Tests for error message localization."""
def test_all_error_types_have_messages(self):
"""Test all error types have user messages."""
handler = ErrorHandler()
for error_type in ErrorType:
message = handler.get_user_message(error_type)
assert message != ""
assert len(message) > 10
def test_messages_are_turkish(self):
"""Test messages contain Turkish text."""
handler = ErrorHandler()
message = handler.get_user_message(ErrorType.DATABASE_CONNECTION)
turkish_chars = set("ğüşıöçĞÜŞİÖÇ")
message_lower = message.lower()
turkish_words = ["lütfen", "veritabanı", "sorgu", "hata", "işlem"]
has_turkish = any(word in message_lower for word in turkish_words)
assert has_turkish
class TestErrorContext:
"""Additional tests for error context."""
def test_error_context_includes_traceback(self):
"""Test error context includes traceback in additional_info."""
handler = ErrorHandler()
try:
raise ValueError("test error")
except Exception as e:
context = handler.handle(e)
assert context.additional_info is not None
assert "traceback" in context.additional_info
assert "ValueError" in context.additional_info["traceback"]