Spaces:
Build error
Build error
"""Unit tests for ISpeechRecognitionService interface contract.""" | |
import pytest | |
from abc import ABC | |
from unittest.mock import Mock | |
from src.domain.interfaces.speech_recognition import ISpeechRecognitionService | |
from src.domain.models.audio_content import AudioContent | |
from src.domain.models.text_content import TextContent | |
class TestISpeechRecognitionService: | |
"""Test cases for ISpeechRecognitionService interface contract.""" | |
def test_interface_is_abstract(self): | |
"""Test that ISpeechRecognitionService is an abstract base class.""" | |
assert issubclass(ISpeechRecognitionService, ABC) | |
# Should not be able to instantiate directly | |
with pytest.raises(TypeError): | |
ISpeechRecognitionService() # 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(ISpeechRecognitionService, 'transcribe') | |
assert getattr(ISpeechRecognitionService.transcribe, '__isabstractmethod__', False) | |
def test_method_signature(self): | |
"""Test that the method has the correct signature.""" | |
import inspect | |
method = ISpeechRecognitionService.transcribe | |
signature = inspect.signature(method) | |
# Check parameter names | |
params = list(signature.parameters.keys()) | |
expected_params = ['self', 'audio', 'model'] | |
assert params == expected_params | |
# Check return annotation | |
assert signature.return_annotation == "'TextContent'" | |
def test_concrete_implementation_must_implement_method(self): | |
"""Test that concrete implementations must implement the abstract method.""" | |
class IncompleteImplementation(ISpeechRecognitionService): | |
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(ISpeechRecognitionService): | |
def transcribe(self, audio, model): | |
return TextContent(text="transcribed text", language="en") | |
# Should be able to instantiate | |
implementation = ConcreteImplementation() | |
assert isinstance(implementation, ISpeechRecognitionService) | |
def test_method_contract_with_mock(self): | |
"""Test the method contract using a mock implementation.""" | |
class MockImplementation(ISpeechRecognitionService): | |
def __init__(self): | |
self.mock_method = Mock() | |
def transcribe(self, audio, model): | |
return self.mock_method(audio, model) | |
# Create test data | |
audio = AudioContent( | |
data=b"test_audio", | |
format="wav", | |
sample_rate=22050, | |
duration=5.0 | |
) | |
model = "whisper-base" | |
expected_result = TextContent(text="Hello world", language="en") | |
# Setup mock | |
implementation = MockImplementation() | |
implementation.mock_method.return_value = expected_result | |
# Call method | |
result = implementation.transcribe(audio=audio, model=model) | |
# Verify call and result | |
implementation.mock_method.assert_called_once_with(audio, model) | |
assert result == expected_result | |
def test_interface_docstring_requirements(self): | |
"""Test that the interface method has proper documentation.""" | |
method = ISpeechRecognitionService.transcribe | |
assert method.__doc__ is not None | |
docstring = method.__doc__ | |
# Check that docstring contains key information | |
assert "Transcribe audio content to text" in docstring | |
assert "Args:" in docstring | |
assert "Returns:" in docstring | |
assert "Raises:" in docstring | |
assert "SpeechRecognitionException" in docstring | |
def test_interface_type_hints(self): | |
"""Test that the interface uses proper type hints.""" | |
method = ISpeechRecognitionService.transcribe | |
annotations = getattr(method, '__annotations__', {}) | |
assert 'audio' in annotations | |
assert 'model' in annotations | |
assert 'return' in annotations | |
# Check that type annotations are correct | |
assert annotations['audio'] == "'AudioContent'" | |
assert annotations['model'] == str | |
assert annotations['return'] == "'TextContent'" | |
def test_multiple_implementations_possible(self): | |
"""Test that multiple implementations of the interface are possible.""" | |
class WhisperImplementation(ISpeechRecognitionService): | |
def transcribe(self, audio, model): | |
return TextContent(text="whisper transcription", language="en") | |
class ParakeetImplementation(ISpeechRecognitionService): | |
def transcribe(self, audio, model): | |
return TextContent(text="parakeet transcription", language="en") | |
whisper = WhisperImplementation() | |
parakeet = ParakeetImplementation() | |
assert isinstance(whisper, ISpeechRecognitionService) | |
assert isinstance(parakeet, ISpeechRecognitionService) | |
assert type(whisper) != type(parakeet) | |
def test_interface_method_can_be_called_polymorphically(self): | |
"""Test that interface methods can be called polymorphically.""" | |
class TestImplementation(ISpeechRecognitionService): | |
def __init__(self, transcription_text): | |
self.transcription_text = transcription_text | |
def transcribe(self, audio, model): | |
return TextContent(text=self.transcription_text, language="en") | |
# Create different implementations | |
implementations = [ | |
TestImplementation("first transcription"), | |
TestImplementation("second transcription") | |
] | |
# Test polymorphic usage | |
audio = AudioContent(data=b"test", format="wav", sample_rate=22050, duration=1.0) | |
model = "test-model" | |
results = [] | |
for impl in implementations: | |
# Can call the same method on different implementations | |
result = impl.transcribe(audio, model) | |
results.append(result.text) | |
assert results == ["first transcription", "second transcription"] | |
def test_interface_inheritance_chain(self): | |
"""Test the inheritance chain of the interface.""" | |
# Check that it inherits from ABC | |
assert ABC in ISpeechRecognitionService.__mro__ | |
# Check that it's at the right position in MRO | |
mro = ISpeechRecognitionService.__mro__ | |
assert mro[0] == ISpeechRecognitionService | |
assert ABC in mro | |
def test_method_parameter_validation_in_implementation(self): | |
"""Test that implementations can validate parameters.""" | |
class ValidatingImplementation(ISpeechRecognitionService): | |
def transcribe(self, audio, model): | |
if not isinstance(audio, AudioContent): | |
raise TypeError("audio must be AudioContent") | |
if not isinstance(model, str): | |
raise TypeError("model must be string") | |
if not model.strip(): | |
raise ValueError("model cannot be empty") | |
return TextContent(text="validated transcription", language="en") | |
impl = ValidatingImplementation() | |
# Valid call should work | |
audio = AudioContent(data=b"test", format="wav", sample_rate=22050, duration=1.0) | |
result = impl.transcribe(audio, "whisper-base") | |
assert result.text == "validated transcription" | |
# Invalid calls should raise appropriate errors | |
with pytest.raises(TypeError, match="audio must be AudioContent"): | |
impl.transcribe("not audio", "whisper-base") # type: ignore | |
with pytest.raises(TypeError, match="model must be string"): | |
impl.transcribe(audio, 123) # type: ignore | |
with pytest.raises(ValueError, match="model cannot be empty"): | |
impl.transcribe(audio, "") | |
def test_implementation_can_handle_different_models(self): | |
"""Test that implementations can handle different model types.""" | |
class MultiModelImplementation(ISpeechRecognitionService): | |
def transcribe(self, audio, model): | |
model_responses = { | |
"whisper-tiny": "tiny transcription", | |
"whisper-base": "base transcription", | |
"whisper-large": "large transcription", | |
"parakeet": "parakeet transcription" | |
} | |
transcription = model_responses.get(model, "unknown model transcription") | |
return TextContent(text=transcription, language="en") | |
impl = MultiModelImplementation() | |
audio = AudioContent(data=b"test", format="wav", sample_rate=22050, duration=1.0) | |
# Test different models | |
models_and_expected = [ | |
("whisper-tiny", "tiny transcription"), | |
("whisper-base", "base transcription"), | |
("whisper-large", "large transcription"), | |
("parakeet", "parakeet transcription"), | |
("unknown-model", "unknown model transcription") | |
] | |
for model, expected_text in models_and_expected: | |
result = impl.transcribe(audio, model) | |
assert result.text == expected_text | |
assert result.language == "en" |