"""Unit tests for ProcessingResult value object.""" import pytest from src.domain.models.processing_result import ProcessingResult from src.domain.models.text_content import TextContent from src.domain.models.audio_content import AudioContent class TestProcessingResult: """Test cases for ProcessingResult value object.""" @pytest.fixture def sample_text_content(self): """Sample text content for testing.""" return TextContent(text="Hello, world!", language="en") @pytest.fixture def sample_translated_text(self): """Sample translated text content for testing.""" return TextContent(text="Hola, mundo!", language="es") @pytest.fixture def sample_audio_content(self): """Sample audio content for testing.""" return AudioContent( data=b"fake_audio_data", format="wav", sample_rate=22050, duration=5.0 ) def test_valid_successful_processing_result(self, sample_text_content, sample_translated_text, sample_audio_content): """Test creating valid successful ProcessingResult.""" result = ProcessingResult( success=True, original_text=sample_text_content, translated_text=sample_translated_text, audio_output=sample_audio_content, error_message=None, processing_time=2.5 ) assert result.success is True assert result.original_text == sample_text_content assert result.translated_text == sample_translated_text assert result.audio_output == sample_audio_content assert result.error_message is None assert result.processing_time == 2.5 assert result.has_translation is True assert result.has_audio_output is True assert result.is_complete_pipeline is True def test_valid_failed_processing_result(self): """Test creating valid failed ProcessingResult.""" result = ProcessingResult( success=False, original_text=None, translated_text=None, audio_output=None, error_message="Processing failed", processing_time=1.0 ) assert result.success is False assert result.original_text is None assert result.translated_text is None assert result.audio_output is None assert result.error_message == "Processing failed" assert result.processing_time == 1.0 assert result.has_translation is False assert result.has_audio_output is False assert result.is_complete_pipeline is False def test_non_boolean_success_raises_error(self, sample_text_content): """Test that non-boolean success raises TypeError.""" with pytest.raises(TypeError, match="Success must be a boolean"): ProcessingResult( success="true", # type: ignore original_text=sample_text_content, translated_text=None, audio_output=None, error_message=None, processing_time=1.0 ) def test_invalid_original_text_type_raises_error(self): """Test that invalid original text type raises TypeError.""" with pytest.raises(TypeError, match="Original text must be a TextContent instance or None"): ProcessingResult( success=True, original_text="not a TextContent", # type: ignore translated_text=None, audio_output=None, error_message=None, processing_time=1.0 ) def test_invalid_translated_text_type_raises_error(self, sample_text_content): """Test that invalid translated text type raises TypeError.""" with pytest.raises(TypeError, match="Translated text must be a TextContent instance or None"): ProcessingResult( success=True, original_text=sample_text_content, translated_text="not a TextContent", # type: ignore audio_output=None, error_message=None, processing_time=1.0 ) def test_invalid_audio_output_type_raises_error(self, sample_text_content): """Test that invalid audio output type raises TypeError.""" with pytest.raises(TypeError, match="Audio output must be an AudioContent instance or None"): ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output="not an AudioContent", # type: ignore error_message=None, processing_time=1.0 ) def test_invalid_error_message_type_raises_error(self, sample_text_content): """Test that invalid error message type raises TypeError.""" with pytest.raises(TypeError, match="Error message must be a string or None"): ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=None, error_message=123, # type: ignore processing_time=1.0 ) def test_non_numeric_processing_time_raises_error(self, sample_text_content): """Test that non-numeric processing time raises TypeError.""" with pytest.raises(TypeError, match="Processing time must be a number"): ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=None, error_message=None, processing_time="1.0" # type: ignore ) def test_negative_processing_time_raises_error(self, sample_text_content): """Test that negative processing time raises ValueError.""" with pytest.raises(ValueError, match="Processing time cannot be negative"): ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=None, error_message=None, processing_time=-1.0 ) def test_successful_result_with_error_message_raises_error(self, sample_text_content): """Test that successful result with error message raises ValueError.""" with pytest.raises(ValueError, match="Successful result cannot have an error message"): ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=None, error_message="This should not be here", processing_time=1.0 ) def test_successful_result_without_original_text_raises_error(self): """Test that successful result without original text raises ValueError.""" with pytest.raises(ValueError, match="Successful result must have original text"): ProcessingResult( success=True, original_text=None, translated_text=None, audio_output=None, error_message=None, processing_time=1.0 ) def test_failed_result_without_error_message_raises_error(self): """Test that failed result without error message raises ValueError.""" with pytest.raises(ValueError, match="Failed result must have a non-empty error message"): ProcessingResult( success=False, original_text=None, translated_text=None, audio_output=None, error_message=None, processing_time=1.0 ) def test_failed_result_with_empty_error_message_raises_error(self): """Test that failed result with empty error message raises ValueError.""" with pytest.raises(ValueError, match="Failed result must have a non-empty error message"): ProcessingResult( success=False, original_text=None, translated_text=None, audio_output=None, error_message="", processing_time=1.0 ) def test_failed_result_with_whitespace_error_message_raises_error(self): """Test that failed result with whitespace-only error message raises ValueError.""" with pytest.raises(ValueError, match="Failed result must have a non-empty error message"): ProcessingResult( success=False, original_text=None, translated_text=None, audio_output=None, error_message=" ", processing_time=1.0 ) def test_has_translation_property(self, sample_text_content, sample_translated_text): """Test has_translation property.""" # With translation result_with_translation = ProcessingResult( success=True, original_text=sample_text_content, translated_text=sample_translated_text, audio_output=None, error_message=None, processing_time=1.0 ) assert result_with_translation.has_translation is True # Without translation result_without_translation = ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=None, error_message=None, processing_time=1.0 ) assert result_without_translation.has_translation is False def test_has_audio_output_property(self, sample_text_content, sample_audio_content): """Test has_audio_output property.""" # With audio output result_with_audio = ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=sample_audio_content, error_message=None, processing_time=1.0 ) assert result_with_audio.has_audio_output is True # Without audio output result_without_audio = ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=None, error_message=None, processing_time=1.0 ) assert result_without_audio.has_audio_output is False def test_is_complete_pipeline_property(self, sample_text_content, sample_translated_text, sample_audio_content): """Test is_complete_pipeline property.""" # Complete pipeline complete_result = ProcessingResult( success=True, original_text=sample_text_content, translated_text=sample_translated_text, audio_output=sample_audio_content, error_message=None, processing_time=1.0 ) assert complete_result.is_complete_pipeline is True # Incomplete pipeline (missing translation) incomplete_result = ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=sample_audio_content, error_message=None, processing_time=1.0 ) assert incomplete_result.is_complete_pipeline is False # Failed result failed_result = ProcessingResult( success=False, original_text=None, translated_text=None, audio_output=None, error_message="Failed", processing_time=1.0 ) assert failed_result.is_complete_pipeline is False def test_success_result_class_method(self, sample_text_content, sample_translated_text, sample_audio_content): """Test success_result class method.""" result = ProcessingResult.success_result( original_text=sample_text_content, translated_text=sample_translated_text, audio_output=sample_audio_content, processing_time=2.5 ) assert result.success is True assert result.original_text == sample_text_content assert result.translated_text == sample_translated_text assert result.audio_output == sample_audio_content assert result.error_message is None assert result.processing_time == 2.5 def test_success_result_with_minimal_parameters(self, sample_text_content): """Test success_result class method with minimal parameters.""" result = ProcessingResult.success_result( original_text=sample_text_content ) assert result.success is True assert result.original_text == sample_text_content assert result.translated_text is None assert result.audio_output is None assert result.error_message is None assert result.processing_time == 0.0 def test_failure_result_class_method(self, sample_text_content): """Test failure_result class method.""" result = ProcessingResult.failure_result( error_message="Something went wrong", processing_time=1.5, original_text=sample_text_content ) assert result.success is False assert result.original_text == sample_text_content assert result.translated_text is None assert result.audio_output is None assert result.error_message == "Something went wrong" assert result.processing_time == 1.5 def test_failure_result_with_minimal_parameters(self): """Test failure_result class method with minimal parameters.""" result = ProcessingResult.failure_result( error_message="Failed" ) assert result.success is False assert result.original_text is None assert result.translated_text is None assert result.audio_output is None assert result.error_message == "Failed" assert result.processing_time == 0.0 def test_processing_result_is_immutable(self, sample_text_content): """Test that ProcessingResult is immutable (frozen dataclass).""" result = ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=None, error_message=None, processing_time=1.0 ) with pytest.raises(AttributeError): result.success = False # type: ignore def test_zero_processing_time_valid(self, sample_text_content): """Test that zero processing time is valid.""" result = ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=None, error_message=None, processing_time=0.0 ) assert result.processing_time == 0.0 def test_partial_success_scenarios(self, sample_text_content, sample_translated_text): """Test various partial success scenarios.""" # Only STT completed stt_only = ProcessingResult( success=True, original_text=sample_text_content, translated_text=None, audio_output=None, error_message=None, processing_time=1.0 ) assert stt_only.has_translation is False assert stt_only.has_audio_output is False assert stt_only.is_complete_pipeline is False # STT + Translation completed stt_translation = ProcessingResult( success=True, original_text=sample_text_content, translated_text=sample_translated_text, audio_output=None, error_message=None, processing_time=1.5 ) assert stt_translation.has_translation is True assert stt_translation.has_audio_output is False assert stt_translation.is_complete_pipeline is False