09e9f3545f
Test / Code Quality (push) Has been cancelled
Test / Test (macos-latest, Python 3.10) (push) Has been cancelled
Test / Test (macos-latest, Python 3.11) (push) Has been cancelled
Test / Test (macos-latest, Python 3.12) (push) Has been cancelled
Test / Test (macos-latest, Python 3.13) (push) Has been cancelled
Test / Test (macos-latest, Python 3.14) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.10) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.11) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.12) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.13) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.14) (push) Has been cancelled
Test / Test (windows-latest, Python 3.10) (push) Has been cancelled
Test / Test (windows-latest, Python 3.11) (push) Has been cancelled
Test / Test (windows-latest, Python 3.12) (push) Has been cancelled
Test / Test (windows-latest, Python 3.13) (push) Has been cancelled
Test / Test (windows-latest, Python 3.14) (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
dependency-audit / pip-audit (push) Has been cancelled
407 lines
15 KiB
Python
407 lines
15 KiB
Python
"""Artifact generation tests.
|
|
|
|
All artifact generation tests consolidated here. These tests:
|
|
- Use `generation_notebook_id` fixture (auto-created, has content)
|
|
- Variant tests are marked @pytest.mark.variants (skip to save quota)
|
|
|
|
Notebook lifecycle:
|
|
- Auto-created on first run if NOTEBOOKLM_GENERATION_NOTEBOOK_ID not set
|
|
- Artifacts/notes cleaned BEFORE tests to ensure clean state
|
|
- In CI (CI=true): notebook deleted after tests to avoid orphans
|
|
- Locally: notebook persists for verification, ID stored in the active profile cache
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from notebooklm import (
|
|
AudioFormat,
|
|
AudioLength,
|
|
InfographicDetail,
|
|
InfographicOrientation,
|
|
QuizDifficulty,
|
|
QuizQuantity,
|
|
SlideDeckFormat,
|
|
SlideDeckLength,
|
|
VideoFormat,
|
|
VideoStyle,
|
|
)
|
|
|
|
from .conftest import assert_generation_started, requires_auth
|
|
|
|
# Live CREATE_ARTIFACT coverage — monitored by the nightly generation coverage
|
|
# floor (tests/e2e/conftest.py, #1819): a fully-throttled run where every marked
|
|
# generation test skipped and none passed reds the nightly instead of hollow-green.
|
|
# INVARIANT: keep this module generation-only — a passing non-generation test here
|
|
# would satisfy the floor and mask a genuinely throttled generation surface.
|
|
pytestmark = pytest.mark.live_generation
|
|
|
|
|
|
@requires_auth
|
|
class TestAudioGeneration:
|
|
"""Audio generation tests."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_audio_default(self, client, generation_notebook_id):
|
|
"""Test audio generation with true defaults."""
|
|
result = await client.artifacts.generate_audio(generation_notebook_id)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_audio_brief(self, client, generation_notebook_id):
|
|
"""Test audio generation with non-default format to verify param encoding."""
|
|
result = await client.artifacts.generate_audio(
|
|
generation_notebook_id,
|
|
audio_format=AudioFormat.BRIEF,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_audio_deep_dive_long(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_audio(
|
|
generation_notebook_id,
|
|
audio_format=AudioFormat.DEEP_DIVE,
|
|
audio_length=AudioLength.LONG,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_audio_brief_short(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_audio(
|
|
generation_notebook_id,
|
|
audio_format=AudioFormat.BRIEF,
|
|
audio_length=AudioLength.SHORT,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_audio_critique(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_audio(
|
|
generation_notebook_id,
|
|
audio_format=AudioFormat.CRITIQUE,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_audio_debate(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_audio(
|
|
generation_notebook_id,
|
|
audio_format=AudioFormat.DEBATE,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_audio_with_language(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_audio(
|
|
generation_notebook_id,
|
|
language="en",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
|
|
@requires_auth
|
|
class TestVideoGeneration:
|
|
"""Video generation tests."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_video_default(self, client, generation_notebook_id):
|
|
"""Test video generation with non-default style to verify param encoding."""
|
|
result = await client.artifacts.generate_video(
|
|
generation_notebook_id,
|
|
video_style=VideoStyle.ANIME,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_video_explainer_anime(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_video(
|
|
generation_notebook_id,
|
|
video_format=VideoFormat.EXPLAINER,
|
|
video_style=VideoStyle.ANIME,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_video_brief_whiteboard(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_video(
|
|
generation_notebook_id,
|
|
video_format=VideoFormat.BRIEF,
|
|
video_style=VideoStyle.WHITEBOARD,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_video_with_instructions(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_video(
|
|
generation_notebook_id,
|
|
video_format=VideoFormat.EXPLAINER,
|
|
video_style=VideoStyle.CLASSIC,
|
|
instructions="Focus on key concepts for beginners",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_video_kawaii_style(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_video(
|
|
generation_notebook_id,
|
|
video_style=VideoStyle.KAWAII,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_video_watercolor_style(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_video(
|
|
generation_notebook_id,
|
|
video_style=VideoStyle.WATERCOLOR,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_video_auto_style(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_video(
|
|
generation_notebook_id,
|
|
video_style=VideoStyle.AUTO_SELECT,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
|
|
@requires_auth
|
|
class TestCinematicVideoGeneration:
|
|
"""Cinematic video generation tests.
|
|
|
|
Cinematic videos use Veo 3 AI for documentary-style footage.
|
|
Requires Google AI Ultra subscription. Generation takes ~30-40 minutes.
|
|
"""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_cinematic_video_default(self, client, generation_notebook_id):
|
|
"""Test cinematic video generation with defaults."""
|
|
result = await client.artifacts.generate_cinematic_video(generation_notebook_id)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_cinematic_video_with_instructions(self, client, generation_notebook_id):
|
|
"""Test cinematic video generation with custom instructions."""
|
|
result = await client.artifacts.generate_cinematic_video(
|
|
generation_notebook_id,
|
|
instructions="Focus on key concepts for a general audience",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_cinematic_video_with_language(self, client, generation_notebook_id):
|
|
"""Test cinematic video generation with explicit language."""
|
|
result = await client.artifacts.generate_cinematic_video(
|
|
generation_notebook_id,
|
|
language="en",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
|
|
@requires_auth
|
|
class TestQuizGeneration:
|
|
"""Quiz generation tests."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_quiz_default(self, client, generation_notebook_id):
|
|
"""Test quiz generation with non-default difficulty to verify param encoding."""
|
|
result = await client.artifacts.generate_quiz(
|
|
generation_notebook_id,
|
|
difficulty=QuizDifficulty.HARD,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_quiz_with_options(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_quiz(
|
|
generation_notebook_id,
|
|
quantity=QuizQuantity.MORE,
|
|
difficulty=QuizDifficulty.HARD,
|
|
instructions="Focus on key concepts and definitions",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_quiz_fewer_easy(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_quiz(
|
|
generation_notebook_id,
|
|
quantity=QuizQuantity.FEWER,
|
|
difficulty=QuizDifficulty.EASY,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
|
|
@requires_auth
|
|
class TestFlashcardsGeneration:
|
|
"""Flashcards generation tests."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_flashcards_default(self, client, generation_notebook_id):
|
|
"""Test flashcards generation with non-default quantity to verify param encoding."""
|
|
result = await client.artifacts.generate_flashcards(
|
|
generation_notebook_id,
|
|
quantity=QuizQuantity.MORE,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_flashcards_with_options(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_flashcards(
|
|
generation_notebook_id,
|
|
quantity=QuizQuantity.STANDARD,
|
|
difficulty=QuizDifficulty.MEDIUM,
|
|
instructions="Create cards for vocabulary terms",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
|
|
@requires_auth
|
|
class TestInfographicGeneration:
|
|
"""Infographic generation tests."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_infographic_default(self, client, generation_notebook_id):
|
|
"""Test infographic generation with non-default orientation to verify param encoding."""
|
|
result = await client.artifacts.generate_infographic(
|
|
generation_notebook_id,
|
|
orientation=InfographicOrientation.PORTRAIT,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_infographic_portrait_detailed(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_infographic(
|
|
generation_notebook_id,
|
|
orientation=InfographicOrientation.PORTRAIT,
|
|
detail_level=InfographicDetail.DETAILED,
|
|
instructions="Include statistics and key findings",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_infographic_square_concise(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_infographic(
|
|
generation_notebook_id,
|
|
orientation=InfographicOrientation.SQUARE,
|
|
detail_level=InfographicDetail.CONCISE,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_infographic_landscape(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_infographic(
|
|
generation_notebook_id,
|
|
orientation=InfographicOrientation.LANDSCAPE,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
|
|
@requires_auth
|
|
class TestSlideDeckGeneration:
|
|
"""Slide deck generation tests."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_slide_deck_default(self, client, generation_notebook_id):
|
|
"""Test slide deck generation with non-default format to verify param encoding."""
|
|
result = await client.artifacts.generate_slide_deck(
|
|
generation_notebook_id,
|
|
slide_format=SlideDeckFormat.PRESENTER_SLIDES,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_slide_deck_detailed(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_slide_deck(
|
|
generation_notebook_id,
|
|
slide_format=SlideDeckFormat.DETAILED_DECK,
|
|
slide_length=SlideDeckLength.DEFAULT,
|
|
instructions="Include speaker notes",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_slide_deck_presenter_short(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_slide_deck(
|
|
generation_notebook_id,
|
|
slide_format=SlideDeckFormat.PRESENTER_SLIDES,
|
|
slide_length=SlideDeckLength.SHORT,
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
|
|
@requires_auth
|
|
class TestDataTableGeneration:
|
|
"""Data table generation tests."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_data_table_default(self, client, generation_notebook_id):
|
|
"""Test data table generation with instructions to verify param encoding."""
|
|
result = await client.artifacts.generate_data_table(
|
|
generation_notebook_id,
|
|
instructions="Create a comparison table",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.variants
|
|
async def test_generate_data_table_with_instructions(self, client, generation_notebook_id):
|
|
result = await client.artifacts.generate_data_table(
|
|
generation_notebook_id,
|
|
instructions="Create a comparison table of key concepts",
|
|
language="en",
|
|
)
|
|
assert_generation_started(result)
|
|
|
|
|
|
@requires_auth
|
|
class TestMindMapGeneration:
|
|
"""Mind map generation tests."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_mind_map(self, client, generation_notebook_id):
|
|
"""Mind map generation is fast (~5-10s), not slow."""
|
|
# Clean up old mind maps to prevent accumulation from nightly runs
|
|
existing_mind_maps = await client.notes.list_mind_maps(generation_notebook_id)
|
|
for mm in existing_mind_maps:
|
|
await client.notes.delete_mind_map(generation_notebook_id, mm[0])
|
|
|
|
result = await client.artifacts.generate_mind_map(generation_notebook_id)
|
|
assert result is not None
|
|
assert result.note_id is not None
|
|
# Verify mind map structure
|
|
mind_map = result.mind_map
|
|
assert isinstance(mind_map, dict)
|
|
assert "name" in mind_map
|
|
assert "children" in mind_map
|
|
|
|
|
|
@requires_auth
|
|
class TestStudyGuideGeneration:
|
|
"""Study guide generation tests."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_study_guide(self, client, generation_notebook_id):
|
|
"""Test study guide generation."""
|
|
result = await client.artifacts.generate_study_guide(generation_notebook_id)
|
|
assert_generation_started(result)
|