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"]