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