feat: migrate Mac mini services into Reyna CLI
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from reyna_cli.cli import app
|
||||
|
||||
runner = CliRunner()
|
||||
|
||||
|
||||
def test_media_execution_local_tts_delegates(monkeypatch, tmp_path):
|
||||
from reyna_cli import media_execution
|
||||
|
||||
target = tmp_path / "speech.wav"
|
||||
|
||||
def fake_synthesize(text, output, **kwargs):
|
||||
output.write_bytes(b"RIFFfake")
|
||||
return output
|
||||
|
||||
monkeypatch.setattr(media_execution, "synthesize_wav", fake_synthesize)
|
||||
result = media_execution.generate_local_tts("hello", target, voice="Alex")
|
||||
assert result["ok"] is True
|
||||
assert result["filePath"] == str(target)
|
||||
assert target.read_bytes() == b"RIFFfake"
|
||||
|
||||
|
||||
def test_media_execution_kokoro_writes_returned_file(monkeypatch, tmp_path):
|
||||
from reyna_cli import media_execution
|
||||
|
||||
source = tmp_path / "source.wav"
|
||||
source.write_bytes(b"wav")
|
||||
target = tmp_path / "target.wav"
|
||||
monkeypatch.setattr(media_execution, "_json_request", lambda *args, **kwargs: {"filePath": str(source), "voice": "af_heart"})
|
||||
result = media_execution.generate_kokoro("hello", target, url="http://127.0.0.1:7332")
|
||||
assert result["ok"] is True
|
||||
assert target.read_bytes() == b"wav"
|
||||
|
||||
|
||||
def test_media_execution_gemini_requires_key(monkeypatch, tmp_path):
|
||||
from reyna_cli import media_execution
|
||||
|
||||
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
|
||||
try:
|
||||
media_execution.generate_gemini_image("a tree", tmp_path / "tree.png")
|
||||
except RuntimeError as exc:
|
||||
assert "GEMINI_API_KEY" in str(exc)
|
||||
else:
|
||||
raise AssertionError("missing Gemini key must fail before network")
|
||||
|
||||
|
||||
def test_media_execution_gemini_decodes_image(monkeypatch, tmp_path):
|
||||
from reyna_cli import media_execution
|
||||
|
||||
monkeypatch.setenv("GEMINI_API_KEY", "test-only")
|
||||
monkeypatch.setattr(media_execution, "_json_request", lambda *args, **kwargs: {"id": "i1", "output_image": {"data": "aGVsbG8=", "mime_type": "image/png"}})
|
||||
target = tmp_path / "tree.png"
|
||||
result = media_execution.generate_gemini_image("a tree", target)
|
||||
assert result["path"] == str(target)
|
||||
assert target.read_bytes() == b"hello"
|
||||
|
||||
|
||||
def test_cli_exposes_execution_commands():
|
||||
for args in [
|
||||
["local-services", "speech", "--help"],
|
||||
["local-services", "kokoro", "--help"],
|
||||
["local-services", "voicebox", "--help"],
|
||||
["local-services", "image", "--help"],
|
||||
]:
|
||||
result = runner.invoke(app, args)
|
||||
assert result.exit_code == 0, result.stdout
|
||||
assert "generate" in result.stdout
|
||||
|
||||
|
||||
def test_cli_image_rejects_unknown_engine():
|
||||
result = runner.invoke(app, ["local-services", "image", "generate", "tree", "--engine", "unknown", "--json"])
|
||||
assert result.exit_code == 1
|
||||
assert "codex or gemini" in result.stdout
|
||||
assert json.loads(result.stdout)["ok"] is False
|
||||
|
||||
|
||||
def test_speech_file_execution_uses_cached_binary(monkeypatch, tmp_path):
|
||||
from reyna_cli import speech_execution
|
||||
|
||||
audio = tmp_path / "sample.wav"
|
||||
audio.write_bytes(b"wav")
|
||||
binary = tmp_path / "transcriber"
|
||||
binary.write_bytes(b"binary")
|
||||
|
||||
monkeypatch.setattr(speech_execution, "_ensure_binary", lambda: binary)
|
||||
monkeypatch.setattr(
|
||||
speech_execution.subprocess,
|
||||
"run",
|
||||
lambda *args, **kwargs: type("Result", (), {"returncode": 0, "stdout": '{"ok":true,"transcript":"hello"}', "stderr": ""})(),
|
||||
)
|
||||
result = speech_execution.transcribe_file(audio, locale="en-US")
|
||||
assert result["transcript"] == "hello"
|
||||
assert result["source"] == "reyna_cli_direct"
|
||||
|
||||
|
||||
def test_speech_file_execution_rejects_missing_audio(tmp_path):
|
||||
from reyna_cli.speech_execution import SpeechExecutionError, transcribe_file
|
||||
|
||||
try:
|
||||
transcribe_file(tmp_path / "missing.wav")
|
||||
except SpeechExecutionError as exc:
|
||||
assert "not found" in str(exc)
|
||||
else:
|
||||
raise AssertionError("missing audio must fail before compilation")
|
||||
|
||||
|
||||
def test_live_session_frames_audio_and_reads_event(monkeypatch, tmp_path):
|
||||
from reyna_cli import speech_live
|
||||
|
||||
class FakeStdin:
|
||||
def __init__(self):
|
||||
self.data = bytearray()
|
||||
def write(self, value):
|
||||
self.data.extend(value)
|
||||
def flush(self):
|
||||
pass
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
class FakeProcess:
|
||||
def __init__(self):
|
||||
self.stdin = FakeStdin()
|
||||
self.stdout = []
|
||||
def poll(self):
|
||||
return None
|
||||
def wait(self, timeout=None):
|
||||
return 0
|
||||
def kill(self):
|
||||
pass
|
||||
|
||||
fake = FakeProcess()
|
||||
monkeypatch.setattr(speech_live, "_ensure_binary", lambda: tmp_path / "live")
|
||||
monkeypatch.setattr(speech_live.subprocess, "Popen", lambda *args, **kwargs: fake)
|
||||
session = speech_live.SpeechLiveSession("en-US")
|
||||
session.events.put({"ok": True, "event": "final", "text": "hello"})
|
||||
result = session.transcribe_chunk(b"wav")
|
||||
assert result["text"] == "hello"
|
||||
assert bytes(fake.stdin.data[:4]) == b"\x00\x00\x00\x03"
|
||||
session.close()
|
||||
Reference in New Issue
Block a user