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

100 lines
3.4 KiB
Python

"""Shared fixtures for the REST server test suite.
The canonical contributor install omits the ``server`` extra (no fastapi), and CI
runs the full pytest after that install — so importing fastapi at collection time
would break the whole run. ``pytest.importorskip("fastapi")`` skips this entire
directory cleanly when the extra is absent; the server suite runs under
``--extra server``.
Every test drives the app through a FastAPI ``TestClient`` with an injected FAKE
client (``client_factory``) — no real auth, no network. The shared
``authed_client`` fixture sets the ``Authorization: Bearer`` header and a loopback
``Host`` on every request, satisfying both the bearer-token gate and the
DNS-rebinding (loopback-Host) guard.
"""
from __future__ import annotations
from collections.abc import AsyncIterator, Callable, Iterator
from contextlib import AbstractAsyncContextManager, asynccontextmanager
from typing import Any
import pytest
pytest.importorskip("fastapi")
from fastapi.testclient import TestClient # noqa: E402 - after importorskip guard
# Import the server package only after the importorskip guard.
from notebooklm.server._auth import SERVER_TOKEN_ENV # noqa: E402
from notebooklm.server.app import create_app # noqa: E402
from .fakes import FakeClient # noqa: E402
#: The bearer token the test server validates against.
TEST_TOKEN = "test-secret-token"
STALE_AUTH_MESSAGE = (
"Authentication expired or invalid. Redirected to: https://accounts.google.com/signin. "
"Run 'notebooklm login' to re-authenticate."
)
@pytest.fixture
def fake_client() -> FakeClient:
"""A fresh in-memory fake client with no notebooks/sources/artifacts."""
return FakeClient()
@pytest.fixture(autouse=True)
def _server_token(monkeypatch: pytest.MonkeyPatch) -> None:
"""Configure the bearer token for every server test."""
monkeypatch.setenv(SERVER_TOKEN_ENV, TEST_TOKEN)
def _factory_for(client: FakeClient) -> Any:
@asynccontextmanager
async def factory() -> Any:
yield client
return factory
def stale_auth_factory() -> Callable[[], AbstractAsyncContextManager[FakeClient]]:
@asynccontextmanager
async def factory() -> AsyncIterator[FakeClient]:
raise ValueError(STALE_AUTH_MESSAGE)
yield FakeClient() # pragma: no cover - makes this an async context manager
return factory
@pytest.fixture
def app(fake_client: FakeClient) -> Any:
"""A FastAPI app bound to the fake client."""
return create_app(client_factory=_factory_for(fake_client))
@pytest.fixture
def raw_client(app: Any) -> Iterator[TestClient]:
"""A TestClient WITHOUT auth headers (for auth-rejection tests).
``client=("127.0.0.1", …)`` gives requests a loopback PEER address so the
unspoofable peer-loopback guard passes — leaving the Host-header /
bearer-token gates as the thing under test.
"""
# raise_server_exceptions=False so a projected 500 envelope (the UNEXPECTED
# bug path) is returned rather than re-raised into the test.
with TestClient(app, client=("127.0.0.1", 5555), raise_server_exceptions=False) as client:
yield client
@pytest.fixture
def authed_client(app: Any) -> Iterator[TestClient]:
"""A TestClient with a valid bearer token + loopback Host + loopback peer."""
headers = {"Authorization": f"Bearer {TEST_TOKEN}", "Host": "127.0.0.1"}
with TestClient(
app, headers=headers, client=("127.0.0.1", 5555), raise_server_exceptions=False
) as client:
yield client