Stand up the full SQLite content layer: all 7 tables from the authoritative schema with WAL + foreign-keys enforced per-connection, entity dataclasses plus row mappers, hand-rolled versioned migrations tracked in schema_migrations, and an idempotent Python seed (system user + welcome post + About page). Add a Markdown->HTML service using markdown-it-py with a strict bleach allowlist (tables intentionally omitted on both sides). Add a typed in-process TTLCache[K,V] and wire it into real DB-backed PostService and PageService, both exposing invalidate_all() for Phase 4 admin writes. Rewire / and /about to read from the DB; homepage renders the seeded welcome post, About renders page.title + sanitized body_html_cached. Update the Phase 1 route tests accordingly. Mark Phase 2 complete in docs/ROADMAP.md.
57 lines
1.8 KiB
Python
57 lines
1.8 KiB
Python
"""Shared pytest fixtures for the ``chicken_babies_site`` suite.
|
|
|
|
Key fixtures:
|
|
|
|
- ``db_engine``: a session-scoped SQLAlchemy engine pointed at a
|
|
temp-file SQLite database. Migrations + seed run once per test
|
|
session. Per the CLAUDE.md mandate, tests do NOT mock the DB —
|
|
they use a real SQLite file so behavior matches production.
|
|
- ``clean_db_engine``: a function-scoped engine with migrations
|
|
applied but seed NOT run, for tests that need to exercise the
|
|
first-boot path.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Iterator
|
|
|
|
import pytest
|
|
from sqlalchemy import Engine
|
|
|
|
from app.db import build_engine, run_migrations
|
|
from app.models.seed import run_seed
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def db_engine(tmp_path_factory: pytest.TempPathFactory) -> Iterator[Engine]:
|
|
"""Return a migrated + seeded SQLite engine shared across the session.
|
|
|
|
Uses a real on-disk file (NOT ``:memory:``) because the CLAUDE.md
|
|
project rules forbid mocking the DB in auth / magic-link tests,
|
|
and doing the same here keeps the behavior identical to
|
|
production.
|
|
"""
|
|
db_path: Path = tmp_path_factory.mktemp("db") / "test.db"
|
|
engine = build_engine(f"sqlite:///{db_path}")
|
|
run_migrations(engine)
|
|
run_seed(engine)
|
|
yield engine
|
|
engine.dispose()
|
|
|
|
|
|
@pytest.fixture
|
|
def clean_db_engine(tmp_path: Path) -> Iterator[Engine]:
|
|
"""Return a fresh engine with tables created but NO seed data.
|
|
|
|
Function-scoped so each test that uses it starts with a virgin
|
|
database — useful for asserting first-run seed behavior and
|
|
migration idempotency without contaminating the session-scoped
|
|
``db_engine``.
|
|
"""
|
|
db_path = tmp_path / "clean.db"
|
|
engine = build_engine(f"sqlite:///{db_path}")
|
|
run_migrations(engine)
|
|
yield engine
|
|
engine.dispose()
|