Implement audio generation endpoint
- Add POST /api/tts endpoint with full request validation - Validate text, language code, speed range, and roles list - Integrate TTSService to generate multi-voice audiobooks - Fix Kokoro pipeline generator handling to properly collect audio - Stream audio response with correct Content-Type (audio/wav) - Add Content-Disposition header with filename (audiobook.wav) - Handle errors gracefully with appropriate HTTP status codes - Add comprehensive test suite for endpoint validation Requirements: 6.1, 6.7, 6.8, 9.5
This commit is contained in:
174
backend/test_generation_endpoint.py
Normal file
174
backend/test_generation_endpoint.py
Normal file
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test script for audio generation endpoint."""
|
||||
import urllib.request
|
||||
import json
|
||||
import sys
|
||||
|
||||
BASE_URL = 'http://localhost:8001'
|
||||
|
||||
def test_generation_endpoint_validation():
|
||||
"""Test validation of generation endpoint."""
|
||||
print("Testing audio generation endpoint validation...")
|
||||
|
||||
# Test 1: Missing text field
|
||||
print("\n1. Testing empty text validation...")
|
||||
try:
|
||||
data = json.dumps({
|
||||
"text": "",
|
||||
"lang": "a",
|
||||
"roles": [{"id": "Narrator", "voice": "af_bella"}],
|
||||
"speed": 1.0
|
||||
}).encode('utf-8')
|
||||
|
||||
req = urllib.request.Request(
|
||||
f'{BASE_URL}/api/tts',
|
||||
data=data,
|
||||
headers={'Content-Type': 'application/json'}
|
||||
)
|
||||
urllib.request.urlopen(req)
|
||||
print("✗ Should have rejected empty text")
|
||||
return False
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 400:
|
||||
error_data = json.loads(e.read().decode())
|
||||
print(f"✓ Correctly rejected empty text: {error_data['message']}")
|
||||
else:
|
||||
print(f"✗ Unexpected error code: {e.code}")
|
||||
return False
|
||||
|
||||
# Test 2: Invalid language code
|
||||
print("\n2. Testing invalid language code...")
|
||||
try:
|
||||
data = json.dumps({
|
||||
"text": "Hello world",
|
||||
"lang": "invalid",
|
||||
"roles": [{"id": "Narrator", "voice": "af_bella"}],
|
||||
"speed": 1.0
|
||||
}).encode('utf-8')
|
||||
|
||||
req = urllib.request.Request(
|
||||
f'{BASE_URL}/api/tts',
|
||||
data=data,
|
||||
headers={'Content-Type': 'application/json'}
|
||||
)
|
||||
urllib.request.urlopen(req)
|
||||
print("✗ Should have rejected invalid language code")
|
||||
return False
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 400:
|
||||
error_data = json.loads(e.read().decode())
|
||||
print(f"✓ Correctly rejected invalid language: {error_data['message']}")
|
||||
else:
|
||||
print(f"✗ Unexpected error code: {e.code}")
|
||||
return False
|
||||
|
||||
# Test 3: Invalid speed value
|
||||
print("\n3. Testing invalid speed value...")
|
||||
try:
|
||||
data = json.dumps({
|
||||
"text": "Hello world",
|
||||
"lang": "a",
|
||||
"roles": [{"id": "Narrator", "voice": "af_bella"}],
|
||||
"speed": 2.0 # Out of range
|
||||
}).encode('utf-8')
|
||||
|
||||
req = urllib.request.Request(
|
||||
f'{BASE_URL}/api/tts',
|
||||
data=data,
|
||||
headers={'Content-Type': 'application/json'}
|
||||
)
|
||||
urllib.request.urlopen(req)
|
||||
print("✗ Should have rejected invalid speed")
|
||||
return False
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 400:
|
||||
error_data = json.loads(e.read().decode())
|
||||
print(f"✓ Correctly rejected invalid speed: {error_data['message']}")
|
||||
else:
|
||||
print(f"✗ Unexpected error code: {e.code}")
|
||||
return False
|
||||
|
||||
# Test 4: Empty roles list
|
||||
print("\n4. Testing empty roles list...")
|
||||
try:
|
||||
data = json.dumps({
|
||||
"text": "Hello world",
|
||||
"lang": "a",
|
||||
"roles": [],
|
||||
"speed": 1.0
|
||||
}).encode('utf-8')
|
||||
|
||||
req = urllib.request.Request(
|
||||
f'{BASE_URL}/api/tts',
|
||||
data=data,
|
||||
headers={'Content-Type': 'application/json'}
|
||||
)
|
||||
urllib.request.urlopen(req)
|
||||
print("✗ Should have rejected empty roles list")
|
||||
return False
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 400:
|
||||
error_data = json.loads(e.read().decode())
|
||||
print(f"✓ Correctly rejected empty roles: {error_data['message']}")
|
||||
else:
|
||||
print(f"✗ Unexpected error code: {e.code}")
|
||||
return False
|
||||
|
||||
# Test 5: Valid request with simple text
|
||||
print("\n5. Testing valid request...")
|
||||
try:
|
||||
data = json.dumps({
|
||||
"text": "Hello, this is a test.",
|
||||
"lang": "a",
|
||||
"roles": [{"id": "Narrator", "voice": "af_bella"}],
|
||||
"speed": 1.0
|
||||
}).encode('utf-8')
|
||||
|
||||
req = urllib.request.Request(
|
||||
f'{BASE_URL}/api/tts',
|
||||
data=data,
|
||||
headers={'Content-Type': 'application/json'}
|
||||
)
|
||||
response = urllib.request.urlopen(req)
|
||||
|
||||
# Check Content-Type header
|
||||
content_type = response.headers.get('Content-Type')
|
||||
if content_type != 'audio/wav':
|
||||
print(f"✗ Wrong Content-Type: {content_type}")
|
||||
return False
|
||||
print(f"✓ Correct Content-Type: {content_type}")
|
||||
|
||||
# Check Content-Disposition header
|
||||
content_disposition = response.headers.get('Content-Disposition')
|
||||
if 'audiobook.wav' not in content_disposition:
|
||||
print(f"✗ Wrong Content-Disposition: {content_disposition}")
|
||||
return False
|
||||
print(f"✓ Correct Content-Disposition: {content_disposition}")
|
||||
|
||||
# Check that we got audio data
|
||||
audio_data = response.read()
|
||||
if len(audio_data) == 0:
|
||||
print("✗ No audio data received")
|
||||
return False
|
||||
print(f"✓ Received audio data: {len(audio_data)} bytes")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Valid request failed: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("=" * 60)
|
||||
print("Audio Generation Endpoint Tests")
|
||||
print("=" * 60)
|
||||
|
||||
success = test_generation_endpoint_validation()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
if success:
|
||||
print("✓ All tests passed!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("✗ Some tests failed")
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user