项目文件夹

文件
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

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"