Files
teng-lin--notebooklm-py/tests/unit/test_artifacts_mind_map_injection.py
wehub-resource-sync 09e9f3545f
CodeQL / Analyze (push) Waiting to run
dependency-audit / pip-audit (push) Waiting to run
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
chore: import upstream snapshot with attribution
2026-07-13 13:30:13 +08:00

151 lines
5.7 KiB
Python

"""Tests for ``NoteBackedMindMapService`` injection into ``ArtifactsAPI``.
After Phase 5 (refactor-history.md Migration Plan steps 6-7), ``ArtifactsAPI``
takes two explicit services through its constructor:
* ``mind_maps: NoteBackedMindMapService`` — the mind-map-only adapter
the download path uses (replaces the previous ``mind_map_service``
parameter name).
* ``note_service: NoteService`` — the raw note-row primitives the
mind-map generation path uses to persist a freshly generated mind map.
These tests pin three contracts:
1. ``_list_mind_maps()`` delegates to the injected ``mind_maps``
facade and does not re-enter the legacy module-level
``_mind_map.NoteBackedMindMapService.list_mind_maps`` adapter.
2. Both ``mind_maps`` and ``note_service`` are required and
keyword-only — the legacy ``mind_map_service`` kwarg is gone.
3. Constructing without the new kwargs (or with the old name) raises
``TypeError``.
``ArtifactsAPI`` consumes its three runtime collaborators (``rpc`` +
``drain`` + ``lifecycle``) directly per ADR-0014 Rule 2; the tests here
do not exercise RPC traffic — they pin the constructor contract — so
the collaborator stubs only need to silently accept the calls
``ArtifactsAPI.__init__`` makes (``drain.register_drain_hook``).
"""
from unittest.mock import AsyncMock, MagicMock
import pytest
from notebooklm._artifacts import ArtifactsAPI
from notebooklm._mind_map import NoteBackedMindMapService
from notebooklm._note_service import NoteService
def _make_collaborators() -> tuple[MagicMock, MagicMock, MagicMock]:
"""Return ``(rpc, drain, lifecycle)`` stubs for constructor-contract tests.
``drain`` must accept ``register_drain_hook`` (called by
:meth:`ArtifactsAPI.__init__` to register the polling-service
close-time cleanup hook); the other collaborators are inert.
"""
rpc = MagicMock()
drain = MagicMock()
lifecycle = MagicMock()
return rpc, drain, lifecycle
@pytest.mark.asyncio
async def test_list_mind_maps_delegates_to_injected_facade():
"""``_list_mind_maps`` calls the injected ``mind_maps`` facade.
Phase 6 (refactor-history.md Step 9, ADR-0013) removed the module-level
``_mind_map.list_mind_maps`` wrapper that previously needed to be
monkeypatched as a guard; the only path now is through the
injected adapter. Confirming the adapter sees the call still pins
the contract.
"""
rpc, drain, lifecycle = _make_collaborators()
fake_mind_maps = MagicMock(spec=NoteBackedMindMapService)
fake_mind_maps.list_mind_maps = AsyncMock(return_value=["sentinel-row"])
fake_note_service = MagicMock(spec=NoteService)
api = ArtifactsAPI(
rpc=rpc,
drain=drain,
lifecycle=lifecycle,
notebooks=MagicMock(),
mind_maps=fake_mind_maps,
note_service=fake_note_service,
)
result = await api._list_mind_maps("nb_abc")
assert result == ["sentinel-row"]
fake_mind_maps.list_mind_maps.assert_awaited_once_with("nb_abc")
def test_mind_maps_and_note_service_are_required():
"""Both new kwargs are required — no implicit fallback installs them."""
rpc, drain, lifecycle = _make_collaborators()
fake_mind_maps = MagicMock(spec=NoteBackedMindMapService)
fake_note_service = MagicMock(spec=NoteService)
kw = {"rpc": rpc, "drain": drain, "lifecycle": lifecycle, "notebooks": MagicMock()}
# Missing both.
with pytest.raises(TypeError):
ArtifactsAPI(**kw) # type: ignore[call-arg]
# Missing note_service.
with pytest.raises(TypeError):
ArtifactsAPI(**kw, mind_maps=fake_mind_maps) # type: ignore[call-arg]
# Missing mind_maps.
with pytest.raises(TypeError):
ArtifactsAPI(**kw, note_service=fake_note_service) # type: ignore[call-arg]
def test_mind_maps_and_note_service_are_keyword_only():
"""All ``ArtifactsAPI`` parameters remain keyword-only."""
rpc, drain, lifecycle = _make_collaborators()
fake_mind_maps = MagicMock(spec=NoteBackedMindMapService)
fake_note_service = MagicMock(spec=NoteService)
with pytest.raises(TypeError):
ArtifactsAPI(rpc, drain, lifecycle, MagicMock(), fake_mind_maps, fake_note_service) # type: ignore[misc]
def test_legacy_mind_map_service_kwarg_is_rejected():
"""The Phase 3 ``mind_map_service=`` kwarg was renamed in Phase 5.
Passing it must raise ``TypeError`` so silent breakage on partial
upgrades surfaces immediately.
"""
rpc, drain, lifecycle = _make_collaborators()
fake_mind_maps = MagicMock(spec=NoteBackedMindMapService)
fake_note_service = MagicMock(spec=NoteService)
with pytest.raises(TypeError):
ArtifactsAPI( # type: ignore[call-arg]
rpc=rpc,
drain=drain,
lifecycle=lifecycle,
notebooks=MagicMock(),
mind_map_service=fake_mind_maps,
note_service=fake_note_service,
)
def test_artifacts_no_longer_exposes_core_property_alias():
"""Phase 5 removes the ``_core`` ``@property`` alias on ArtifactsAPI.
The transitional ``_core`` shim added in Phase 3 is dead code; after
the runtime-adapter inlining the three runtime collaborators are
stored on ``ArtifactsAPI`` directly as ``_rpc`` / ``_drain`` /
``_lifecycle`` rather than behind a single ``_runtime`` attribute.
"""
rpc, drain, lifecycle = _make_collaborators()
api = ArtifactsAPI(
rpc=rpc,
drain=drain,
lifecycle=lifecycle,
notebooks=MagicMock(),
mind_maps=MagicMock(spec=NoteBackedMindMapService),
note_service=MagicMock(spec=NoteService),
)
# The descriptor must be gone — not just empty, not just delegating.
assert not hasattr(api, "_core")
assert api._rpc is rpc
assert api._drain is drain
assert api._lifecycle is lifecycle