Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:30:13 +08:00

166 lines
5.6 KiB
Python

"""E2E tests for NotesAPI."""
import pytest
from notebooklm import NoteNotFoundError
from .conftest import requires_auth
@requires_auth
class TestNotesList:
"""Test listing notes."""
@pytest.mark.asyncio
@pytest.mark.readonly
async def test_list_notes(self, client, read_only_notebook_id):
"""List notes in test notebook - read-only."""
notes = await client.notes.list(read_only_notebook_id)
assert isinstance(notes, list)
@requires_auth
class TestNotesGet:
"""Test getting individual notes."""
@pytest.mark.asyncio
@pytest.mark.readonly
async def test_get_note(self, client, read_only_notebook_id):
"""Get a specific note from test notebook - read-only."""
notes = await client.notes.list(read_only_notebook_id)
if not notes:
pytest.skip("No notes available in test notebook")
note = await client.notes.get(read_only_notebook_id, notes[0].id)
assert note is not None
assert note.id == notes[0].id
@pytest.mark.asyncio
async def test_get_note_not_found(self, client, read_only_notebook_id):
"""Test getting a non-existent note raises NoteNotFoundError."""
# v0.8.0: a miss now raises NoteNotFoundError (issue #1247).
with pytest.raises(NoteNotFoundError):
await client.notes.get(read_only_notebook_id, "nonexistent_note_id")
@requires_auth
class TestNotesCRUD:
"""Test note CRUD operations - uses temp notebook."""
@pytest.mark.asyncio
async def test_create_and_delete_note(self, client, temp_notebook):
"""Create and delete a note in temp notebook."""
# Create
note = await client.notes.create(
temp_notebook.id,
title="Test Note",
content="Test content for E2E",
)
assert note is not None
assert note.id != ""
assert note.title == "Test Note"
assert note.content == "Test content for E2E"
# Verify it appears in list
notes = await client.notes.list(temp_notebook.id)
note_ids = [n.id for n in notes]
assert note.id in note_ids
# Delete (v0.7.0: returns None, idempotent — issue #1211)
result = await client.notes.delete(temp_notebook.id, note.id)
assert result is None
@pytest.mark.asyncio
async def test_update_note(self, client, temp_notebook):
"""Update a note's content and title."""
# Create
note = await client.notes.create(
temp_notebook.id,
title="Original Title",
content="Original content",
)
# Update
await client.notes.update(
temp_notebook.id,
note.id,
content="Updated content",
title="Updated Title",
)
# Verify
updated = await client.notes.get(temp_notebook.id, note.id)
assert updated is not None
assert updated.title == "Updated Title"
assert updated.content == "Updated content"
# Cleanup
await client.notes.delete(temp_notebook.id, note.id)
@requires_auth
class TestMindMaps:
"""Test mind map operations."""
@pytest.mark.asyncio
@pytest.mark.readonly
async def test_list_mind_maps(self, client, read_only_notebook_id):
"""List mind maps in test notebook - read-only."""
mind_maps = await client.notes.list_mind_maps(read_only_notebook_id)
assert isinstance(mind_maps, list)
@pytest.mark.e2e
@requires_auth
class TestSaveAnswerAsNote:
"""Test saving chat answers as citation-rich notes (issue #660).
These tests exercise the full ask → save_answer_as_note path against
the live NotebookLM service. The note's creation success is
automated; the per-citation hover-anchor rendering is a MANUAL
verification step — open the resulting note in the web UI, hover a
``[N]`` marker, and confirm a passage popup appears. The test
prints instructions to the log to make this step actionable.
"""
@pytest.mark.asyncio
async def test_save_answer_as_note_preserves_citations(self, client, temp_notebook):
"""Ask a citation-bearing question, save as note, verify creation.
Uses ``temp_notebook`` so the test cleans up after itself even
when a downstream assertion fails. The actual hover-anchor
check is manual — see class docstring.
"""
result = await client.chat.ask(
temp_notebook.id,
"Summarize the key points from the sources in one sentence.",
)
if not result.references:
pytest.skip(
"Live answer had no citations; save_answer_as_note requires "
"non-empty references. Temp notebook may need sources."
)
note = await client.chat.save_answer_as_note(
temp_notebook.id,
result,
title=f"E2E #660 hover-anchor test {result.conversation_id[:8]}",
)
try:
assert note.id != ""
# The server may apply smart-title generation for [2]-mode
# notes — assert non-empty but don't pin the value.
assert note.title
listed = await client.notes.list(temp_notebook.id)
assert note.id in {n.id for n in listed}
print(
f"\n[Manual hover-anchor check] Open "
f"https://notebooklm.google.com/notebook/{temp_notebook.id}, "
f"find note '{note.title}' (id={note.id[:8]}...), hover any "
f"[N] marker, confirm the popup shows the cited passage."
)
finally:
await client.notes.delete(temp_notebook.id, note.id)