62 lines
2.2 KiB
Python
62 lines
2.2 KiB
Python
"""Shared fixtures for unit + integration tests."""
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
from collections.abc import Iterator
|
||
|
||
import pytest
|
||
from fastapi.testclient import TestClient
|
||
from sqlalchemy.orm import Session
|
||
|
||
# In-process integration/E2E tests drive the app with *mock* embeddings
|
||
# (bag-of-words, cosine ~0.1–0.8), not the live aipi model — so the honesty
|
||
# gate is calibrated to the mock's distribution, mirroring tests/e2e/
|
||
# conftest.py. Must be set before ``app.main`` (below) caches settings.
|
||
# The production default stays 0.62 (app/config.py, A8 revised).
|
||
os.environ.setdefault("BOR_RELEVANCE_THRESHOLD", "0.30")
|
||
|
||
# Phase 16: single-admin auth is fail-loud — create_app() refuses to boot
|
||
# without both vars, and app.main (imported below) builds the app at
|
||
# import time. Set known test values first, same pattern as the threshold.
|
||
ADMIN_PASSWORD = "test-admin-password"
|
||
SESSION_SECRET = "test-session-secret-0123456789abcdef0123456789abcdef"
|
||
os.environ.setdefault("BOR_ADMIN_PASSWORD", ADMIN_PASSWORD)
|
||
os.environ.setdefault("BOR_SESSION_SECRET", SESSION_SECRET)
|
||
|
||
from app.db import SessionLocal, db_available # noqa: E402
|
||
from app.main import app as fastapi_app # noqa: E402
|
||
|
||
|
||
@pytest.fixture()
|
||
def client() -> TestClient:
|
||
return TestClient(fastapi_app)
|
||
|
||
|
||
@pytest.fixture()
|
||
def admin_client(client: TestClient) -> TestClient:
|
||
"""A client signed in as the single admin (phase 16).
|
||
|
||
TestClient keeps its cookie jar across requests, so one login covers
|
||
every subsequent request of the test. Use it for the admin-only
|
||
surface (``GET /api/docs``, ``/api/steering``).
|
||
"""
|
||
r = client.post("/api/login", json={"password": ADMIN_PASSWORD})
|
||
assert r.status_code == 204, f"admin login failed: {r.status_code} {r.text}"
|
||
return client
|
||
|
||
|
||
@pytest.fixture()
|
||
def db() -> Iterator[Session]:
|
||
"""Real Postgres session (``podman compose up -d db``).
|
||
|
||
Skips with clear instructions when the database is not running, so the
|
||
suite degrades gracefully on a machine without the stack started.
|
||
"""
|
||
if not db_available():
|
||
pytest.skip("Postgres not reachable — run `podman compose up -d db` first")
|
||
session = SessionLocal()
|
||
try:
|
||
yield session
|
||
finally:
|
||
session.close()
|