| from types import SimpleNamespace |
|
|
| from app.services import learn_lesson_builder |
|
|
|
|
| def test_mock_provider_builds_a_cached_reading_lesson_without_external_calls( |
| monkeypatch, tmp_path |
| ) -> None: |
| monkeypatch.setattr( |
| learn_lesson_builder, |
| "get_settings", |
| lambda: SimpleNamespace(ai_provider="mock"), |
| ) |
| monkeypatch.setattr(learn_lesson_builder, "PUBLIC_ROOT", tmp_path) |
| monkeypatch.setattr( |
| learn_lesson_builder, |
| "generate_lesson_script", |
| lambda **_kwargs: (_ for _ in ()).throw(AssertionError("Groq should not run")), |
| ) |
| monkeypatch.setattr( |
| learn_lesson_builder, |
| "synthesize_beats", |
| lambda *_args, **_kwargs: (_ for _ in ()).throw( |
| AssertionError("TTS should not run") |
| ), |
| ) |
|
|
| manifest = learn_lesson_builder.build_lesson( |
| topic="Python programming", |
| lesson_title="Python Programming", |
| level="beginner", |
| ) |
|
|
| assert manifest["deliveryMode"] == "reading" |
| assert manifest["isFallback"] is True |
| assert "Local preview" in manifest["deliveryNotice"] |
| assert len(manifest["beats"]) >= 6 |
| assert all(beat["audio_src"] == "" for beat in manifest["beats"]) |
| assert "variables" in str(manifest).lower() |
| assert (tmp_path / "unowned" / manifest["lessonHash"] / "lesson.json").exists() |
|
|
|
|
| def test_provider_timeout_returns_bounded_reading_lesson(monkeypatch, tmp_path) -> None: |
| monkeypatch.setattr( |
| learn_lesson_builder, |
| "get_settings", |
| lambda: SimpleNamespace(ai_provider="sarvam"), |
| ) |
| monkeypatch.setattr(learn_lesson_builder, "PUBLIC_ROOT", tmp_path) |
| monkeypatch.setattr(learn_lesson_builder, "_lesson_sync_tts_enabled", lambda: False) |
| monkeypatch.setattr(learn_lesson_builder, "_lesson_generation_budget_seconds", lambda: 1.0) |
| monkeypatch.setattr( |
| learn_lesson_builder, |
| "_script_llm_providers", |
| lambda: [{"name": "test", "call": lambda _prompt: (_ for _ in ()).throw(TimeoutError())}], |
| ) |
| monkeypatch.setattr( |
| learn_lesson_builder, |
| "synthesize_beats", |
| lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("TTS must not run")), |
| ) |
|
|
| manifest = learn_lesson_builder.build_lesson( |
| topic="Basic Python for data analysis", |
| lesson_title="Python foundations", |
| level="beginner", |
| ) |
|
|
| assert manifest["deliveryMode"] == "reading" |
| assert manifest["isFallback"] is True |
| assert "reading" in manifest["deliveryNotice"].lower() |
| assert manifest["beats"] |
|
|
|
|
| def test_authored_lesson_can_return_reading_mode_without_audio(monkeypatch, tmp_path) -> None: |
| monkeypatch.setattr( |
| learn_lesson_builder, |
| "get_settings", |
| lambda: SimpleNamespace(ai_provider="sarvam"), |
| ) |
| monkeypatch.setattr(learn_lesson_builder, "PUBLIC_ROOT", tmp_path) |
| monkeypatch.setattr(learn_lesson_builder, "_lesson_sync_tts_enabled", lambda: False) |
| monkeypatch.setattr( |
| learn_lesson_builder, |
| "generate_lesson_script", |
| lambda **_kwargs: { |
| "lesson_title": "Authored lesson", |
| "summary": "A real authored reading class.", |
| "objectives": ["Explain the idea"], |
| "beats": [ |
| { |
| "kind": "explain", |
| "narration": "This is a complete authored explanation that a student can read and study without waiting for a voice asset.", |
| "board_heading": "Core idea", |
| "board_lines": ["Reason first"], |
| } |
| ], |
| "notes": ["A useful note."], |
| "flashcards": [{"front": "What matters?", "back": "The reason."}], |
| }, |
| ) |
|
|
| manifest = learn_lesson_builder.build_lesson( |
| topic="A real topic", |
| lesson_title="Authored lesson", |
| level="beginner", |
| ) |
|
|
| assert manifest["deliveryMode"] == "reading" |
| assert manifest["summary"] == "A real authored reading class." |
| assert manifest["beats"][0]["audio_src"] == "" |
|
|