Gateway: pytest suite #8

Closed
opened 2026-08-09 20:31:47 +00:00 by hermes · 1 comment
Owner

Context

Gateway service (see #18). Test conventions follow the beets plugin's
tests/ (FakeResponse/FakeSession classes, no network).

Task

A tests/ suite covering:

  • dev-token signing (generated test EC key), caching, threshold refresh
  • credential store: round-trip, clear, 0o600 creation, permission warning
  • login page + callback capture; token absent from logs
  • API routes: bearer rejection, status, playlists, tracks, logout,
    Apple-401 -> 502 re-login hint
  • Apple client: pagination, nested splicing, video skipping, 429 retry
    (ported from the plugin tests)

Dev deps: pytest>=8, httpx (FastAPI TestClient). Add
[tool.pytest.ini_options] testpaths = ["tests"].

Acceptance criteria

  • uv run pytest green; no test makes a real network call.
## Context Gateway service (see #18). Test conventions follow the beets plugin's `tests/` (FakeResponse/FakeSession classes, no network). ## Task A `tests/` suite covering: - dev-token signing (generated test EC key), caching, threshold refresh - credential store: round-trip, clear, 0o600 creation, permission warning - login page + callback capture; token absent from logs - API routes: bearer rejection, status, playlists, tracks, logout, Apple-401 -> 502 re-login hint - Apple client: pagination, nested splicing, video skipping, 429 retry (ported from the plugin tests) Dev deps: `pytest>=8`, `httpx` (FastAPI TestClient). Add `[tool.pytest.ini_options] testpaths = ["tests"]`. ## Acceptance criteria - `uv run pytest` green; no test makes a real network call.
Author
Owner

Done. uv run pytest is green (62 passed, no network calls). The suite lives in tests/ and covers everything on the checklist:

  • test_apple_auth.py — ES256 signing with a generated P-256 key, caching within the 30-day threshold, refresh past it (incl. exact-threshold boundary), unreadable/garbage keys, no token in repr, concurrency
  • test_credential_store.py — round-trip, clear, owner-only 0o600 creation, permission warnings (world/group-readable), no auto-chmod, upsert keeps one row, lazy reads never create the file
  • test_login_routes.py — login page embeds a fresh valid dev token + redirect target; callback captures the token without echoing it; token absent from app logs and access-log query strings scrubbed
  • test_api_routes.py — bearer rejection (401 on all four routes, wrong token), status/playlists/tracks/logout, Apple 401/403 -> 502 re-login hint, missing token -> 502
  • test_apple_api.py — pagination offsets, nested-playlist splicing, music-video skipping, 429 retry, status_code on AppleAPIError
  • test_config.py + test_healthz.py — env parsing, fail-fast startup naming missing vars, healthz

Dev deps (pytest>=8, httpx) and [tool.pytest.ini_options] testpaths = ["tests"] are in pyproject.toml.

Done. `uv run pytest` is green (62 passed, no network calls). The suite lives in `tests/` and covers everything on the checklist: - `test_apple_auth.py` — ES256 signing with a generated P-256 key, caching within the 30-day threshold, refresh past it (incl. exact-threshold boundary), unreadable/garbage keys, no token in repr, concurrency - `test_credential_store.py` — round-trip, clear, owner-only 0o600 creation, permission warnings (world/group-readable), no auto-chmod, upsert keeps one row, lazy reads never create the file - `test_login_routes.py` — login page embeds a fresh valid dev token + redirect target; callback captures the token without echoing it; token absent from app logs and access-log query strings scrubbed - `test_api_routes.py` — bearer rejection (401 on all four routes, wrong token), status/playlists/tracks/logout, Apple 401/403 -> 502 re-login hint, missing token -> 502 - `test_apple_api.py` — pagination offsets, nested-playlist splicing, music-video skipping, 429 retry, status_code on AppleAPIError - `test_config.py` + `test_healthz.py` — env parsing, fail-fast startup naming missing vars, healthz Dev deps (`pytest>=8`, `httpx`) and `[tool.pytest.ini_options] testpaths = ["tests"]` are in `pyproject.toml`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coop/beets-appleplaylists#8
No description provided.