Implements Option A from the issue design: each cloud user gets their own
data directory (DATA_DIR/users/{user_id}/) with separate pagepiper.db,
pagepiper_vecs.db, uploads/, and books/. Local mode is unchanged.
Key changes:
- app/startup.py: extract apply_migrations, reembed_docs,
check_and_rebuild_vec_schema out of main.py (no circular imports)
- app/config.py: add LOCAL_USER_ID constant and user_data_dir() helper
- app/cloud_session.py: extract resolve_authenticated_user(); require_paid_tier
now returns user_id (str) instead of None
- app/deps.py: add UserCtx dataclass (db_path, vec_db_path, data_dir,
watch_dir, bm25) + get_user_ctx dependency; per-user startup guard runs
migrations + vec schema check once per process per user
- app/main.py: _bm25 singleton -> _bm25_map dict keyed by user_id;
add _get_bm25_for(); lifespan only runs startup checks in local mode
- app/api/library.py, search.py, chat.py: thread UserCtx through all
endpoints; remove module-level _mark_bm25_dirty injection pattern
- tests/conftest.py: override get_user_ctx in addition to get_db so all
endpoints get a consistent test UserCtx
69 lines
2 KiB
Python
69 lines
2 KiB
Python
# tests/conftest.py
|
|
"""Shared fixtures for pagepiper test suite."""
|
|
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
|
|
@pytest.fixture
|
|
def test_db(tmp_path) -> str:
|
|
db_path = str(tmp_path / "test.db")
|
|
schema = Path("migrations/001_initial_schema.sql").read_text()
|
|
conn = sqlite3.connect(db_path)
|
|
conn.executescript(schema)
|
|
conn.commit()
|
|
conn.close()
|
|
return db_path
|
|
|
|
|
|
@pytest.fixture
|
|
def client(test_db, tmp_path, monkeypatch):
|
|
monkeypatch.setenv("PAGEPIPER_DATA_DIR", str(tmp_path))
|
|
monkeypatch.setenv("PAGEPIPER_WATCH_DIR", str(tmp_path / "books"))
|
|
(tmp_path / "books").mkdir(exist_ok=True)
|
|
|
|
import app.main as _main_module
|
|
from app.config import LOCAL_USER_ID
|
|
from app.deps import UserCtx, get_db, get_user_ctx
|
|
from app.main import app
|
|
from app.services.bm25_index import BM25Index
|
|
from app.startup import apply_migrations, check_and_rebuild_vec_schema
|
|
|
|
monkeypatch.setattr(_main_module, "_apply_migrations", lambda: None, raising=False)
|
|
monkeypatch.setattr(
|
|
"app.startup.apply_migrations", lambda *a, **kw: None
|
|
)
|
|
monkeypatch.setattr(
|
|
"app.startup.check_and_rebuild_vec_schema", lambda *a, **kw: None
|
|
)
|
|
|
|
test_bm25 = BM25Index()
|
|
test_bm25.mark_dirty()
|
|
|
|
def override_user_ctx():
|
|
return UserCtx(
|
|
user_id=LOCAL_USER_ID,
|
|
db_path=test_db,
|
|
vec_db_path=str(tmp_path / "test_vecs.db"),
|
|
data_dir=Path(tmp_path),
|
|
watch_dir=Path(tmp_path) / "books",
|
|
bm25=test_bm25,
|
|
)
|
|
|
|
def override_db():
|
|
conn = sqlite3.connect(test_db)
|
|
conn.execute("PRAGMA foreign_keys = ON")
|
|
conn.row_factory = sqlite3.Row
|
|
try:
|
|
yield conn
|
|
finally:
|
|
conn.close()
|
|
|
|
app.dependency_overrides[get_user_ctx] = override_user_ctx
|
|
app.dependency_overrides[get_db] = override_db
|
|
yield TestClient(app)
|
|
app.dependency_overrides.clear()
|