Skip to content

Commit d579bfe

Browse files
committed
feat: add output directory and save audio fixture for integration tests
Signed-off-by: James Ding <jamesding365@gmail.com>
1 parent ba4ab45 commit d579bfe

4 files changed

Lines changed: 75 additions & 13 deletions

File tree

.github/workflows/python.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ jobs:
8585
env:
8686
FISH_AUDIO_API_KEY: ${{ secrets.FISH_AUDIO_API_KEY }}
8787

88+
- name: Upload Test Artifacts
89+
uses: actions/upload-artifact@v4
90+
if: always()
91+
with:
92+
name: test-audio-output
93+
path: output/
94+
8895
build:
8996
name: Build Package
9097
runs-on: ubuntu-latest

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,7 @@ cython_debug/
159159
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160160
# and can be added to the global gitignore or merged into this file. For a more nuclear
161161
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
162-
#.idea/
162+
.idea/
163+
164+
# Test output
165+
output/

tests/integration/conftest.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Fixtures for integration tests."""
22

33
import os
4+
from pathlib import Path
45

56
import pytest
67
from dotenv import load_dotenv
@@ -9,6 +10,10 @@
910

1011
load_dotenv()
1112

13+
# Create output directory for test audio files
14+
OUTPUT_DIR = Path("output")
15+
OUTPUT_DIR.mkdir(exist_ok=True)
16+
1217

1318
@pytest.fixture
1419
def api_key():
@@ -33,3 +38,29 @@ async def async_client(api_key):
3338
client = AsyncFishAudio(api_key=api_key)
3439
yield client
3540
await client.close()
41+
42+
43+
@pytest.fixture
44+
def save_audio():
45+
"""Fixture that provides a helper to save audio chunks to the output directory.
46+
47+
Returns:
48+
A callable that takes audio chunks and filename and saves to output/
49+
"""
50+
51+
def _save(audio_chunks: list[bytes], filename: str) -> Path:
52+
"""Save audio chunks to output directory.
53+
54+
Args:
55+
audio_chunks: List of audio byte chunks
56+
filename: Name of the output file (including extension)
57+
58+
Returns:
59+
Path to the saved file
60+
"""
61+
complete_audio = b"".join(audio_chunks)
62+
output_file = OUTPUT_DIR / filename
63+
output_file.write_bytes(complete_audio)
64+
return output_file
65+
66+
return _save

tests/integration/test_tts_integration.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import pytest
66

77
from fishaudio.types import Prosody, TTSConfig
8-
from fishaudio.types.shared import Model
8+
from fishaudio.types.shared import AudioFormat, Model
99

1010

1111
class TestTTSIntegration:
1212
"""Test TTS with real API."""
1313

14-
def test_basic_tts(self, client):
14+
def test_basic_tts(self, client, save_audio):
1515
"""Test basic text-to-speech generation."""
1616
audio_chunks = list(client.tts.convert(text="Hello, this is a test."))
1717

@@ -20,18 +20,24 @@ def test_basic_tts(self, client):
2020
complete_audio = b"".join(audio_chunks)
2121
assert len(complete_audio) > 1000 # Should have substantial audio data
2222

23-
def test_tts_with_different_formats(self, client):
23+
# Write to output directory
24+
save_audio(audio_chunks, "test_basic_tts.mp3")
25+
26+
def test_tts_with_different_formats(self, client, save_audio):
2427
"""Test TTS with different audio formats."""
25-
formats = ["mp3", "wav", "pcm"]
28+
formats = get_args(AudioFormat)
2629

2730
for fmt in formats:
2831
config = TTSConfig(format=fmt, chunk_length=100)
2932
audio_chunks = list(
30-
client.tts.convert(text="Testing format", config=config)
33+
client.tts.convert(text=f"Testing format {fmt}", config=config)
3134
)
3235
assert len(audio_chunks) > 0, f"Failed for format: {fmt}"
3336

34-
def test_tts_with_prosody(self, client):
37+
# Write to output directory
38+
save_audio(audio_chunks, f"test_format_{fmt}.{fmt}")
39+
40+
def test_tts_with_prosody(self, client, save_audio):
3541
"""Test TTS with prosody settings."""
3642
prosody = Prosody(speed=1.2, volume=0.5)
3743
config = TTSConfig(prosody=prosody)
@@ -42,21 +48,27 @@ def test_tts_with_prosody(self, client):
4248

4349
assert len(audio_chunks) > 0
4450

45-
def test_tts_with_different_backends(self, client):
46-
"""Test TTS with different backend models."""
51+
# Write to output directory
52+
save_audio(audio_chunks, "test_prosody.mp3")
53+
54+
def test_tts_with_different_models(self, client, save_audio):
55+
"""Test TTS with different models."""
4756
models = get_args(Model)
4857

4958
for model in models:
5059
try:
5160
audio_chunks = list(
52-
client.tts.convert(text="Testing model", model=model)
61+
client.tts.convert(text=f"Testing model {model}", model=model)
5362
)
5463
assert len(audio_chunks) > 0, f"Failed for model: {model}"
64+
65+
# Write to output directory
66+
save_audio(audio_chunks, f"test_model_{model}.mp3")
5567
except Exception as e:
5668
# Some models might not be available
5769
pytest.skip(f"Model {model} not available: {e}")
5870

59-
def test_tts_longer_text(self, client):
71+
def test_tts_longer_text(self, client, save_audio):
6072
"""Test TTS with longer text."""
6173
long_text = "This is a longer piece of text for testing. " * 10
6274
config = TTSConfig(chunk_length=200)
@@ -68,6 +80,9 @@ def test_tts_longer_text(self, client):
6880
# Longer text should produce more audio
6981
assert len(complete_audio) > 5000
7082

83+
# Write to output directory
84+
save_audio(audio_chunks, "test_longer_text.mp3")
85+
7186
def test_tts_empty_text_should_fail(self, client):
7287
"""Test that empty text is handled."""
7388
# This might succeed with silence or fail - test behavior
@@ -84,7 +99,7 @@ class TestAsyncTTSIntegration:
8499
"""Test async TTS with real API."""
85100

86101
@pytest.mark.asyncio
87-
async def test_basic_async_tts(self, async_client):
102+
async def test_basic_async_tts(self, async_client, save_audio):
88103
"""Test basic async text-to-speech generation."""
89104
audio_chunks = []
90105
async for chunk in async_client.tts.convert(text="Hello from async"):
@@ -94,8 +109,11 @@ async def test_basic_async_tts(self, async_client):
94109
complete_audio = b"".join(audio_chunks)
95110
assert len(complete_audio) > 1000
96111

112+
# Write to output directory
113+
save_audio(audio_chunks, "test_async_basic.mp3")
114+
97115
@pytest.mark.asyncio
98-
async def test_async_tts_with_prosody(self, async_client):
116+
async def test_async_tts_with_prosody(self, async_client, save_audio):
99117
"""Test async TTS with prosody."""
100118
prosody = Prosody(speed=0.8, volume=-0.2)
101119
config = TTSConfig(prosody=prosody)
@@ -107,3 +125,6 @@ async def test_async_tts_with_prosody(self, async_client):
107125
audio_chunks.append(chunk)
108126

109127
assert len(audio_chunks) > 0
128+
129+
# Write to output directory
130+
save_audio(audio_chunks, "test_async_prosody.mp3")

0 commit comments

Comments
 (0)