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
175 lines
6.5 KiB
Python
175 lines
6.5 KiB
Python
"""Unit tests for the artifact-envelope unwrap helpers (issue #1491 burndown).
|
|
|
|
These cover the two functions that centralise the positional envelope/leaf
|
|
descent the ``_artifact``/``_artifacts`` decode sites previously open-coded:
|
|
|
|
* :func:`unwrap_artifact_rows` — the ``[[row, ...]]`` envelope probe shared by
|
|
``ArtifactListingService.list_raw`` (``LIST_ARTIFACTS``) and
|
|
``ArtifactsAPI.suggest_reports`` (``GET_SUGGESTED_REPORTS``); and
|
|
* :func:`unwrap_mind_map_generation_leaf` — the two-level ``result[0][0]`` leaf
|
|
descent of a ``GENERATE_MIND_MAP`` reply.
|
|
|
|
Each table proves the helper reproduces the EXACT result of the prior inline
|
|
reads on present/happy, empty, too-short, and malformed inputs (the soft-vs-
|
|
strict semantics the migration had to preserve).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from notebooklm._row_adapters.artifacts import (
|
|
MIND_MAP_LEAF_ABSENT,
|
|
unwrap_artifact_rows,
|
|
unwrap_mind_map_generation_leaf,
|
|
)
|
|
from notebooklm.rpc.types import RPCMethod
|
|
|
|
_LIST_METHOD = RPCMethod.LIST_ARTIFACTS.value
|
|
|
|
|
|
def _unwrap(result: list[Any]) -> list[Any]:
|
|
return unwrap_artifact_rows(result, method_id=_LIST_METHOD, source="test")
|
|
|
|
|
|
class TestUnwrapArtifactRows:
|
|
"""``unwrap_artifact_rows`` reproduces the inline ``[[row,...]]`` probe."""
|
|
|
|
def test_wrapped_envelope_returns_inner_rows(self) -> None:
|
|
"""A single-element envelope whose inner head is a row unwraps."""
|
|
rows = [["id1", "Title1"], ["id2", "Title2"]]
|
|
assert _unwrap([rows]) == rows
|
|
|
|
def test_wrapped_empty_inner_returns_empty_inner(self) -> None:
|
|
"""A single outer element wrapping an empty inner list unwraps to ``[]``."""
|
|
# ``not inner`` short-circuits before the inner-head probe — the
|
|
# legitimately-empty notebook shape.
|
|
inner: list[Any] = []
|
|
assert _unwrap([inner]) is inner
|
|
|
|
def test_already_flat_rows_returned_unchanged(self) -> None:
|
|
"""A flat list of >1 rows is not single-element, so it is returned as-is."""
|
|
flat = [["id1"], ["id2"]]
|
|
assert _unwrap(flat) is flat
|
|
|
|
def test_single_flat_row_with_scalar_head_returned_unchanged(self) -> None:
|
|
"""A single outer element whose inner head is a SCALAR is a flat row.
|
|
|
|
``[[scalar, ...]]`` is the already-flat ``[row]`` shape (one row whose
|
|
first field is a scalar), not a wrapped envelope — so it must NOT be
|
|
unwrapped. Mirrors the prior ``isinstance(inner[0], list)`` guard.
|
|
"""
|
|
flat = [["id-as-scalar-head", 2, 3]]
|
|
assert _unwrap(flat) is flat
|
|
|
|
def test_single_element_non_list_returned_unchanged(self) -> None:
|
|
"""A single outer element that is not a list is left flat (caller's row)."""
|
|
flat = ["scalar-row"]
|
|
assert _unwrap(flat) is flat
|
|
|
|
def test_empty_outer_returned_unchanged(self) -> None:
|
|
"""An empty outer list (len != 1) is returned unchanged (== ``[]``)."""
|
|
outer: list[Any] = []
|
|
assert _unwrap(outer) is outer
|
|
|
|
def test_matches_legacy_inline_logic_across_shapes(self) -> None:
|
|
"""Differential check against the exact pre-migration inline algorithm."""
|
|
|
|
def legacy(result: list[Any]) -> list[Any]:
|
|
items = result
|
|
if len(result) == 1 and isinstance(result[0], list):
|
|
inner = result[0]
|
|
if not inner or isinstance(inner[0], list):
|
|
items = inner
|
|
return items
|
|
|
|
shapes: list[list[Any]] = [
|
|
[[["a"], ["b"]]], # wrapped
|
|
[[]], # wrapped empty inner
|
|
[["scalar", 1]], # flat single row (scalar head)
|
|
[["a"], ["b"]], # flat multi-row
|
|
["scalar"], # single non-list element
|
|
[], # empty
|
|
[[["only-row"]]], # wrapped single row
|
|
]
|
|
for shape in shapes:
|
|
assert _unwrap(shape) == legacy(shape), shape
|
|
|
|
|
|
def _leaf(result: Any) -> Any:
|
|
return unwrap_mind_map_generation_leaf(
|
|
result, method_id=RPCMethod.GENERATE_MIND_MAP.value, source="test"
|
|
)
|
|
|
|
|
|
class TestUnwrapMindMapGenerationLeaf:
|
|
"""``unwrap_mind_map_generation_leaf`` reproduces the ``result[0][0]`` descent."""
|
|
|
|
def test_happy_returns_present_leaf(self) -> None:
|
|
assert _leaf([['{"name": "Tree"}']]) == '{"name": "Tree"}'
|
|
|
|
def test_present_none_leaf_is_returned_not_treated_as_absent(self) -> None:
|
|
"""A PRESENT ``None`` leaf must be returned verbatim, not the sentinel.
|
|
|
|
The historical code processes a ``None`` leaf (it serialises to a
|
|
``"null"`` note body), so the helper must distinguish it from the
|
|
absence shapes via the sentinel.
|
|
"""
|
|
result = _leaf([[None]])
|
|
assert result is None
|
|
assert result is not MIND_MAP_LEAF_ABSENT
|
|
|
|
def test_present_empty_string_leaf_returned(self) -> None:
|
|
result = _leaf([[""]])
|
|
assert result == ""
|
|
assert result is not MIND_MAP_LEAF_ABSENT
|
|
|
|
@pytest.mark.parametrize(
|
|
"result",
|
|
[
|
|
None, # null payload
|
|
[], # empty outer
|
|
"scalar", # non-list payload
|
|
0, # falsy non-list
|
|
[[]], # present outer, empty inner
|
|
[None], # present outer, non-list inner
|
|
["scalar-inner"], # present outer, non-list (str) inner
|
|
],
|
|
)
|
|
def test_absence_shapes_return_sentinel(self, result: Any) -> None:
|
|
"""Short / non-list outer or inner returns the absence sentinel (soft)."""
|
|
assert _leaf(result) is MIND_MAP_LEAF_ABSENT
|
|
|
|
def test_matches_legacy_inline_logic(self) -> None:
|
|
"""Differential check: sentinel iff the legacy nested guard skipped."""
|
|
|
|
def legacy_found(result: Any) -> tuple[bool, Any]:
|
|
if result and isinstance(result, list) and len(result) > 0:
|
|
inner = result[0]
|
|
if isinstance(inner, list) and len(inner) > 0:
|
|
return True, inner[0]
|
|
return False, None
|
|
|
|
shapes: list[Any] = [
|
|
[["leaf"]],
|
|
[[None]],
|
|
[[""]],
|
|
[[123]],
|
|
[[]],
|
|
[None],
|
|
[],
|
|
None,
|
|
"x",
|
|
[["a", "b"]], # inner len > 1 — head only
|
|
]
|
|
for shape in shapes:
|
|
found, expected_leaf = legacy_found(shape)
|
|
actual = _leaf(shape)
|
|
if found:
|
|
assert actual == expected_leaf, shape
|
|
assert actual is not MIND_MAP_LEAF_ABSENT, shape
|
|
else:
|
|
assert actual is MIND_MAP_LEAF_ABSENT, shape
|