test(backend): pure-logic pytest harness, run in an isolated test image
The backend had zero tests. This adds 33, all pure (no DB, no network), plus the harness to run them in the gate: - backend/Dockerfile.test: the app's deps + pinned pytest/ruff, code bind-mounted at run time so it always tests the working tree. Isolated from the prod Dockerfile, so the published image never carries test tooling. - requirements-dev.txt: pytest==8.4.2, ruff==0.15.21 (the version the gate already runs) — the backend lane is now pinned, not just host-global. - tests: normalize_title (de-shout, hashtag strip, emoji drop, trilingual letters survive); storage sanitize/rel_path/download_filename and the safe_abs_path traversal guard (../ and absolute-path escapes rejected — verified by mutation); links HMAC grants (round-trip, wrong-token, tampered sig/exp, expiry) + is_expired; a DB-free app-assembly smoke (every router wires up, OpenAPI generates). DB-backed router smoke (auth/feed/downloads) needs a test Postgres + migrations — deferred. useCardPager needs hook-test infra (jsdom/renderHook) — deferred.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
"""App-assembly smoke test — DB-free.
|
||||
|
||||
Uses TestClient WITHOUT its context-manager form on purpose: that skips the lifespan (which opens a
|
||||
DB session + starts the scheduler), so the app object can be built and its OpenAPI schema generated
|
||||
with no database. That alone proves every included router imported and wired cleanly — a broken
|
||||
route registration or import fails right here.
|
||||
|
||||
Endpoints that actually answer (/healthz, /api/version, the auth/feed/downloads routers) all touch
|
||||
the database, so exercising THEM belongs in a DB-backed integration lane (needs a test Postgres +
|
||||
migrations) — deferred; see siftlode-ops/ROADMAP.md R2 S2b.
|
||||
"""
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
|
||||
client = TestClient(app) # no `with` → lifespan/startup does not run → no DB required
|
||||
|
||||
|
||||
def test_app_assembles_and_serves_openapi():
|
||||
schema = client.get("/openapi.json")
|
||||
assert schema.status_code == 200
|
||||
paths = schema.json()["paths"]
|
||||
# A handful of endpoints that must always exist — a rename/removal is a real contract change,
|
||||
# and their absence here would mean a router failed to register.
|
||||
for path in ["/healthz", "/api/version", "/api/feed", "/api/me"]:
|
||||
assert path in paths, f"missing route {path} — a router failed to wire up"
|
||||
Reference in New Issue
Block a user