CoolFace
Apppublic

nifty-coder/stemsplit-backend

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes
test_azure_speech_provider.py686 linesDownload Raw Back to tests
1"""2Unit tests for Azure Speech Services provider.3 4This module contains comprehensive tests for the AzureSpeechProvider class,5including transcription functionality, quota management, error handling,6and integration scenarios.7"""8 9import pytest10import asyncio11import json12from unittest.mock import Mock, AsyncMock, patch, MagicMock13from datetime import datetime, timezone, timedelta14from typing import Dict, Any15 16from voice_control.providers.azure_speech_provider import AzureSpeechProvider, create_azure_speech_provider17from voice_control.models import (18    ProviderConfig, 19    TranscriptionResult, 20    QuotaStatus, 21    QuotaType,22    ProviderType23)24from voice_control.exceptions import (25    ProviderError,26    ProviderUnavailableError,27    ProviderAuthenticationError,28    ProviderTimeoutError,29    UnsupportedFormatError,30    UnsupportedLanguageError,31    QuotaExceededError,32    RateLimitExceededError,33    TranscriptionError34)35 36 37class TestAzureSpeechProvider:38    """Test cases for AzureSpeechProvider."""39    40    @pytest.fixture41    def mock_azure_sdk(self):42        """Mock Azure Speech SDK components."""43        with patch('voice_control.providers.azure_speech_provider.AZURE_SPEECH_AVAILABLE', True):44            # Mock the individual imports45            with patch('voice_control.providers.azure_speech_provider.speechsdk') as mock_speechsdk, \46                 patch('voice_control.providers.azure_speech_provider.SpeechConfig') as mock_speech_config_class, \47                 patch('voice_control.providers.azure_speech_provider.AudioConfig') as mock_audio_config_class, \48                 patch('voice_control.providers.azure_speech_provider.SpeechRecognizer') as mock_recognizer_class, \49                 patch('voice_control.providers.azure_speech_provider.ResultReason') as mock_result_reason, \50                 patch('voice_control.providers.azure_speech_provider.CancellationReason') as mock_cancel_reason:51                52                # Mock SpeechConfig instances53                mock_speech_config = Mock()54                mock_speech_config_class.return_value = mock_speech_config55                56                # Mock AudioConfig instances57                mock_audio_config = Mock()58                mock_audio_config_class.return_value = mock_audio_config59                60                # Mock SpeechRecognizer instances61                mock_recognizer = Mock()62                mock_recognizer_class.return_value = mock_recognizer63                64                # Mock audio components65                mock_push_stream = Mock()66                mock_speechsdk.audio.PushAudioInputStream.return_value = mock_push_stream67                mock_speechsdk.audio.AudioConfig.return_value = mock_audio_config68                69                # Mock ResultReason enum values70                mock_result_reason.RecognizedSpeech = "RecognizedSpeech"71                mock_result_reason.NoMatch = "NoMatch"72                mock_result_reason.RecognizingSpeech = "RecognizingSpeech"73                74                # Mock CancellationReason enum values75                mock_cancel_reason.Error = "Error"76                77                # Mock OutputFormat78                mock_speechsdk.OutputFormat.Detailed = "Detailed"79                80                # Mock ProfanityOption81                mock_speechsdk.ProfanityOption.Raw = "Raw"82                mock_speechsdk.ProfanityOption.Masked = "Masked"83                84                # Create a mock SDK object with all the mocked components85                mock_sdk = Mock()86                mock_sdk.SpeechConfig = mock_speech_config_class87                mock_sdk.AudioConfig = mock_audio_config_class88                mock_sdk.SpeechRecognizer = mock_recognizer_class89                mock_sdk.ResultReason = mock_result_reason90                mock_sdk.CancellationReason = mock_cancel_reason91                mock_sdk.OutputFormat = mock_speechsdk.OutputFormat92                mock_sdk.ProfanityOption = mock_speechsdk.ProfanityOption93                mock_sdk.audio = mock_speechsdk.audio94                95                yield mock_sdk96    97    @pytest.fixture98    def provider_config(self):99        """Create a test provider configuration."""100        return ProviderConfig(101            name="test_azure_speech",102            provider_type=ProviderType.AZURE_SPEECH,103            enabled=True,104            priority=3,105            free_tier_limits={106                "audio_hours_per_month": 5.0,107                "requests_per_minute": 20,108                "requests_per_day": 5000109            },110            rate_limits={111                "requests_per_minute": 20,112                "requests_per_day": 5000113            },114            supported_formats=["wav", "flac", "mp3", "ogg", "webm"],115            supported_languages=["en-US", "en-GB", "es-ES", "fr-FR"],116            cost_per_minute=2.5 / 60.0,  # Neural pricing per minute117            api_credentials={118                "subscription_key": "test_key_12345",119                "region": "eastus"120            },121            timeout_seconds=60,122            max_retries=3123        )124    125    @pytest.fixture126    def provider(self, provider_config, mock_azure_sdk):127        """Create a test Azure Speech provider instance."""128        return AzureSpeechProvider(provider_config)129    130    def test_initialization_success(self, provider_config, mock_azure_sdk):131        """Test successful provider initialization."""132        provider = AzureSpeechProvider(provider_config)133        134        assert provider.name == "test_azure_speech"135        assert provider.provider_type == ProviderType.AZURE_SPEECH136        assert not provider._client_initialized137        assert provider._usage_stats["requests_today"] == 0138        assert provider._usage_stats["audio_hours_this_month"] == 0.0139    140    def test_initialization_invalid_provider_type(self, provider_config, mock_azure_sdk):141        """Test initialization with invalid provider type."""142        provider_config.provider_type = ProviderType.GOOGLE_SPEECH143        144        with pytest.raises(ValueError, match="Invalid provider type"):145            AzureSpeechProvider(provider_config)146    147    def test_initialization_missing_azure_sdk(self, provider_config):148        """Test initialization when Azure SDK is not available."""149        with patch('voice_control.providers.azure_speech_provider.AZURE_SPEECH_AVAILABLE', False):150            with pytest.raises(ImportError, match="Azure Speech SDK not available"):151                AzureSpeechProvider(provider_config)152    153    @pytest.mark.asyncio154    async def test_initialize_client_success(self, provider, mock_azure_sdk):155        """Test successful client initialization."""156        await provider._initialize_client()157        158        assert provider._client_initialized159        mock_azure_sdk.SpeechConfig.assert_called_once()160    161    @pytest.mark.asyncio162    async def test_initialize_client_missing_credentials(self, provider_config, mock_azure_sdk):163        """Test client initialization with missing credentials."""164        provider_config.api_credentials = {}165        provider = AzureSpeechProvider(provider_config)166        167        with pytest.raises(ProviderAuthenticationError):168            await provider._initialize_client()169    170    @pytest.mark.asyncio171    async def test_initialize_client_with_endpoint(self, provider_config, mock_azure_sdk):172        """Test client initialization with custom endpoint."""173        provider_config.api_credentials = {174            "subscription_key": "test_key",175            "endpoint": "https://custom.cognitiveservices.azure.com/"176        }177        provider = AzureSpeechProvider(provider_config)178        179        await provider._initialize_client()180        181        assert provider._client_initialized182        mock_azure_sdk.SpeechConfig.assert_called_once()183    184    def test_supports_format(self, provider):185        """Test audio format support checking."""186        assert provider.supports_format("wav")187        assert provider.supports_format("WAV")  # Case insensitive188        assert provider.supports_format("mp3")189        assert provider.supports_format("flac")190        assert provider.supports_format("webm")191        assert not provider.supports_format("xyz")192        assert not provider.supports_format("")193    194    def test_supports_language(self, provider):195        """Test language support checking."""196        assert provider.supports_language("en-US")197        assert provider.supports_language("es-ES")198        assert provider.supports_language("fr-FR")199        assert provider.supports_language("de-DE")200        assert not provider.supports_language("xx-XX")201        assert not provider.supports_language("")202    203    @pytest.mark.asyncio204    async def test_estimate_cost_free_tier(self, provider):205        """Test cost estimation within free tier."""206        # 1 hour of audio (within 5-hour free tier)207        cost = await provider.estimate_cost(3600.0)208        assert cost == 0.0209    210    @pytest.mark.asyncio211    async def test_estimate_cost_exceeds_free_tier(self, provider):212        """Test cost estimation exceeding free tier."""213        # 6 hours of audio (exceeds 5-hour free tier)214        cost = await provider.estimate_cost(6 * 3600.0)215        expected_cost = 1 * 2.5  # 1 hour at $2.50/hour (neural pricing)216        assert cost == expected_cost217    218    @pytest.mark.asyncio219    async def test_transcribe_audio_success(self, provider, mock_azure_sdk):220        """Test successful audio transcription."""221        # Mock recognition result222        mock_result = Mock()223        mock_result.reason = "RecognizedSpeech"224        mock_result.text = "Hello world"225        mock_result.json = json.dumps({226            "NBest": [{227                "Confidence": 0.95,228                "Display": "Hello world",229                "Words": [230                    {"Word": "Hello", "Offset": 0, "Duration": 5000000, "Confidence": 0.98},231                    {"Word": "world", "Offset": 5000000, "Duration": 5000000, "Confidence": 0.92}232                ]233            }]234        })235        236        mock_recognizer = mock_azure_sdk.SpeechRecognizer.return_value237        mock_recognizer.recognize_once.return_value = mock_result238        239        # Test transcription240        audio_data = b"fake_audio_data"241        result = await provider.transcribe_audio(audio_data, "wav", "en-US")242        243        assert isinstance(result, TranscriptionResult)244        assert result.text == "Hello world"245        assert result.confidence == 0.95246        assert result.provider == provider.name247        assert result.language == "en-US"248        assert result.is_final249        assert len(result.word_timestamps) == 2250        assert result.word_timestamps[0].word == "Hello"251        assert result.word_timestamps[1].word == "world"252    253    @pytest.mark.asyncio254    async def test_transcribe_audio_no_match(self, provider, mock_azure_sdk):255        """Test transcription with no speech detected."""256        # Mock no match result257        mock_result = Mock()258        mock_result.reason = "NoMatch"259        mock_result.text = ""260        261        mock_recognizer = mock_azure_sdk.SpeechRecognizer.return_value262        mock_recognizer.recognize_once.return_value = mock_result263        264        # Test transcription265        audio_data = b"silent_audio_data"266        result = await provider.transcribe_audio(audio_data, "wav", "en-US")267        268        assert result.text == ""269        assert result.confidence == 0.0270        assert result.is_final271    272    @pytest.mark.asyncio273    async def test_transcribe_audio_unsupported_format(self, provider):274        """Test transcription with unsupported audio format."""275        audio_data = b"fake_audio_data"276        277        with pytest.raises(UnsupportedFormatError):278            await provider.transcribe_audio(audio_data, "xyz", "en-US")279    280    @pytest.mark.asyncio281    async def test_transcribe_audio_unsupported_language(self, provider):282        """Test transcription with unsupported language."""283        audio_data = b"fake_audio_data"284        285        with pytest.raises(UnsupportedLanguageError):286            await provider.transcribe_audio(audio_data, "wav", "xx-XX")287    288    @pytest.mark.asyncio289    async def test_transcribe_audio_quota_exceeded(self, provider, mock_azure_sdk):290        """Test transcription when quota is exceeded."""291        # Set usage to exceed monthly limit292        provider._usage_stats["audio_hours_this_month"] = 5.0  # At limit293        294        audio_data = b"fake_audio_data"295        296        with pytest.raises(QuotaExceededError):297            await provider.transcribe_audio(audio_data, "wav", "en-US", audio_duration=3600.0)  # 1 hour298    299    @pytest.mark.asyncio300    async def test_transcribe_audio_rate_limit_exceeded(self, provider, mock_azure_sdk):301        """Test transcription when rate limit is exceeded."""302        # Set usage to exceed minute limit303        provider._usage_stats["requests_this_minute"] = 20  # At limit304        305        audio_data = b"fake_audio_data"306        307        with pytest.raises(RateLimitExceededError):308            await provider.transcribe_audio(audio_data, "wav", "en-US")309    310    @pytest.mark.asyncio311    async def test_transcribe_streaming_success(self, provider, mock_azure_sdk):312        """Test successful streaming transcription."""313        # Mock streaming components314        mock_push_stream = Mock()315        mock_azure_sdk.audio.PushAudioInputStream.return_value = mock_push_stream316        317        mock_recognizer = mock_azure_sdk.SpeechRecognizer.return_value318        mock_recognizer.recognized = Mock()319        mock_recognizer.recognizing = Mock()320        mock_recognizer.start_continuous_recognition = Mock()321        mock_recognizer.stop_continuous_recognition = Mock()322        323        # Create async generator for audio stream324        async def audio_generator():325            yield b"chunk1"326            yield b"chunk2"327            yield b"chunk3"328        329        # Test streaming330        results = []331        async for result in provider.transcribe_streaming(audio_generator(), "wav", "en-US"):332            results.append(result)333        334        # Verify streaming setup335        mock_push_stream.write.assert_called()336        mock_push_stream.close.assert_called_once()337        mock_recognizer.start_continuous_recognition.assert_called_once()338        mock_recognizer.stop_continuous_recognition.assert_called_once()339    340    @pytest.mark.asyncio341    async def test_check_health_success(self, provider, mock_azure_sdk):342        """Test successful health check."""343        # Mock successful recognition344        mock_result = Mock()345        mock_result.reason = "RecognizedSpeech"346        347        mock_recognizer = mock_azure_sdk.SpeechRecognizer.return_value348        mock_recognizer.recognize_once.return_value = mock_result349        350        health = await provider.check_health()351        assert health is True352    353    @pytest.mark.asyncio354    async def test_check_health_failure(self, provider, mock_azure_sdk):355        """Test health check failure."""356        # Mock recognition failure357        mock_recognizer = mock_azure_sdk.SpeechRecognizer.return_value358        mock_recognizer.recognize_once.side_effect = Exception("Service unavailable")359        360        health = await provider.check_health()361        assert health is False362    363    @pytest.mark.asyncio364    async def test_get_quota_status(self, provider):365        """Test quota status retrieval."""366        # Set some usage367        provider._usage_stats["requests_this_minute"] = 5368        provider._usage_stats["requests_today"] = 100369        provider._usage_stats["audio_hours_this_month"] = 2.5370        371        quota_status = await provider.get_quota_status()372        373        assert "requests_per_minute" in quota_status374        assert "requests_per_day" in quota_status375        assert "audio_hours_per_month" in quota_status376        377        minute_quota = quota_status["requests_per_minute"]378        assert minute_quota.current_usage == 5379        assert minute_quota.limit == 20380        assert minute_quota.remaining == 15381        assert minute_quota.percentage_used == 0.25382        383        monthly_quota = quota_status["audio_hours_per_month"]384        assert monthly_quota.current_usage == 2.5385        assert monthly_quota.limit == 5.0386        assert monthly_quota.remaining == 2.5387        assert monthly_quota.percentage_used == 0.5388    389    @pytest.mark.asyncio390    async def test_reset_counters_minute(self, provider):391        """Test minute counter reset."""392        # Set usage and old timestamp393        provider._usage_stats["requests_this_minute"] = 10394        provider._usage_stats["last_minute_reset"] = datetime.now(timezone.utc) - timedelta(minutes=2)395        396        await provider._reset_counters_if_needed()397        398        assert provider._usage_stats["requests_this_minute"] == 0399    400    @pytest.mark.asyncio401    async def test_reset_counters_daily(self, provider):402        """Test daily counter reset."""403        # Set usage and old timestamp404        provider._usage_stats["requests_today"] = 100405        provider._usage_stats["last_daily_reset"] = datetime.now(timezone.utc).date() - timedelta(days=1)406        407        await provider._reset_counters_if_needed()408        409        assert provider._usage_stats["requests_today"] == 0410    411    @pytest.mark.asyncio412    async def test_reset_counters_monthly(self, provider):413        """Test monthly counter reset."""414        # Set usage and old timestamp415        provider._usage_stats["audio_hours_this_month"] = 3.0416        provider._usage_stats["last_monthly_reset"] = datetime.now(timezone.utc) - timedelta(days=32)417        418        await provider._reset_counters_if_needed()419        420        assert provider._usage_stats["audio_hours_this_month"] == 0.0421    422    def test_estimate_audio_duration_wav(self, provider):423        """Test audio duration estimation for WAV format."""424        audio_data = b"x" * 32000  # 32KB of data425        duration = provider._estimate_audio_duration(audio_data, "wav")426        expected = 32000 / (16000 * 2)  # 16kHz, 16-bit427        assert duration == expected428    429    def test_estimate_audio_duration_mp3(self, provider):430        """Test audio duration estimation for MP3 format."""431        audio_data = b"x" * 8000  # 8KB of data432        duration = provider._estimate_audio_duration(audio_data, "mp3")433        expected = 8000 / 8000  # Compressed format estimation434        assert duration == expected435    436    @pytest.mark.asyncio437    async def test_handle_azure_error_authentication(self, provider):438        """Test handling of authentication errors."""439        error = Exception("unauthorized access")440        441        with pytest.raises(ProviderAuthenticationError):442            await provider._handle_azure_error(error)443    444    @pytest.mark.asyncio445    async def test_handle_azure_error_rate_limit(self, provider):446        """Test handling of rate limit errors."""447        error = Exception("rate limit exceeded")448        449        with pytest.raises(RateLimitExceededError):450            await provider._handle_azure_error(error)451    452    @pytest.mark.asyncio453    async def test_handle_azure_error_timeout(self, provider):454        """Test handling of timeout errors."""455        error = Exception("request timed out")456        457        with pytest.raises(ProviderTimeoutError):458            await provider._handle_azure_error(error)459    460    @pytest.mark.asyncio461    async def test_handle_azure_error_unavailable(self, provider):462        """Test handling of service unavailable errors."""463        error = Exception("service unavailable")464        465        with pytest.raises(ProviderUnavailableError):466            await provider._handle_azure_error(error)467    468    @pytest.mark.asyncio469    async def test_handle_azure_error_generic(self, provider):470        """Test handling of generic errors."""471        error = Exception("unknown error")472        473        with pytest.raises(TranscriptionError):474            await provider._handle_azure_error(error)475    476    def test_extract_word_timestamps_success(self, provider):477        """Test successful word timestamp extraction."""478        detailed_result = {479            "NBest": [{480                "Words": [481                    {"Word": "Hello", "Offset": 0, "Duration": 5000000, "Confidence": 0.98},482                    {"Word": "world", "Offset": 5000000, "Duration": 5000000, "Confidence": 0.92}483                ]484            }]485        }486        487        timestamps = provider._extract_word_timestamps(detailed_result)488        489        assert len(timestamps) == 2490        assert timestamps[0].word == "Hello"491        assert timestamps[0].start_time == 0.0492        assert timestamps[0].end_time == 0.5493        assert timestamps[0].confidence == 0.98494        495        assert timestamps[1].word == "world"496        assert timestamps[1].start_time == 0.5497        assert timestamps[1].end_time == 1.0498        assert timestamps[1].confidence == 0.92499    500    def test_extract_word_timestamps_empty(self, provider):501        """Test word timestamp extraction with empty result."""502        detailed_result = {}503        timestamps = provider._extract_word_timestamps(detailed_result)504        assert timestamps == []505        506        detailed_result = {"NBest": []}507        timestamps = provider._extract_word_timestamps(detailed_result)508        assert timestamps == []509        510        detailed_result = {"NBest": [{"Words": []}]}511        timestamps = provider._extract_word_timestamps(detailed_result)512        assert timestamps == []513    514    def test_extract_word_timestamps_malformed(self, provider):515        """Test word timestamp extraction with malformed data."""516        detailed_result = {517            "NBest": [{518                "Words": [519                    {"Word": "Hello"},  # Missing timing info520                    {"Offset": 1000000, "Duration": 2000000}  # Missing word521                ]522            }]523        }524        525        # Should handle gracefully and return empty list526        timestamps = provider._extract_word_timestamps(detailed_result)527        assert len(timestamps) <= 2  # May extract partial data or return empty528    529    def test_get_next_month_start_december(self, provider):530        """Test next month calculation for December."""531        with patch('voice_control.providers.azure_speech_provider.datetime') as mock_dt:532            mock_dt.now.return_value = datetime(2023, 12, 15, 10, 30, 0, tzinfo=timezone.utc)533            mock_dt.side_effect = lambda *args, **kw: datetime(*args, **kw)534            535            next_month = provider._get_next_month_start()536            expected = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc)537            assert next_month == expected538    539    def test_get_next_month_start_regular(self, provider):540        """Test next month calculation for regular months."""541        with patch('voice_control.providers.azure_speech_provider.datetime') as mock_dt:542            mock_dt.now.return_value = datetime(2023, 6, 15, 10, 30, 0, tzinfo=timezone.utc)543            mock_dt.side_effect = lambda *args, **kw: datetime(*args, **kw)544            545            next_month = provider._get_next_month_start()546            expected = datetime(2023, 7, 1, 0, 0, 0, tzinfo=timezone.utc)547            assert next_month == expected548 549 550class TestAzureSpeechProviderFactory:551    """Test cases for Azure Speech provider factory function."""552    553    @patch('voice_control.providers.azure_speech_provider.AZURE_SPEECH_AVAILABLE', True)554    def test_create_azure_speech_provider_defaults(self):555        """Test factory function with default parameters."""556        provider = create_azure_speech_provider()557        558        assert provider.name == "azure_speech"559        assert provider.config.priority == 3560        assert provider.config.provider_type == ProviderType.AZURE_SPEECH561        assert provider.config.enabled562        assert provider.config.cost_per_minute == AzureSpeechProvider.PRICING["neural"] / 60.0563    564    @patch('voice_control.providers.azure_speech_provider.AZURE_SPEECH_AVAILABLE', True)565    def test_create_azure_speech_provider_custom(self):566        """Test factory function with custom parameters."""567        provider = create_azure_speech_provider(568            name="custom_azure",569            priority=1,570            enabled=False,571            api_credentials={572                "subscription_key": "custom_key",573                "region": "westus"574            }575        )576        577        assert provider.name == "custom_azure"578        assert provider.config.priority == 1579        assert not provider.config.enabled580        assert provider.config.api_credentials["subscription_key"] == "custom_key"581        assert provider.config.api_credentials["region"] == "westus"582 583 584class TestAzureSpeechProviderIntegration:585    """Integration tests for Azure Speech provider."""586    587    @pytest.fixture588    def integration_config(self):589        """Create configuration for integration tests."""590        return ProviderConfig(591            name="azure_integration_test",592            provider_type=ProviderType.AZURE_SPEECH,593            enabled=True,594            priority=1,595            free_tier_limits=AzureSpeechProvider.FREE_TIER_LIMITS,596            rate_limits={597                "requests_per_minute": 20,598                "requests_per_day": 5000599            },600            supported_formats=AzureSpeechProvider.SUPPORTED_FORMATS,601            supported_languages=AzureSpeechProvider.SUPPORTED_LANGUAGES,602            cost_per_minute=AzureSpeechProvider.PRICING["neural"] / 60.0,603            api_credentials={604                "subscription_key": "integration_test_key",605                "region": "eastus"606            },607            timeout_seconds=30,608            max_retries=2609        )610    611    @pytest.mark.asyncio612    @patch('voice_control.providers.azure_speech_provider.AZURE_SPEECH_AVAILABLE', True)613    async def test_full_transcription_workflow(self, integration_config, mock_azure_sdk):614        """Test complete transcription workflow."""615        # Mock successful recognition616        mock_result = Mock()617        mock_result.reason = "RecognizedSpeech"618        mock_result.text = "Integration test successful"619        mock_result.json = json.dumps({620            "NBest": [{621                "Confidence": 0.92,622                "Display": "Integration test successful"623            }]624        })625        626        mock_recognizer = mock_azure_sdk.SpeechRecognizer.return_value627        mock_recognizer.recognize_once.return_value = mock_result628        629        # Create provider and test630        provider = AzureSpeechProvider(integration_config)631        632        # Test transcription633        audio_data = b"integration_test_audio_data"634        result = await provider.transcribe_audio(audio_data, "wav", "en-US")635        636        # Verify results637        assert result.text == "Integration test successful"638        assert result.confidence == 0.92639        assert result.provider == "azure_integration_test"640        assert result.language == "en-US"641        assert result.processing_time > 0642        643        # Verify usage was tracked644        assert provider._usage_stats["requests_today"] == 1645        assert provider._usage_stats["requests_this_minute"] == 1646        assert provider._usage_stats["audio_hours_this_month"] > 0647    648    @pytest.mark.asyncio649    @patch('voice_control.providers.azure_speech_provider.AZURE_SPEECH_AVAILABLE', True)650    async def test_quota_management_workflow(self, integration_config, mock_azure_sdk):651        """Test quota management across multiple requests."""652        provider = AzureSpeechProvider(integration_config)653        654        # Make multiple requests to test quota tracking655        for i in range(5):656            # Mock successful recognition657            mock_result = Mock()658            mock_result.reason = "RecognizedSpeech"659            mock_result.text = f"Request {i+1}"660            mock_result.json = json.dumps({661                "NBest": [{"Confidence": 0.9, "Display": f"Request {i+1}"}]662            })663            664            mock_recognizer = mock_azure_sdk.SpeechRecognizer.return_value665            mock_recognizer.recognize_once.return_value = mock_result666            667            # Transcribe668            audio_data = b"test_audio"669            result = await provider.transcribe_audio(audio_data, "wav", "en-US")670            assert result.text == f"Request {i+1}"671        672        # Check quota status673        quota_status = await provider.get_quota_status()674        675        minute_quota = quota_status["requests_per_minute"]676        assert minute_quota.current_usage == 5677        assert minute_quota.remaining == 15678        assert minute_quota.percentage_used == 0.25679        680        # Verify usage stats681        assert provider._usage_stats["requests_today"] == 5682        assert provider._usage_stats["requests_this_minute"] == 5683 684 685if __name__ == "__main__":686    pytest.main([__file__])