fix(test): router-smoke tests leak-proof against full-suite order (#932)
tests/test_router_smoke.py showed ~10 `sqlite3.OperationalError: no such table: jobs` failures in the full suite (and in isolation on a clean data dir), but passed when a schema-creating module ran first. Root cause: the `client` fixture builds a bare `TestClient(app)` with no `with` block, so the FastAPI lifespan never runs — and `init_db()` (the only place the schema is created) lives in that lifespan (main.py). The smoke tests therefore free-rode on whatever schema an earlier module left on the active DB. A module that reloads `core.config`/`core.db` and leaves `core.db.DB_PATH` pointed at a fresh, schema-less DB (test_pronunciation_api's `importlib.reload` teardown restores the env var but never re-runs init_db on the restored data dir) strands router-smoke on a DB with no tables -> every DB-backed route 500s. Same class as #878 / #917. Fix (test-only, zero blast radius): the `client` fixture now calls `core.db.init_db()` against whatever DB is active at run time before serving requests — the same `init_db()` pattern test_api.py / test_personas_api.py use. Because it targets the live `core.db.DB_PATH`, it re-creates the schema regardless of which path any prior module left active, making the suite self-sufficient and order-independent. Verify: - `pytest tests/test_router_smoke.py` alone: 10 failed -> 24 passed - `pytest tests/test_pronunciation_api.py tests/test_router_smoke.py` (deterministic reproducer): 10 failed -> 38 passed - `pytest tests/` full suite: 2215 passed, 20 skipped, 10 xfailed, 4 xpassed, 0 failed / 0 errors Co-authored-by: mergetest <test@local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
mergetest
Claude Fable 5
parent
0d80ab2cb0
commit
ed0b0b63cf
@@ -17,12 +17,31 @@ os.environ.setdefault("OMNIVOICE_DISABLE_FILE_LOG", "1")
|
||||
def client():
|
||||
# Lazy import so test_api.py's session fixtures can mock the model first
|
||||
# if both suites run together.
|
||||
# `client=("127.0.0.1", 50000)` so `request.client.host` resolves to a
|
||||
# loopback address — the system router is now gated by a router-level
|
||||
# `require_loopback` dependency. Smoke tests are happy-path tests and
|
||||
# should pass the gate.
|
||||
from fastapi.testclient import TestClient
|
||||
from main import app
|
||||
|
||||
# Create the DB schema explicitly against whatever DB is active *now*.
|
||||
# A bare `TestClient(app)` (no `with` block) never enters the app
|
||||
# lifespan, so the lifespan's `init_db()` — the only place the schema is
|
||||
# laid down (main.py) — never runs. These smoke tests therefore used to
|
||||
# free-ride on the schema some *earlier* module happened to create on the
|
||||
# shared data dir. That made them order-dependent: run first / alone, or
|
||||
# after a module that reloads `core.config`/`core.db` and leaves
|
||||
# `core.db.DB_PATH` pointed at a fresh, schema-less DB (e.g.
|
||||
# test_pronunciation_api's `importlib.reload` teardown), and every
|
||||
# DB-backed route 500s with `no such table: jobs` / `voice_profiles`.
|
||||
# Seeding the schema here — the same `init_db()` call test_api.py /
|
||||
# test_personas_api.py use — makes the suite self-sufficient and
|
||||
# leak-proof against full-suite ordering (same class as #878 / #917).
|
||||
# Uses the live `core.db` from sys.modules so it targets the exact
|
||||
# DB_PATH the app's routers resolve to.
|
||||
import core.db
|
||||
core.db.init_db()
|
||||
|
||||
# `client=("127.0.0.1", 50000)` so `request.client.host` resolves to a
|
||||
# loopback address — the system router is gated by a router-level
|
||||
# `require_loopback` dependency. Smoke tests are happy-path tests and
|
||||
# should pass the gate.
|
||||
return TestClient(app, client=("127.0.0.1", 50000))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user