| """ |
| Unit tests for common.exceptions module |
| |
| Tests all custom exception classes and their attributes. |
| """ |
|
|
| import pytest |
| from common import ( |
| MedicalTranscriberException, |
| AudioFileException, |
| TranscriptionException, |
| CorrectionException, |
| ReportGenerationException, |
| ConfigurationException, |
| APIException, |
| ValidationException, |
| KnowledgeBaseException |
| ) |
|
|
|
|
| class TestMedicalTranscriberException: |
| """Test cases for base MedicalTranscriberException""" |
| |
| def test_is_exception(self): |
| """Should be an Exception subclass""" |
| exc = MedicalTranscriberException("Test error") |
| assert isinstance(exc, Exception) |
| |
| def test_with_message(self): |
| """Should accept message""" |
| msg = "Test error message" |
| exc = MedicalTranscriberException(msg) |
| assert str(exc) == msg |
| |
| def test_inheritance(self): |
| """All specific exceptions should inherit from base""" |
| exceptions = [ |
| AudioFileException("path", "message"), |
| TranscriptionException("message"), |
| CorrectionException("message"), |
| ReportGenerationException("message"), |
| ConfigurationException("message"), |
| APIException("endpoint", 404, "message"), |
| ValidationException("field", "value", "message"), |
| KnowledgeBaseException("message") |
| ] |
| |
| for exc in exceptions: |
| assert isinstance(exc, MedicalTranscriberException) |
|
|
|
|
| class TestAudioFileException: |
| """Test cases for AudioFileException""" |
| |
| def test_with_default_message(self): |
| """Should use default message if not provided""" |
| exc = AudioFileException("/path/to/file.wav") |
| assert "/path/to/file.wav" in str(exc) |
| assert "Invalid audio file" in str(exc) |
| |
| def test_with_custom_message(self): |
| """Should use custom message if provided""" |
| exc = AudioFileException("/path/to/file.wav", "File is corrupted") |
| assert "File is corrupted" in str(exc) |
| assert "/path/to/file.wav" in str(exc) |
| |
| def test_file_path_attribute(self): |
| """Should have file_path attribute""" |
| file_path = "/path/to/file.wav" |
| exc = AudioFileException(file_path, "Test") |
| assert exc.file_path == file_path |
| |
| def test_message_attribute(self): |
| """Should have message attribute""" |
| exc = AudioFileException("/path/to/file.wav", "Test message") |
| assert "Test message" in exc.message |
|
|
|
|
| class TestTranscriptionException: |
| """Test cases for TranscriptionException""" |
| |
| def test_basic_usage(self): |
| """Should work with simple message""" |
| exc = TranscriptionException("Transcription failed") |
| assert "Transcription failed" in str(exc) |
| |
| def test_inheritance_chain(self): |
| """Should be MedicalTranscriberException""" |
| exc = TranscriptionException("Test") |
| assert isinstance(exc, MedicalTranscriberException) |
|
|
|
|
| class TestCorrectionException: |
| """Test cases for CorrectionException""" |
| |
| def test_basic_usage(self): |
| """Should work with simple message""" |
| exc = CorrectionException("LLM correction failed") |
| assert "LLM correction failed" in str(exc) |
| |
| def test_inheritance_chain(self): |
| """Should be MedicalTranscriberException""" |
| exc = CorrectionException("Test") |
| assert isinstance(exc, MedicalTranscriberException) |
|
|
|
|
| class TestReportGenerationException: |
| """Test cases for ReportGenerationException""" |
| |
| def test_basic_usage(self): |
| """Should work with simple message""" |
| exc = ReportGenerationException("Report generation failed") |
| assert "Report generation failed" in str(exc) |
| |
| def test_inheritance_chain(self): |
| """Should be MedicalTranscriberException""" |
| exc = ReportGenerationException("Test") |
| assert isinstance(exc, MedicalTranscriberException) |
|
|
|
|
| class TestConfigurationException: |
| """Test cases for ConfigurationException""" |
| |
| def test_basic_usage(self): |
| """Should work with simple message""" |
| exc = ConfigurationException("Invalid configuration") |
| assert "Invalid configuration" in str(exc) |
| |
| def test_inheritance_chain(self): |
| """Should be MedicalTranscriberException""" |
| exc = ConfigurationException("Test") |
| assert isinstance(exc, MedicalTranscriberException) |
|
|
|
|
| class TestAPIException: |
| """Test cases for APIException with status codes""" |
| |
| def test_with_status_code_and_message(self): |
| """Should capture endpoint, status code, and message""" |
| exc = APIException("https://api.example.com/v1/chat", 429, "Rate limit exceeded") |
| |
| assert exc.endpoint == "https://api.example.com/v1/chat" |
| assert exc.status_code == 429 |
| assert "429" in str(exc) |
| assert "Rate limit" in str(exc) |
| |
| def test_error_400(self): |
| """Should handle 400 Bad Request""" |
| exc = APIException("api/endpoint", 400, "Bad request format") |
| assert exc.status_code == 400 |
| assert "400" in str(exc) |
| |
| def test_error_401(self): |
| """Should handle 401 Unauthorized""" |
| exc = APIException("api/endpoint", 401, "Invalid API key") |
| assert exc.status_code == 401 |
| assert "401" in str(exc) |
| assert "Invalid API key" in str(exc) |
| |
| def test_error_429(self): |
| """Should handle 429 Rate Limit""" |
| exc = APIException("api/endpoint", 429, "Rate limit exceeded") |
| assert exc.status_code == 429 |
| assert "429" in str(exc) |
| |
| def test_error_500(self): |
| """Should handle 500 Internal Server Error""" |
| exc = APIException("api/endpoint", 500, "Server error") |
| assert exc.status_code == 500 |
| assert "500" in str(exc) |
| |
| def test_message_attribute(self): |
| """Should have message attribute with full context""" |
| exc = APIException("api/endpoint", 404, "Not found") |
| assert "404" in exc.message |
| assert "api/endpoint" in exc.message |
| |
| def test_inheritance_chain(self): |
| """Should be MedicalTranscriberException""" |
| exc = APIException("api", 500, "error") |
| assert isinstance(exc, MedicalTranscriberException) |
|
|
|
|
| class TestValidationException: |
| """Test cases for ValidationException with field context""" |
| |
| def test_with_field_name(self): |
| """Should capture field name""" |
| exc = ValidationException("email", "invalid@", "Invalid email format") |
| |
| assert exc.field == "email" |
| assert exc.value == "invalid@" |
| assert "email" in str(exc) |
| |
| def test_default_reason(self): |
| """Should use default reason if not provided""" |
| exc = ValidationException("username", "ab", "") |
| assert "Invalid value" in str(exc) |
| |
| def test_custom_reason(self): |
| """Should use custom reason""" |
| exc = ValidationException("age", "-5", "Age must be positive") |
| assert "Age must be positive" in str(exc) |
| |
| def test_audio_file_field(self): |
| """Should work with audio_file field""" |
| exc = ValidationException("audio_file", "test.xyz", "Unsupported format") |
| assert "audio_file" in str(exc) |
| assert "test.xyz" in str(exc) |
| |
| def test_api_key_field(self): |
| """Should work with api_key field""" |
| exc = ValidationException("api_key", "***", "API key is too short") |
| assert "api_key" in str(exc) |
| |
| def test_inheritance_chain(self): |
| """Should be MedicalTranscriberException""" |
| exc = ValidationException("field", "value", "reason") |
| assert isinstance(exc, MedicalTranscriberException) |
|
|
|
|
| class TestKnowledgeBaseException: |
| """Test cases for KnowledgeBaseException""" |
| |
| def test_basic_usage(self): |
| """Should work with simple message""" |
| exc = KnowledgeBaseException("Knowledge base not found") |
| assert "Knowledge base not found" in str(exc) |
| |
| def test_inheritance_chain(self): |
| """Should be MedicalTranscriberException""" |
| exc = KnowledgeBaseException("Test") |
| assert isinstance(exc, MedicalTranscriberException) |
|
|
|
|
| class TestExceptionHandling: |
| """Integration tests for exception handling""" |
| |
| def test_catch_api_exception_by_status_code(self): |
| """Should be able to catch and handle by status code""" |
| exc = APIException("api/chat", 429, "Rate limit") |
| |
| try: |
| raise exc |
| except APIException as e: |
| if e.status_code == 429: |
| assert True |
| else: |
| assert False |
| |
| def test_catch_specific_exceptions(self): |
| """Should be able to catch specific exception types""" |
| exceptions_to_test = [ |
| (AudioFileException("/path", "test"), AudioFileException), |
| (TranscriptionException("test"), TranscriptionException), |
| (CorrectionException("test"), CorrectionException), |
| (APIException("api", 500, "error"), APIException), |
| (ValidationException("field", "value", "reason"), ValidationException), |
| ] |
| |
| for exc, exc_type in exceptions_to_test: |
| try: |
| raise exc |
| except exc_type: |
| assert True |
| except Exception: |
| assert False |
| |
| def test_catch_all_as_medical_transcriber_exception(self): |
| """Should be able to catch all as base exception""" |
| exceptions = [ |
| AudioFileException("/path", "test"), |
| TranscriptionException("test"), |
| CorrectionException("test"), |
| APIException("api", 500, "error"), |
| ValidationException("field", "value", "reason"), |
| KnowledgeBaseException("test"), |
| ] |
| |
| for exc in exceptions: |
| try: |
| raise exc |
| except MedicalTranscriberException: |
| assert True |
| except Exception: |
| assert False |
| |
| def test_exception_chain_preservation(self): |
| """Should preserve exception chain""" |
| try: |
| try: |
| raise ValueError("Original error") |
| except ValueError as e: |
| raise AudioFileException("/path", str(e)) from e |
| except AudioFileException as e: |
| assert str(e.file_path) == "/path" |
| |
| def test_multiple_exception_handlers(self): |
| """Should work with multiple exception handlers""" |
| def test_api_error(): |
| raise APIException("api", 429, "Rate limited") |
| |
| def test_validation_error(): |
| raise ValidationException("field", "value", "Invalid") |
| |
| try: |
| test_api_error() |
| except APIException as e: |
| assert e.status_code == 429 |
| except Exception: |
| assert False |
| |
| try: |
| test_validation_error() |
| except ValidationException as e: |
| assert e.field == "field" |
| except Exception: |
| assert False |
|
|
|
|
| class TestExceptionStringRepresentation: |
| """Test string representations of exceptions""" |
| |
| def test_audio_file_exception_string(self): |
| """Should have informative string representation""" |
| exc = AudioFileException("/path/to/audio.wav", "Corrupted file") |
| exc_str = str(exc) |
| |
| assert "Corrupted file" in exc_str |
| assert "/path/to/audio.wav" in exc_str |
| |
| def test_api_exception_string(self): |
| """Should have informative string representation""" |
| exc = APIException("api/v1/chat", 401, "Unauthorized") |
| exc_str = str(exc) |
| |
| assert "401" in exc_str |
| assert "api/v1/chat" in exc_str |
| assert "Unauthorized" in exc_str |
| |
| def test_validation_exception_string(self): |
| """Should have informative string representation""" |
| exc = ValidationException("username", "admin", "Username reserved") |
| exc_str = str(exc) |
| |
| assert "username" in exc_str |
| assert "admin" in exc_str |
| assert "Username reserved" in exc_str |
|
|