Spaces:
Build error
Build error
"""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 |