teachingAssistant / tests /unit /domain /interfaces /test_audio_processing.py
Michael Hu
Create unit tests for domain layer
48f8a08
raw
history blame
8.51 kB
"""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