feat: add voiceclone audio API and bridge bootstrap #19
1 changed files with 104 additions and 0 deletions
|
|
@ -45,6 +45,110 @@ def test_health_exposes_current_version():
|
|||
assert response.json()["version"] == app.VERSION == "2.3.5"
|
||||
|
||||
|
||||
def test_tts_generate_returns_cloned_wav(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
class FakeResponse:
|
||||
status_code = 200
|
||||
content = b"RIFF" + b"test-wave"
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, **_kwargs):
|
||||
pass
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *_args):
|
||||
return False
|
||||
|
||||
async def post(self, url, json):
|
||||
captured["url"] = url
|
||||
captured["json"] = json
|
||||
return FakeResponse()
|
||||
|
||||
monkeypatch.setattr(app.httpx, "AsyncClient", FakeClient)
|
||||
with TestClient(app.app) as client:
|
||||
response = client.post(
|
||||
"/tts/generate",
|
||||
headers={"Authorization": "Bearer test-token"},
|
||||
json={"text": "Hallo Sascha", "voice": "deep_thought.mp3", "language": "de"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.headers["content-type"].startswith("audio/wav")
|
||||
assert response.content.startswith(b"RIFF")
|
||||
assert captured["json"]["voice_mode"] == "clone"
|
||||
assert captured["json"]["reference_audio_filename"] == "deep_thought.mp3"
|
||||
|
||||
|
||||
def test_tts_generate_validates_text_and_voice_before_backend(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
app.httpx,
|
||||
"AsyncClient",
|
||||
lambda **_kwargs: (_ for _ in ()).throw(AssertionError("backend must not be called")),
|
||||
)
|
||||
with TestClient(app.app) as client:
|
||||
empty = client.post(
|
||||
"/tts/generate",
|
||||
headers={"Authorization": "Bearer test-token"},
|
||||
json={"text": ""},
|
||||
)
|
||||
traversal = client.post(
|
||||
"/tts/generate",
|
||||
headers={"Authorization": "Bearer test-token"},
|
||||
json={"text": "Hallo", "voice": "../secret.wav"},
|
||||
)
|
||||
assert empty.status_code == 422
|
||||
assert traversal.status_code == 422
|
||||
|
||||
|
||||
def test_tts_generate_rejects_non_wav_backend_response(monkeypatch):
|
||||
class FakeResponse:
|
||||
status_code = 200
|
||||
content = b"not audio"
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, **_kwargs):
|
||||
pass
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *_args):
|
||||
return False
|
||||
|
||||
async def post(self, _url, json):
|
||||
return FakeResponse()
|
||||
|
||||
monkeypatch.setattr(app.httpx, "AsyncClient", FakeClient)
|
||||
with TestClient(app.app) as client:
|
||||
response = client.post(
|
||||
"/tts/generate",
|
||||
headers={"Authorization": "Bearer test-token"},
|
||||
json={"text": "Hallo"},
|
||||
)
|
||||
assert response.status_code == 502
|
||||
assert "invalid audio" in response.text
|
||||
|
||||
|
||||
def test_tts_bridge_deploy_installs_secrets_without_logging_them(monkeypatch):
|
||||
calls = []
|
||||
monkeypatch.setattr(app, "BUTLER_TOKEN", "butler-secret")
|
||||
monkeypatch.setattr(app, "_vault_cache", {})
|
||||
monkeypatch.setattr(app, "_ssh", lambda host, command, timeout=30: (calls.append((host, command, timeout)) or (0, "", "")))
|
||||
with TestClient(app.app) as client:
|
||||
response = client.post(
|
||||
"/tts/bridge/deploy",
|
||||
headers={"Authorization": "Bearer butler-secret"},
|
||||
json={"rotate_client_token": True},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["listen"] == "0.0.0.0:8099"
|
||||
assert len(response.json()["client_token"]) >= 32
|
||||
assert calls[0][0] == "sascha@10.5.85.5"
|
||||
assert "butler-secret" not in calls[0][1]
|
||||
|
||||
|
||||
def test_sysctl_audit_reads_fixed_keys_from_inventory_host(monkeypatch):
|
||||
payload = {
|
||||
"live": {"net.ipv4.tcp_congestion_control": "bbr"},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue