| import asyncio |
| import importlib |
| import sys |
| from types import SimpleNamespace |
|
|
| import pytest |
|
|
| import gradio as gr |
| from fastapi.responses import FileResponse |
|
|
|
|
| def test_start_render_api_registers_one_streamed_output() -> None: |
| sys.modules.pop("app", None) |
| module = importlib.import_module("app") |
|
|
| with gr.Blocks() as demo: |
| dependency = gr.api(module.start_render_api, api_name="start_render") |
|
|
| assert dependency.get("outputs") |
| assert len(dependency["outputs"]) == 1 |
|
|
|
|
| def test_start_render_api_adds_public_audio_url_to_completed_chapters(monkeypatch) -> None: |
| sys.modules.pop("app", None) |
| module = importlib.import_module("app") |
|
|
| class FakeSynthesisService: |
| def get_job(self, session_id): |
| return SimpleNamespace(status="idle") |
|
|
| def render(self, **kwargs): |
| yield { |
| "type": "chapter_done", |
| "session_id": kwargs["session_id"], |
| "chapter_id": "c1", |
| "duration_seconds": 12, |
| "overall_progress": 0.5, |
| "output_path": "/tmp/scriptorium/session-a/renders/001-c1.wav", |
| "backend": "modal", |
| "model": "magpie", |
| } |
| yield { |
| "type": "completed", |
| "session_id": kwargs["session_id"], |
| "outputs": ["/tmp/scriptorium/session-a/renders/001-c1.wav"], |
| "backend": "modal", |
| "model": "magpie", |
| } |
|
|
| monkeypatch.setattr(module, "synthesis_service", FakeSynthesisService()) |
| monkeypatch.setattr(module, "_selected_book", lambda session_id, selected_ids: {"chapters": [{"id": "c1", "included": True}]}) |
| monkeypatch.setattr(module, "_voice_config_for_backend", lambda voice_config, session_id: voice_config) |
| monkeypatch.setattr(module.store, "save_json", lambda *args, **kwargs: None) |
|
|
| events = list( |
| module.start_render_api( |
| session_id="session-a", |
| selected_chapter_ids=["c1"], |
| voice_config={"mode": "auto"}, |
| diffusion_steps=32, |
| speed=1.0, |
| ) |
| ) |
|
|
| chapter_done = events[0] |
| assert chapter_done["type"] == "chapter_done" |
| assert chapter_done["url"] == "/files/session-a/renders/001-c1.wav" |
| assert chapter_done["backend"] == "modal" |
| assert chapter_done["model"] == "magpie" |
|
|
|
|
| def test_generate_preview_api_exposes_model_and_backend(monkeypatch) -> None: |
| sys.modules.pop("app", None) |
| module = importlib.import_module("app") |
|
|
| class FakeSynthesisService: |
| def generate_preview(self, **kwargs): |
| output_path = kwargs["output_path"] |
| output_path.parent.mkdir(parents=True, exist_ok=True) |
| output_path.write_bytes(b"RIFFpreview") |
| return { |
| "duration_seconds": 6, |
| "sample_rate": 24000, |
| "backend": "modal", |
| "model": "magpie", |
| } |
|
|
| monkeypatch.setattr(module, "synthesis_service", FakeSynthesisService()) |
| monkeypatch.setattr( |
| module, |
| "_book_payload", |
| lambda _session_id: {"chapters": [{"id": "c1", "text": "hello world", "title": "One"}]}, |
| ) |
| monkeypatch.setattr(module, "_voice_config_for_backend", lambda voice_config, session_id: voice_config) |
|
|
| payload = module.generate_preview_api( |
| session_id="session-a", |
| chapter_id="c1", |
| voice_config={"model": "magpie", "backend": "modal", "speaker": "Sofia", "language": "en"}, |
| diffusion_steps=32, |
| speed=1.0, |
| ) |
|
|
| assert payload["url"] == "/files/session-a/previews/c1.wav" |
| assert payload["backend"] == "modal" |
| assert payload["model"] == "magpie" |
|
|
|
|
| def test_serve_generated_file_supports_attachment_downloads(tmp_path, monkeypatch) -> None: |
| sys.modules.pop("app", None) |
| module = importlib.import_module("app") |
|
|
| session_root = tmp_path / "session-a" |
| export_path = session_root / "exports" / "reading-room.m4a" |
| export_path.parent.mkdir(parents=True, exist_ok=True) |
| export_path.write_bytes(b"m4a") |
|
|
| monkeypatch.setattr(module, "_session_root", lambda _session_id: session_root) |
|
|
| response = asyncio.run( |
| module.serve_generated_file( |
| session_id="session-a", |
| kind="exports", |
| filename="reading-room.m4a", |
| download=True, |
| ) |
| ) |
|
|
| assert isinstance(response, FileResponse) |
| assert "attachment" in response.headers["content-disposition"] |
| assert "reading-room.m4a" in response.headers["content-disposition"] |
|
|