from pathlib import Path ROOT = Path(__file__).resolve().parents[1] NARRATION_ROUTES = ROOT / "src/landppt/web/route_modules/narration_routes.py" EXPORT_ROUTES = ROOT / "src/landppt/web/route_modules/export_routes.py" BACKGROUND_TASKS = ROOT / "src/landppt/services/background_tasks.py" NARRATION_JS = ROOT / "src/landppt/web/static/js/pages/project/slides_editor/projectEditorNarration.js" SPEECH_MANAGE_JS = ROOT / "src/landppt/web/static/js/pages/project/slides_editor/projectSlidesEditor.speechScriptsManage.js" SETTINGS_TEMPLATE = ROOT / "src/landppt/web/templates/components/settings/ai_config/content_1.html" NARRATION_SERVICE = ROOT / "src/landppt/services/narration_service.py" DB_CONFIG_SERVICE = ROOT / "src/landppt/services/db_config_service.py" TEMPLATE_FILE = ROOT / "src/landppt/web/templates/pages/project/project_slides_editor.html" def _read(path: Path) -> str: return path.read_text(encoding="utf-8") def test_narration_audio_export_route_and_download_support_are_wired(): narration_routes_text = _read(NARRATION_ROUTES) export_routes_text = _read(EXPORT_ROUTES) background_tasks_text = _read(BACKGROUND_TASKS) assert '@router.post("/api/projects/{project_id}/export/narration-audio")' in narration_routes_text assert "async def export_narration_audio(" in narration_routes_text assert 'task_type="narration_audio_export"' in narration_routes_text assert '"audio_path": zip_path' in narration_routes_text assert "narration_audio_export" in background_tasks_text assert "narration_audio_export" in export_routes_text assert '"audio_path"' in export_routes_text assert "X-Export-Method\": \"Narration-Audio\"" in export_routes_text def test_narration_generation_progress_updates_are_wired(): narration_routes_text = _read(NARRATION_ROUTES) narration_service_text = _read(NARRATION_SERVICE) assert 'task_id = task_manager.create_task(\n "narration_generation"' in narration_routes_text assert "async_task = asyncio.create_task(task_manager.execute_task(task_id, narration_task))" in narration_routes_text assert "task_id_holder" not in narration_routes_text assert "async def update_audio_progress(done_count: int, total_count: int)" in narration_routes_text assert "progress_callback=update_audio_progress" in narration_routes_text assert "progress_callback: Optional[Callable[[int, int], Awaitable[None]]] = None" in narration_service_text assert "await progress_callback(done_count, total_count)" in narration_service_text assert "await mark_done()" in narration_service_text def test_narration_audio_export_ui_is_wired(): narration_js_text = _read(NARRATION_JS) speech_manage_js_text = _read(SPEECH_MANAGE_JS) template_text = _read(TEMPLATE_FILE) assert "async function exportNarrationAudio()" in narration_js_text assert "/export/narration-audio" in narration_js_text assert "triggerFileDownload(downloadUrl)" in narration_js_text assert "导出讲解音频" in speech_manage_js_text assert 'onclick="exportNarrationAudio()"' in speech_manage_js_text assert "projectEditorNarration.js?v=20260509-custom-tts-api-v1" in template_text assert "projectSlidesEditor.speechScriptsManage.js?v=20260509-custom-tts-api-v1" in template_text def test_xiaomimimo_tts_provider_is_wired(): narration_service_text = _read(NARRATION_SERVICE) narration_routes_text = _read(NARRATION_ROUTES) narration_js_text = _read(NARRATION_JS) speech_manage_js_text = _read(SPEECH_MANAGE_JS) settings_template_text = _read(SETTINGS_TEMPLATE) db_config_text = _read(DB_CONFIG_SERVICE) assert '"xiaomimimo"' in narration_service_text assert "_generate_project_slide_audios_xiaomimimo" in narration_service_text assert "mimo-v2.5-tts-voicedesign" in narration_service_text assert "mimo-v2.5-tts-voiceclone" in narration_service_text assert '"api-key": api_key' in narration_service_text assert '"voice": ref_voice_data_uri' in narration_service_text assert "reference_audio_path=reference_audio_path" in narration_service_text assert "ref={ref_hash}" in narration_service_text assert "voice_prompt: str = \"\"" in narration_routes_text assert "voice_prompt=(request.voice_prompt or \"\")" in narration_routes_text assert '