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

227 lines
8.5 KiB
Python

"""Tests for SectionedGroup CLI help formatting."""
import click
import pytest
from click.testing import CliRunner
from notebooklm.cli.grouped import SectionedGroup
from notebooklm.notebooklm_cli import cli
@pytest.fixture
def runner():
return CliRunner()
class TestSectionedHelp:
"""Test that CLI help output is organized into sections."""
def test_help_shows_session_section(self, runner):
"""Verify Session section appears with expected commands."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "Session:" in result.output
assert "login" in result.output
assert "use" in result.output
assert "status" in result.output
assert "clear" in result.output
def test_help_shows_notebooks_section(self, runner):
"""Verify Notebooks section appears with expected commands."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "Notebooks:" in result.output
assert "list" in result.output
assert "create" in result.output
assert "delete" in result.output
assert "rename" in result.output
assert "share" in result.output
assert "summary" in result.output
def test_help_shows_chat_section(self, runner):
"""Verify Chat section appears with expected commands."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "Chat:" in result.output
assert "ask" in result.output
assert "configure" in result.output
assert "history" in result.output
def test_help_shows_command_groups_section(self, runner):
"""Verify Command Groups section appears with subcommand listings."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "Command Groups" in result.output
# These should show subcommands, not help text
assert "source" in result.output
assert "artifact" in result.output
assert "note" in result.output
def test_help_shows_artifact_actions_section(self, runner):
"""Verify Artifact Actions section appears with type listings."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "Artifact Actions" in result.output
assert "generate" in result.output
assert "download" in result.output
def test_source_group_shows_subcommands(self, runner):
"""Verify source group subcommands are listed in help."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
# Source subcommands should appear in the command group line
# They should be sorted alphabetically
assert "add" in result.output
assert "list" in result.output
def test_generate_group_shows_types(self, runner):
"""Verify generate subcommands (types) are listed in help."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
# Generate types should appear
assert "audio" in result.output
assert "video" in result.output
def test_no_commands_section_header(self, runner):
"""Verify the default 'Commands:' section header is replaced by sections."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
# The output should not have a generic "Commands:" section
# (it may still appear if Click adds it, but our sections should dominate)
lines = result.output.split("\n")
# Count section headers
section_count = sum(
1
for line in lines
if line.strip().endswith(":")
and any(
s in line
for s in ["Session", "Notebooks", "Chat", "Command Groups", "Artifact Actions"]
)
)
assert section_count >= 4 # At least 4 of our sections should appear (no Insights anymore)
class TestSectionedHelpOrder:
"""Test that sections appear in the correct order."""
def test_section_order(self, runner):
"""Verify sections appear in the expected order."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
output = result.output
# Find positions of key sections
session_pos = output.find("Session:")
notebooks_pos = output.find("Notebooks:")
chat_pos = output.find("Chat:")
groups_pos = output.find("Command Groups")
actions_pos = output.find("Artifact Actions")
# Verify they all exist
assert session_pos > 0
assert notebooks_pos > 0
assert chat_pos > 0
assert groups_pos > 0
assert actions_pos > 0
# Verify order
assert session_pos < notebooks_pos < chat_pos < groups_pos < actions_pos
class TestNoOrphanCommands:
"""Enforce: every top-level command lands in a primary section.
The "Other" safety-net bin in :class:`SectionedGroup` is reserved for
commands explicitly tagged ``category="misc"``. Any unbinned-and-untagged
command is a discoverability regression.
"""
def _binned_names(self) -> set[str]:
"""All command names that appear in any explicit primary section."""
binned: set[str] = set()
for cmd_names in SectionedGroup.command_sections.values():
binned.update(cmd_names)
for group_names in SectionedGroup.command_groups.values():
binned.update(group_names)
return binned
def test_no_command_falls_into_other_unless_misc_tagged(self):
"""Walk the assembled CLI; assert every visible top-level command is
either explicitly binned or explicitly tagged ``category="misc"``."""
ctx = click.Context(cli)
binned = self._binned_names()
orphans: list[str] = []
for name in cli.list_commands(ctx):
cmd = cli.get_command(ctx, name)
if cmd is None or cmd.hidden:
continue
if name in binned:
continue
if getattr(cmd, "category", None) == "misc":
continue
orphans.append(name)
assert orphans == [], (
f"Top-level command(s) {orphans!r} fell into the 'Other' safety-net "
"bin. Either add them to a primary section in "
"src/notebooklm/cli/grouped.py (command_sections or "
"command_groups), or explicitly tag them with `cmd.category = "
'"misc"` to opt into the Other bin.'
)
def test_help_has_no_other_section(self):
"""Verify the rendered --help output has no 'Other:' header (since no
top-level command is currently tagged ``category="misc"``)."""
runner = CliRunner()
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
# Match a section header only — avoid false positives on substrings
# like "Other" that may appear inside command help text.
assert "\nOther:\n" not in result.output, (
"Top-level help renders an 'Other:' section, which means at least "
"one command is unbinned. See test_no_command_falls_into_other_"
"unless_misc_tagged for details.\n\n" + result.output
)
def test_misc_tagged_command_lands_in_other(self):
"""Sanity-check the contract: a command tagged ``category="misc"`` is
the *only* legitimate inhabitant of the 'Other' bin, and it does
render there when present."""
@click.group(cls=SectionedGroup)
def fake_cli():
pass
@fake_cli.command("login")
def _login():
"""Log in."""
@fake_cli.command("oddball")
def _oddball():
"""Doesn't fit anywhere."""
# Tag oddball as misc; the SectionedGroup safety-net should still
# render it under "Other", and our orphan-test logic should accept it.
oddball_cmd = fake_cli.commands["oddball"]
oddball_cmd.category = "misc" # type: ignore[attr-defined]
runner = CliRunner()
result = runner.invoke(fake_cli, ["--help"])
assert result.exit_code == 0
assert "Other:" in result.output
assert "oddball" in result.output
# And the orphan-detection helper accepts it
ctx = click.Context(fake_cli)
binned = self._binned_names()
orphans = [
name
for name in fake_cli.list_commands(ctx)
if name not in binned
and getattr(fake_cli.get_command(ctx, name), "category", None) != "misc"
]
assert orphans == []