teng-lin--notebooklm-py
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
110 行
4.1 KiB
Python
110 行
4.1 KiB
Python
"""Tests for building the remote file-transfer config in ``mcp/__main__.py``.
|
|
|
|
Pins: neither URL set → ``None`` (and **no** ``SystemExit`` — a bearer-only remote
|
|
server must keep starting); ``NOTEBOOKLM_MCP_PUBLIC_URL`` set → a config is built;
|
|
an invalid public URL → ``SystemExit`` (same validation as the OAuth base URL); and
|
|
the OAuth-base-URL fallback. Plus the http-path wiring threads it into
|
|
``create_server``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
# Skip cleanly when the `mcp` extra (fastmcp) is absent; see conftest.py.
|
|
pytest.importorskip("fastmcp")
|
|
|
|
from notebooklm.mcp import __main__ as entry # noqa: E402 - after importorskip guard
|
|
from notebooklm.mcp._filelink import FileTransferConfig # noqa: E402 - after importorskip guard
|
|
|
|
PUBLIC = entry.PUBLIC_URL_ENV
|
|
OAUTH_BASE = entry.OAUTH_BASE_URL_ENV
|
|
ALLOW_EXTERNAL = entry.ALLOW_EXTERNAL_BIND_ENV
|
|
TOKEN = entry.MCP_TOKEN_ENV
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
for var in (PUBLIC, OAUTH_BASE, ALLOW_EXTERNAL, TOKEN, "NOTEBOOKLM_MCP_OAUTH_PASSWORD"):
|
|
monkeypatch.delenv(var, raising=False)
|
|
|
|
|
|
def test_build_none_when_no_url() -> None:
|
|
assert entry._build_file_transfer() is None
|
|
|
|
|
|
def test_build_from_public_url(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv(PUBLIC, "https://files.example")
|
|
cfg = entry._build_file_transfer()
|
|
assert isinstance(cfg, FileTransferConfig)
|
|
assert cfg.base_url == "https://files.example"
|
|
assert len(cfg.signer.key) == 32 # ephemeral per-process key
|
|
|
|
|
|
def test_build_strips_whitespace(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv(PUBLIC, " https://files.example ")
|
|
cfg = entry._build_file_transfer()
|
|
assert cfg is not None and cfg.base_url == "https://files.example"
|
|
|
|
|
|
def test_build_falls_back_to_oauth_base_url(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv(OAUTH_BASE, "https://oauth.example")
|
|
cfg = entry._build_file_transfer()
|
|
assert cfg is not None and cfg.base_url == "https://oauth.example"
|
|
|
|
|
|
def test_public_url_wins_over_oauth_base(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv(PUBLIC, "https://files.example")
|
|
monkeypatch.setenv(OAUTH_BASE, "https://oauth.example")
|
|
cfg = entry._build_file_transfer()
|
|
assert cfg is not None and cfg.base_url == "https://files.example"
|
|
|
|
|
|
@pytest.mark.parametrize("bad", ["http://files.example", "https://files.example/mcp", "not-a-url"])
|
|
def test_invalid_public_url_exits(monkeypatch: pytest.MonkeyPatch, bad: str) -> None:
|
|
monkeypatch.setenv(PUBLIC, bad)
|
|
with pytest.raises(SystemExit):
|
|
entry._build_file_transfer()
|
|
|
|
|
|
def test_http_external_bind_no_url_starts_with_file_transfer_none(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""External http bind + a bearer + NO public URL → starts fine, file_transfer
|
|
is None (no SystemExit)."""
|
|
monkeypatch.setenv(ALLOW_EXTERNAL, "1")
|
|
monkeypatch.setenv(TOKEN, "a-strong-random-bearer-token")
|
|
seen: dict[str, object] = {}
|
|
fake_server = MagicMock()
|
|
fake_server.run = lambda **k: None
|
|
|
|
def fake_create(**kw: object) -> MagicMock:
|
|
seen.update(kw)
|
|
return fake_server
|
|
|
|
monkeypatch.setattr(entry, "create_server", fake_create)
|
|
entry.main(["--transport", "http", "--host", "0.0.0.0", "--port", "9123"])
|
|
assert "file_transfer" in seen
|
|
assert seen["file_transfer"] is None
|
|
|
|
|
|
def test_http_with_public_url_threads_config_into_create_server(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setenv(PUBLIC, "https://files.example")
|
|
seen: dict[str, object] = {}
|
|
fake_server = MagicMock()
|
|
fake_server.run = lambda **k: None
|
|
|
|
def fake_create(**kw: object) -> MagicMock:
|
|
seen.update(kw)
|
|
return fake_server
|
|
|
|
monkeypatch.setattr(entry, "create_server", fake_create)
|
|
# Loopback http needs no auth; the public URL still builds the config.
|
|
entry.main(["--transport", "http", "--host", "127.0.0.1", "--port", "9123"])
|
|
assert isinstance(seen["file_transfer"], FileTransferConfig)
|
|
assert seen["file_transfer"].base_url == "https://files.example"
|