Everything is verified green. Final report: **Phase 106 — Document dates (final verification pass; all 10 tasks already complete)** - Verified all phase artifacts: alembic `0020` (dev DB at `0020`), `doc_dates.py`, git `file_commit_dates`, importer `doc_dates_by_root`/`dates_updated`, both entry-point wirings, date APIs + tree `created_at`/`updated_at`, LLM surfaces (prompt block, `read` line 2, appended `ls` field), `apply_recency_boost` in `retrieve()`, UI columns/badge, admin editor, mock-LLM regex — all present and correct; no defects found, no fixes needed. - `uv run pytest --cov=app --cov-report=term-missing` → **2299 passed, TOTAL 99%** (>90% ✓) - `uv run pytest tests/e2e/test_document_dates.py -v --no-cov` → **6/6 passed** in isolation (DB up) - 12 regression E2E suites (retrieval_quality, whole_document_context, agent_document_tools, ls_tree_drilldown, read_truncation_cap, kb_tree, kb_tree_nav, document_viewer, edit_summaries, import_documents, sync_button, hidden_folders_toggle, smoke) → **all green in isolation** - `uv run ruff check .` → clean; `uv run pyright` → **0 errors, 0 warnings** **Completion criteria:** 1) non-null `created_at` + 0020 upgrade/downgrade on dev DB ✓ (real-Alembic integration tests) 2) sync refresh/older/manual-persists/content-reset/no sources_meta bump ✓ 3) zip/tar mtime + future→today ✓ 4) LLM date surfaces + cross-check ✓ 5) UI Created/Updated/badge positions ✓ 6) admin editor set+revert round-trip ✓ 7) old-correct-beats-new-similar (defaults & boost-off) + near-tie + `BOR_RECENCY_BOOST=0` byte-identical ✓ 8) full gate ✓ 9) commit/phase-move — left to harness per instructions. - **Notable:** recency default tuned 0.001 → **0.0007** (task 07 step 5 explicitly permits; measured margins recorded in `test_recency_boost.py` docstring). - **Next pending phase:** none — `todo/` holds only this phase.
97 lines
4.1 KiB
Python
97 lines
4.1 KiB
Python
"""Shared fixtures for unit + integration tests."""
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
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)
|
||
|
||
# Phase 61 (defect fix): the app under test must see the code DEFAULTS,
|
||
# not an operator's local (gitignored) ``.env`` — ``Settings`` loads
|
||
# ``env_file=".env"`` from the repo root, and a machine-specific corpus
|
||
# (e.g. ``BOR_SUGGESTIONS``, ``BOR_DOCS_REPO``) leaked into the tests
|
||
# broke the default-metadata pins. pydantic-settings ranks process env
|
||
# vars ABOVE the ``.env`` file, so force the defaults explicitly here,
|
||
# before ``app.main`` (below) caches settings. The suggestions default
|
||
# is derived from the class field so this can never drift from
|
||
# ``app/config.py``; docs-push stays inert (empty repo).
|
||
from app.config import Settings as _Settings # noqa: E402
|
||
|
||
os.environ["BOR_DOCS_REPO"] = ""
|
||
os.environ["BOR_SUGGESTIONS"] = json.dumps(_Settings.model_fields["suggestions"].default)
|
||
|
||
# Phase 62: the same leak class for the new UI customization settings —
|
||
# an operator's local ``.env`` may legitimately carry
|
||
# ``BOR_INPUT_PLACEHOLDER`` / ``BOR_FOOTER_TEXT``, and the
|
||
# default-metadata pins must see the code defaults (derived from
|
||
# the class fields, same pattern as the suggestions line above).
|
||
# (Phase 91, task 03: the retired CSS-file theme env var no longer
|
||
# exists — nothing to pin.)
|
||
os.environ["BOR_INPUT_PLACEHOLDER"] = _Settings.model_fields["input_placeholder"].default
|
||
os.environ["BOR_FOOTER_TEXT"] = _Settings.model_fields["footer_text"].default
|
||
|
||
# Phase 106 (task 07): the same leak class for the recency-boost
|
||
# settings — an operator's local ``.env`` may legitimately carry
|
||
# ``BOR_RECENCY_BOOST`` / ``BOR_RECENCY_HALF_LIFE_DAYS`` re-tuned live,
|
||
# and the fine-line integration battery pins the CODE DEFAULTS
|
||
# (derived from the class fields, same pattern as the lines above).
|
||
os.environ["BOR_RECENCY_BOOST"] = str(_Settings.model_fields["recency_boost"].default)
|
||
os.environ["BOR_RECENCY_HALF_LIFE_DAYS"] = str(
|
||
_Settings.model_fields["recency_half_life_days"].default
|
||
)
|
||
|
||
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()
|