"""Tests for custom exception hierarchy.""" import pytest from src.exceptions import ( ChunkParsingError, CLINotFoundError, ClaudeAPIError, ConfigurationError, EvaluationError, GlossaryMatchError, InvalidReferenceError, ModelNotFoundError, PericopeNotFoundError, QualityThresholdError, RateLimitError, StorageError, TranslationError, VerseNotFoundError, classify_claude_error, ) class TestTranslationError: """Tests for base TranslationError.""" def test_basic_creation(self): """Test basic error creation.""" error = TranslationError("Test error") assert str(error) == "Test error" assert error.details == {} def test_with_details(self): """Test error with details.""" error = TranslationError("Test error", {"key": "value"}) assert error.details == {"key": "value"} class TestClaudeAPIError: """Tests for ClaudeAPIError.""" def test_basic_creation(self): """Test basic error creation.""" error = ClaudeAPIError("API error") assert str(error) == "API error" assert error.stderr is None assert error.return_code is None def test_with_stderr_and_code(self): """Test error with stderr and return code.""" error = ClaudeAPIError("API error", stderr="Error output", return_code=1) assert error.stderr == "Error output" assert error.return_code == 1 assert error.details["stderr"] == "Error output" assert error.details["return_code"] == 1 class TestRateLimitError: """Tests for RateLimitError.""" def test_default_message(self): """Test default error message.""" error = RateLimitError() assert "Rate limit" in str(error) assert error.retry_after is None def test_with_retry_after(self): """Test error with retry_after.""" error = RateLimitError("Rate limit hit", retry_after=30.0) assert error.retry_after == 30.0 assert error.details["retry_after"] == 30.0 class TestModelNotFoundError: """Tests for ModelNotFoundError.""" def test_creation(self): """Test error creation.""" error = ModelNotFoundError("claude-invalid-model") assert "claude-invalid-model" in str(error) assert error.model == "claude-invalid-model" assert error.details["model"] == "claude-invalid-model" class TestCLINotFoundError: """Tests for CLINotFoundError.""" def test_creation(self): """Test error creation.""" error = CLINotFoundError() assert "Claude CLI not found" in str(error) assert "npm install" in str(error) class TestVerseNotFoundError: """Tests for VerseNotFoundError.""" def test_creation(self): """Test error creation.""" error = VerseNotFoundError("john", 1, 1) assert "john 1:1" in str(error) assert error.book == "john" assert error.chapter == 1 assert error.verse == 1 assert error.details == {"book": "john", "chapter": 1, "verse": 1} class TestInvalidReferenceError: """Tests for InvalidReferenceError.""" def test_basic_creation(self): """Test basic error creation.""" error = InvalidReferenceError("bad:ref") assert "bad:ref" in str(error) assert error.reference == "bad:ref" assert error.reason is None def test_with_reason(self): """Test error with reason.""" error = InvalidReferenceError("bad:ref", "Invalid format") assert "Invalid format" in str(error) assert error.reason == "Invalid format" class TestChunkParsingError: """Tests for ChunkParsingError.""" def test_basic_creation(self): """Test basic error creation.""" error = ChunkParsingError("Parse failed") assert str(error) == "Parse failed" assert error.response is None assert error.expected_verses is None def test_with_context(self): """Test error with response context.""" error = ChunkParsingError( "Parse failed", response="Not JSON", expected_verses=["1:1", "1:2"], ) assert error.response == "Not JSON" assert error.expected_verses == ["1:1", "1:2"] assert error.details["expected_verses"] == ["1:1", "1:2"] class TestPericopeNotFoundError: """Tests for PericopeNotFoundError.""" def test_creation(self): """Test error creation.""" error = PericopeNotFoundError("john-prologue") assert "john-prologue" in str(error) assert error.pericope_id == "john-prologue" class TestEvaluationError: """Tests for EvaluationError.""" def test_basic_creation(self): """Test basic error creation.""" error = EvaluationError("Eval failed") assert str(error) == "Eval failed" assert error.verse_ref is None assert error.dimension is None def test_with_context(self): """Test error with verse and dimension context.""" error = EvaluationError( "Eval failed", verse_ref="john 1:1", dimension="theological_accuracy", ) assert error.verse_ref == "john 1:1" assert error.dimension == "theological_accuracy" class TestQualityThresholdError: """Tests for QualityThresholdError.""" def test_creation(self): """Test error creation.""" error = QualityThresholdError( "Quality too low", score=65.0, threshold=70.0, ) assert "Quality too low" in str(error) assert error.score == 65.0 assert error.threshold == 70.0 assert error.dimension is None def test_with_dimension(self): """Test error with specific dimension.""" error = QualityThresholdError( "Theology score low", score=60.0, threshold=75.0, dimension="theological_accuracy", ) assert error.dimension == "theological_accuracy" class TestStorageError: """Tests for StorageError.""" def test_basic_creation(self): """Test basic error creation.""" error = StorageError("Storage failed") assert str(error) == "Storage failed" assert error.path is None assert error.operation is None def test_with_context(self): """Test error with path and operation context.""" error = StorageError( "Write failed", path="/data/file.json", operation="write", ) assert error.path == "/data/file.json" assert error.operation == "write" class TestGlossaryMatchError: """Tests for GlossaryMatchError.""" def test_basic_creation(self): """Test basic error creation.""" error = GlossaryMatchError("Match failed") assert str(error) == "Match failed" assert error.term is None def test_with_term(self): """Test error with term context.""" error = GlossaryMatchError("Match failed", term="λόγος") assert error.term == "λόγος" class TestConfigurationError: """Tests for ConfigurationError.""" def test_basic_creation(self): """Test basic error creation.""" error = ConfigurationError("Config invalid") assert str(error) == "Config invalid" assert error.config_key is None def test_with_key(self): """Test error with config key context.""" error = ConfigurationError("Invalid value", config_key="model") assert error.config_key == "model" class TestClassifyClaudeError: """Tests for classify_claude_error function.""" def test_rate_limit_detection(self): """Test rate limit error detection.""" error = classify_claude_error("Error: rate limit exceeded", 1) assert isinstance(error, RateLimitError) def test_rate_limit_detection_429(self): """Test rate limit error detection via 429.""" error = classify_claude_error("HTTP 429 Too Many Requests", 1) assert isinstance(error, RateLimitError) def test_model_error_detection(self): """Test model error detection.""" error = classify_claude_error("Model not found: invalid-model", 1) assert isinstance(error, ClaudeAPIError) assert "Model error" in str(error) def test_cli_not_found_detection(self): """Test CLI not found detection.""" error = classify_claude_error("command not found", 127) assert isinstance(error, CLINotFoundError) def test_network_error_detection(self): """Test network error detection.""" error = classify_claude_error("Network connection failed", 1) assert isinstance(error, ClaudeAPIError) assert "Network error" in str(error) def test_timeout_detection(self): """Test timeout error detection.""" error = classify_claude_error("Request timeout", 1) assert isinstance(error, ClaudeAPIError) assert "Network error" in str(error) def test_generic_error(self): """Test generic error fallback.""" error = classify_claude_error("Unknown error occurred", 1) assert isinstance(error, ClaudeAPIError) assert "Claude CLI failed" in str(error)