"""Unit tests for IAudioProcessingService interface contract.""" import pytest from abc import ABC from unittest.mock import Mock from src.domain.interfaces.audio_processing import IAudioProcessingService from src.domain.models.audio_content import AudioContent from src.domain.models.voice_settings import VoiceSettings from src.domain.models.processing_result import ProcessingResult from src.domain.models.text_content import TextContent class TestIAudioProcessingService: """Test cases for IAudioProcessingService interface contract.""" def test_interface_is_abstract(self): """Test that IAudioProcessingService is an abstract base class.""" assert issubclass(IAudioProcessingService, ABC) # Should not be able to instantiate directly with pytest.raises(TypeError): IAudioProcessingService() # type: ignore def test_interface_has_required_method(self): """Test that interface defines the required abstract method.""" # Check that the method exists and is abstract assert hasattr(IAudioProcessingService, 'process_audio_pipeline') assert getattr(IAudioProcessingService.process_audio_pipeline, '__isabstractmethod__', False) def test_method_signature(self): """Test that the method has the correct signature.""" import inspect method = IAudioProcessingService.process_audio_pipeline signature = inspect.signature(method) # Check parameter names and types params = list(signature.parameters.keys()) expected_params = ['self', 'audio', 'target_language', 'voice_settings'] assert params == expected_params # Check return annotation assert signature.return_annotation == "'ProcessingResult'" def test_concrete_implementation_must_implement_method(self): """Test that concrete implementations must implement the abstract method.""" class IncompleteImplementation(IAudioProcessingService): pass # Should not be able to instantiate without implementing abstract method with pytest.raises(TypeError, match="Can't instantiate abstract class"): IncompleteImplementation() # type: ignore def test_concrete_implementation_with_method(self): """Test that concrete implementation with method can be instantiated.""" class ConcreteImplementation(IAudioProcessingService): def process_audio_pipeline(self, audio, target_language, voice_settings): return ProcessingResult.success_result( original_text=TextContent(text="test", language="en") ) # Should be able to instantiate implementation = ConcreteImplementation() assert isinstance(implementation, IAudioProcessingService) def test_method_contract_with_mock(self): """Test the method contract using a mock implementation.""" class MockImplementation(IAudioProcessingService): def __init__(self): self.mock_method = Mock() def process_audio_pipeline(self, audio, target_language, voice_settings): return self.mock_method(audio, target_language, voice_settings) # Create test data audio = AudioContent( data=b"test_audio", format="wav", sample_rate=22050, duration=5.0 ) voice_settings = VoiceSettings( voice_id="test_voice", speed=1.0, language="es" ) expected_result = ProcessingResult.success_result( original_text=TextContent(text="test", language="en") ) # Setup mock implementation = MockImplementation() implementation.mock_method.return_value = expected_result # Call method result = implementation.process_audio_pipeline( audio=audio, target_language="es", voice_settings=voice_settings ) # Verify call and result implementation.mock_method.assert_called_once_with(audio, "es", voice_settings) assert result == expected_result def test_interface_docstring_requirements(self): """Test that the interface method has proper documentation.""" method = IAudioProcessingService.process_audio_pipeline assert method.__doc__ is not None docstring = method.__doc__ # Check that docstring contains key information assert "Process audio through the complete pipeline" in docstring assert "STT -> Translation -> TTS" in docstring assert "Args:" in docstring assert "Returns:" in docstring assert "Raises:" in docstring assert "AudioProcessingException" in docstring def test_interface_type_hints(self): """Test that the interface uses proper type hints.""" import inspect from typing import get_type_hints # Get type hints (this will resolve string annotations) try: hints = get_type_hints(IAudioProcessingService.process_audio_pipeline) except NameError: # If forward references can't be resolved, check annotations directly method = IAudioProcessingService.process_audio_pipeline annotations = getattr(method, '__annotations__', {}) assert 'audio' in annotations assert 'target_language' in annotations assert 'voice_settings' in annotations assert 'return' in annotations # Check that type annotations are strings (forward references) assert annotations['audio'] == "'AudioContent'" assert annotations['target_language'] == str assert annotations['voice_settings'] == "'VoiceSettings'" assert annotations['return'] == "'ProcessingResult'" def test_multiple_implementations_possible(self): """Test that multiple implementations of the interface are possible.""" class Implementation1(IAudioProcessingService): def process_audio_pipeline(self, audio, target_language, voice_settings): return ProcessingResult.success_result( original_text=TextContent(text="impl1", language="en") ) class Implementation2(IAudioProcessingService): def process_audio_pipeline(self, audio, target_language, voice_settings): return ProcessingResult.failure_result(error_message="impl2 failed") impl1 = Implementation1() impl2 = Implementation2() assert isinstance(impl1, IAudioProcessingService) assert isinstance(impl2, IAudioProcessingService) assert type(impl1) != type(impl2) def test_interface_method_can_be_called_polymorphically(self): """Test that interface methods can be called polymorphically.""" class TestImplementation(IAudioProcessingService): def __init__(self, result): self.result = result def process_audio_pipeline(self, audio, target_language, voice_settings): return self.result # Create different implementations success_result = ProcessingResult.success_result( original_text=TextContent(text="success", language="en") ) failure_result = ProcessingResult.failure_result(error_message="failed") implementations = [ TestImplementation(success_result), TestImplementation(failure_result) ] # Test polymorphic usage audio = AudioContent(data=b"test", format="wav", sample_rate=22050, duration=1.0) voice_settings = VoiceSettings(voice_id="test", speed=1.0, language="en") results = [] for impl in implementations: # Can call the same method on different implementations result = impl.process_audio_pipeline(audio, "en", voice_settings) results.append(result) assert len(results) == 2 assert results[0].success is True assert results[1].success is False def test_interface_inheritance_chain(self): """Test the inheritance chain of the interface.""" # Check that it inherits from ABC assert ABC in IAudioProcessingService.__mro__ # Check that it's at the right position in MRO mro = IAudioProcessingService.__mro__ assert mro[0] == IAudioProcessingService assert ABC in mro