File size: 9,653 Bytes
48f8a08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
"""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"