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
150 lines
6.5 KiB
Python
150 lines
6.5 KiB
Python
"""Tests for root-group flags on the top-level ``notebooklm`` Click group.
|
|
|
|
Covers the root-group pieces of ``--quiet`` (env-var precedence + global quiet mode):
|
|
|
|
* ``notebooklm --quiet ...`` suppresses INFO/WARN logs from the ``notebooklm``
|
|
package logger; only ERROR (and above) survive on stderr.
|
|
* ``notebooklm --quiet -v ...`` is rejected with a ``UsageError`` (exit 2)
|
|
because the two intents conflict — ``--quiet`` raises the floor to ERROR
|
|
while ``-v`` lowers it to INFO; honoring either silently would surprise
|
|
one of the two callers.
|
|
* ``--quiet`` does NOT collide with the existing subcommand-scoped
|
|
``auth refresh --quiet`` flag — Click parses the closer scope first, so
|
|
``notebooklm auth refresh --quiet`` continues to hit the subcommand flag.
|
|
|
|
Status-output suppression is covered in ``test_quiet_flag.py``.
|
|
|
|
The new ``NOTEBOOKLM_NOTEBOOK`` env-var integration is tested in
|
|
``test_helpers.py::TestRequireNotebook`` (the resolver covers both the direct
|
|
helper API and the Click ``envvar=`` wiring on ``notebook_option``).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
import pytest
|
|
from click.testing import CliRunner
|
|
|
|
from notebooklm.notebooklm_cli import cli
|
|
|
|
|
|
@pytest.fixture
|
|
def runner() -> CliRunner:
|
|
return CliRunner()
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _reset_notebooklm_logger():
|
|
"""Restore the ``notebooklm`` package logger level after every test.
|
|
|
|
The root ``cli()`` callback mutates ``logging.getLogger("notebooklm")``'s
|
|
level as a side effect of ``--quiet`` / ``-v`` / ``-vv``. Without this
|
|
fixture, the level set by one test leaks into the next via the shared
|
|
logger registry and turns the suite order-dependent.
|
|
"""
|
|
pkg_logger = logging.getLogger("notebooklm")
|
|
saved = pkg_logger.level
|
|
try:
|
|
yield
|
|
finally:
|
|
pkg_logger.setLevel(saved)
|
|
|
|
|
|
class TestQuietFlag:
|
|
def test_quiet_raises_notebooklm_logger_to_error(self, runner):
|
|
"""``notebooklm --quiet ...`` sets the ``notebooklm`` logger level to
|
|
ERROR so INFO and WARNING records emitted by the package are dropped
|
|
before they hit the configured StreamHandler.
|
|
|
|
We invoke a subcommand's ``--help`` (rather than the bare root
|
|
``--help``) because Click short-circuits ``cli --help`` inside the
|
|
parsing layer before the group callback runs; ``cli status --help``
|
|
invokes the callback first (so ``--quiet`` takes effect) and exits
|
|
with the subcommand help next.
|
|
"""
|
|
result = runner.invoke(cli, ["--quiet", "status", "--help"])
|
|
assert result.exit_code == 0, result.output
|
|
assert logging.getLogger("notebooklm").level == logging.ERROR
|
|
|
|
def test_no_quiet_keeps_notebooklm_logger_at_default(self, runner):
|
|
"""Baseline: omitting ``--quiet`` and ``-v`` leaves the package logger
|
|
untouched (configured by ``configure_logging()`` at import time, which
|
|
respects ``NOTEBOOKLM_LOG_LEVEL``; default is WARNING).
|
|
"""
|
|
before = logging.getLogger("notebooklm").level
|
|
result = runner.invoke(cli, ["status", "--help"])
|
|
assert result.exit_code == 0
|
|
assert logging.getLogger("notebooklm").level == before
|
|
|
|
def test_quiet_and_verbose_are_mutually_exclusive(self, runner):
|
|
"""``--quiet`` + ``-v`` together must exit 2 with a UsageError, since
|
|
the two flags resolve to incompatible log-level intents (ERROR vs INFO).
|
|
"""
|
|
result = runner.invoke(cli, ["--quiet", "-v", "status", "--help"])
|
|
assert result.exit_code == 2
|
|
# Click renders ``UsageError`` to stderr by default. ``CliRunner``
|
|
# mixes stdout + stderr in ``result.output``.
|
|
assert "--quiet" in result.output
|
|
assert "-v" in result.output or "verbose" in result.output.lower()
|
|
assert "mutually exclusive" in result.output.lower()
|
|
|
|
def test_quiet_and_double_verbose_are_mutually_exclusive(self, runner):
|
|
"""``--quiet`` + ``-vv`` (DEBUG) is also rejected — same conflict as
|
|
``--quiet -v``, just at a different verbosity step. Pinned separately
|
|
so future refactors can't accidentally narrow the check to ``-v``
|
|
only and let ``-vv`` slip through.
|
|
"""
|
|
result = runner.invoke(cli, ["--quiet", "-vv", "status", "--help"])
|
|
assert result.exit_code == 2
|
|
assert "mutually exclusive" in result.output.lower()
|
|
|
|
def test_quiet_advertised_in_root_help(self, runner):
|
|
"""``notebooklm --help`` must list the new ``--quiet`` flag so users
|
|
can discover it without reading the changelog.
|
|
"""
|
|
result = runner.invoke(cli, ["--help"])
|
|
assert result.exit_code == 0
|
|
assert "--quiet" in result.output
|
|
|
|
|
|
class TestNotebookOptionEnvVar:
|
|
"""The ``-n/--notebook`` option declared by ``cli/options.py:notebook_option``
|
|
must accept ``NOTEBOOKLM_NOTEBOOK`` as a fallback so Click documents the
|
|
binding in ``--help`` and resolves the env value natively.
|
|
|
|
Behavioral resolution (env > context > error when no flag) is exercised
|
|
in ``test_helpers.py::TestRequireNotebook`` against the resolver itself;
|
|
here we pin the wiring at the Click-option layer so the documented
|
|
``NOTEBOOKLM_NOTEBOOK`` binding cannot regress silently.
|
|
"""
|
|
|
|
def test_notebook_option_declares_envvar_binding(self):
|
|
"""The probe-decorator pattern used by ``test_helpers.py`` confirms
|
|
the canonical option declaration; here we assert the new ``envvar=``
|
|
was wired in.
|
|
"""
|
|
from click import Option
|
|
|
|
from notebooklm.cli.options import notebook_option
|
|
|
|
@notebook_option
|
|
def _probe(notebook_id): # pragma: no cover — never invoked
|
|
pass
|
|
|
|
for param in _probe.__click_params__: # type: ignore[attr-defined]
|
|
if isinstance(param, Option) and "--notebook" in param.opts:
|
|
assert param.envvar == "NOTEBOOKLM_NOTEBOOK", (
|
|
f"notebook_option must bind envvar=NOTEBOOKLM_NOTEBOOK so "
|
|
f"Click resolves and documents it natively, got {param.envvar!r}"
|
|
)
|
|
# Click renders ``[env var: NOTEBOOKLM_NOTEBOOK]`` in --help
|
|
# only when ``show_envvar=True``. Assert the surface is
|
|
# discoverable in ``--help`` (the dominant LLM-agent UX).
|
|
assert param.show_envvar is True, (
|
|
"notebook_option must set show_envvar=True so the env-var "
|
|
"binding shows up in --help output"
|
|
)
|
|
return
|
|
raise AssertionError("notebook_option did not declare a --notebook option")
|