Spaces:
Sleeping
Sleeping
| """ | |
| VoiceForge - TTS Service Unit Tests | |
| ------------------------------------ | |
| Tests for Text-to-Speech service functions: | |
| - Voice listing | |
| - Audio synthesis | |
| - SSML parsing | |
| - Streaming audio | |
| """ | |
| import pytest | |
| import asyncio | |
| from unittest.mock import Mock, patch, AsyncMock | |
| import struct | |
| class TestEdgeTTSService: | |
| """Test suite for Edge TTS service""" | |
| async def test_list_voices_returns_list(self): | |
| """Test that voice listing returns non-empty list""" | |
| with patch('app.services.edge_tts_service.EdgeTTSService') as MockService: | |
| mock_service = MockService.return_value | |
| mock_service.list_voices = AsyncMock(return_value=[ | |
| {"name": "en-US-Neural2-F", "gender": "Female", "language": "en-US"}, | |
| {"name": "en-US-Neural2-M", "gender": "Male", "language": "en-US"}, | |
| ]) | |
| voices = await mock_service.list_voices() | |
| assert isinstance(voices, list) | |
| assert len(voices) > 0 | |
| assert "name" in voices[0] | |
| async def test_synthesize_returns_audio_bytes(self): | |
| """Test that synthesis returns audio data""" | |
| with patch('app.services.edge_tts_service.EdgeTTSService') as MockService: | |
| mock_service = MockService.return_value | |
| # Return minimal valid audio bytes | |
| mock_audio = b'RIFF' + b'\x00' * 100 | |
| mock_service.synthesize = AsyncMock(return_value=mock_audio) | |
| audio = await mock_service.synthesize("Hello world", voice="en-US-Neural2-F") | |
| assert isinstance(audio, bytes) | |
| assert len(audio) > 0 | |
| async def test_synthesize_with_ssml(self): | |
| """Test SSML input processing""" | |
| ssml_input = """ | |
| <speak> | |
| <prosody rate="slow" pitch="+2st"> | |
| Hello, welcome to VoiceForge. | |
| </prosody> | |
| </speak> | |
| """ | |
| with patch('app.services.edge_tts_service.EdgeTTSService') as MockService: | |
| mock_service = MockService.return_value | |
| mock_service.synthesize_ssml = AsyncMock(return_value=b'AUDIO_DATA') | |
| audio = await mock_service.synthesize_ssml(ssml_input, voice="en-US-Neural2-F") | |
| assert audio is not None | |
| async def test_streaming_synthesis(self): | |
| """Test streaming audio generation""" | |
| with patch('app.services.edge_tts_service.EdgeTTSService') as MockService: | |
| mock_service = MockService.return_value | |
| # Simulate streaming chunks - must accept text parameter | |
| async def mock_stream(text): | |
| for i in range(3): | |
| yield b'chunk_' + str(i).encode() | |
| mock_service.stream_synthesize = mock_stream | |
| chunks = [] | |
| async for chunk in mock_service.stream_synthesize("Test text"): | |
| chunks.append(chunk) | |
| assert len(chunks) == 3 | |
| class TestGoogleTTSService: | |
| """Test suite for Google Cloud TTS""" | |
| async def test_google_tts_synthesize(self): | |
| """Test Google TTS synthesis""" | |
| with patch('app.services.tts_service.TTSService') as MockService: | |
| mock_service = MockService.return_value | |
| mock_service.synthesize_google = AsyncMock(return_value=b'MP3_AUDIO_DATA') | |
| audio = await mock_service.synthesize_google( | |
| text="Hello world", | |
| voice="en-US-Wavenet-D", | |
| audio_format="MP3" | |
| ) | |
| assert audio is not None | |
| assert isinstance(audio, bytes) | |
| class TestVoiceParameters: | |
| """Test voice parameter validation""" | |
| def test_valid_speed_range(self): | |
| """Test speed parameter is within valid range""" | |
| valid_speeds = [0.5, 1.0, 1.5, 2.0] | |
| for speed in valid_speeds: | |
| assert 0.25 <= speed <= 4.0 | |
| def test_valid_pitch_range(self): | |
| """Test pitch parameter is within valid range""" | |
| valid_pitches = [-5.0, 0.0, 5.0] | |
| for pitch in valid_pitches: | |
| assert -20.0 <= pitch <= 20.0 | |
| def test_invalid_speed_rejected(self): | |
| """Test that invalid speed values are caught""" | |
| invalid_speeds = [0.0, -1.0, 5.0] | |
| for speed in invalid_speeds: | |
| assert not (0.25 <= speed <= 4.0) | |
| # Run tests | |
| if __name__ == "__main__": | |
| pytest.main([__file__, "-v"]) | |