You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
6.0 KiB
146 lines
6.0 KiB
import os
|
|
import tempfile
|
|
import pytest
|
|
from unittest.mock import patch, Mock, MagicMock
|
|
|
|
|
|
class TestCambAITTSUnit:
|
|
"""Unit tests for CambAITTS — all mocked, no API key needed."""
|
|
|
|
def test_max_chars_is_5000(self):
|
|
with patch("TTS.cambai.settings") as mock_settings:
|
|
from TTS.cambai import CambAITTS
|
|
tts = CambAITTS()
|
|
assert tts.max_chars == 5000
|
|
|
|
def test_initialize_raises_without_api_key(self):
|
|
with patch("TTS.cambai.settings") as mock_settings:
|
|
mock_settings.config = {"settings": {"tts": {"cambai_api_key": ""}}}
|
|
from TTS.cambai import CambAITTS
|
|
tts = CambAITTS()
|
|
with pytest.raises(ValueError, match="CAMB AI API key"):
|
|
tts.initialize()
|
|
|
|
def test_initialize_creates_client(self):
|
|
with patch("TTS.cambai.settings") as mock_settings, \
|
|
patch("TTS.cambai.CambAI") as mock_camb:
|
|
mock_settings.config = {"settings": {"tts": {"cambai_api_key": "test-key"}}}
|
|
from TTS.cambai import CambAITTS
|
|
tts = CambAITTS()
|
|
tts.initialize()
|
|
mock_camb.assert_called_once_with(api_key="test-key")
|
|
assert tts.client is not None
|
|
|
|
def test_run_generates_mp3(self):
|
|
with patch("TTS.cambai.settings") as mock_settings, \
|
|
patch("TTS.cambai.CambAI") as mock_camb_cls, \
|
|
patch("TTS.cambai.save_stream_to_file") as mock_save, \
|
|
patch("TTS.cambai.StreamTtsOutputConfiguration") as mock_config:
|
|
mock_settings.config = {
|
|
"settings": {"tts": {
|
|
"cambai_api_key": "test-key",
|
|
"cambai_voice_id": "147320",
|
|
"cambai_language": "en-us",
|
|
"cambai_speech_model": "mars-flash",
|
|
}}
|
|
}
|
|
mock_client = Mock()
|
|
mock_client.text_to_speech.tts.return_value = iter([b"fake-audio"])
|
|
mock_camb_cls.return_value = mock_client
|
|
|
|
from TTS.cambai import CambAITTS
|
|
tts = CambAITTS()
|
|
tts.run("Hello world", "/tmp/test.mp3", random_voice=False)
|
|
|
|
mock_client.text_to_speech.tts.assert_called_once()
|
|
call_kwargs = mock_client.text_to_speech.tts.call_args
|
|
assert call_kwargs.kwargs["text"] == "Hello world"
|
|
assert call_kwargs.kwargs["voice_id"] == 147320
|
|
assert call_kwargs.kwargs["language"] == "en-us"
|
|
assert call_kwargs.kwargs["speech_model"] == "mars-flash"
|
|
mock_save.assert_called_once()
|
|
assert mock_save.call_args[0][1] == "/tmp/test.mp3"
|
|
|
|
def test_run_reads_config_voice_id(self):
|
|
with patch("TTS.cambai.settings") as mock_settings, \
|
|
patch("TTS.cambai.CambAI") as mock_camb_cls, \
|
|
patch("TTS.cambai.save_stream_to_file"), \
|
|
patch("TTS.cambai.StreamTtsOutputConfiguration"):
|
|
mock_settings.config = {
|
|
"settings": {"tts": {
|
|
"cambai_api_key": "test-key",
|
|
"cambai_voice_id": "99999",
|
|
"cambai_language": "es-es",
|
|
"cambai_speech_model": "mars-pro",
|
|
}}
|
|
}
|
|
|
|
mock_client = Mock()
|
|
mock_client.text_to_speech.tts.return_value = iter([b"audio"])
|
|
mock_camb_cls.return_value = mock_client
|
|
|
|
from TTS.cambai import CambAITTS
|
|
tts = CambAITTS()
|
|
tts.run("test", "/tmp/out.mp3")
|
|
|
|
call_kwargs = mock_client.text_to_speech.tts.call_args.kwargs
|
|
assert call_kwargs["voice_id"] == 99999
|
|
assert call_kwargs["language"] == "es-es"
|
|
assert call_kwargs["speech_model"] == "mars-pro"
|
|
|
|
def test_random_voice_picks_from_list(self):
|
|
with patch("TTS.cambai.settings") as mock_settings, \
|
|
patch("TTS.cambai.CambAI") as mock_camb_cls, \
|
|
patch("TTS.cambai.save_stream_to_file"), \
|
|
patch("TTS.cambai.StreamTtsOutputConfiguration"), \
|
|
patch("TTS.cambai.random") as mock_random:
|
|
mock_settings.config = {"settings": {"tts": {"cambai_api_key": "test-key"}}}
|
|
mock_client = Mock()
|
|
mock_client.voice_cloning.list_voices.return_value = [
|
|
{"id": 111, "voice_name": "Voice A"},
|
|
{"id": 222, "voice_name": "Voice B"},
|
|
]
|
|
mock_client.text_to_speech.tts.return_value = iter([b"audio"])
|
|
mock_camb_cls.return_value = mock_client
|
|
mock_random.choice.return_value = {"id": 222, "voice_name": "Voice B"}
|
|
|
|
from TTS.cambai import CambAITTS
|
|
tts = CambAITTS()
|
|
tts.run("test", "/tmp/out.mp3", random_voice=True)
|
|
|
|
mock_client.voice_cloning.list_voices.assert_called_once()
|
|
mock_random.choice.assert_called_once()
|
|
|
|
|
|
@pytest.mark.integration
|
|
class TestCambAITTSIntegration:
|
|
"""Integration tests — require CAMB_API_KEY env var."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def skip_without_key(self):
|
|
if not os.environ.get("CAMB_API_KEY"):
|
|
pytest.skip("CAMB_API_KEY not set")
|
|
|
|
def test_real_api_generates_audio(self):
|
|
from camb.client import CambAI, save_stream_to_file
|
|
from camb.types import StreamTtsOutputConfiguration
|
|
|
|
client = CambAI(api_key=os.environ["CAMB_API_KEY"])
|
|
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
|
|
filepath = f.name
|
|
|
|
try:
|
|
stream = client.text_to_speech.tts(
|
|
text="Integration test for RedditVideoMakerBot.",
|
|
language="en-us",
|
|
voice_id=147320,
|
|
speech_model="mars-flash",
|
|
output_configuration=StreamTtsOutputConfiguration(format="mp3"),
|
|
)
|
|
save_stream_to_file(stream, filepath)
|
|
assert os.path.exists(filepath)
|
|
assert os.path.getsize(filepath) > 0
|
|
finally:
|
|
if os.path.exists(filepath):
|
|
os.remove(filepath)
|