# 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"]