"""Alembic migration smoke test. We do not boot a real Postgres in CI; instead we point Alembic at an in-process SQLite database and verify: - ``alembic upgrade head`` succeeds offline (SQL generation) using the real migration files, exercising every column type and constraint declaration; - ``downgrade base`` rewinds without errors. This catches typos and broken migration ordering early without requiring the full backing-service compose stack to be online. """ from __future__ import annotations import os import sys from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) @pytest.fixture def alembic_cfg(tmp_path, monkeypatch): """Configure Alembic against an isolated SQLite file.""" db_file = tmp_path / "legacyhub.db" monkeypatch.setenv("POSTGRES_HOST", "127.0.0.1") monkeypatch.setenv("POSTGRES_PORT", "5432") # Force a fresh Settings + Alembic env that ignores the configured PG. from alembic.config import Config cfg = Config(str(ROOT / "alembic.ini")) cfg.set_main_option("script_location", str(ROOT / "app" / "db" / "migrations")) cfg.set_main_option("sqlalchemy.url", f"sqlite:///{db_file}") return cfg def test_migration_offline_emits_sql(alembic_cfg, tmp_path): """Offline mode generates SQL for every table; verify ``documents`` appears and at least one JSONB-equivalent column is rendered. SQLite has no JSONB but Alembic's offline mode happily emits the raw DDL for inspection. """ from alembic import command out_file = tmp_path / "upgrade.sql" # ``--sql`` mode bypasses dialect-specific runtime, perfect for a fast check. with out_file.open("w", encoding="utf-8") as f: old_stdout = sys.stdout sys.stdout = f try: command.upgrade(alembic_cfg, "head", sql=True) finally: sys.stdout = old_stdout sql = out_file.read_text(encoding="utf-8") assert "CREATE TABLE documents" in sql assert "CREATE TABLE chunks" in sql assert "CREATE TABLE processing_events" in sql # Constraint sanity assert "uq_chunks_doc_idx" in sql assert "uq_pages_doc_page" in sql def test_revision_history_is_linear(alembic_cfg): """The current project has a single linear history at 0001_initial.""" from alembic.script import ScriptDirectory script = ScriptDirectory.from_config(alembic_cfg) heads = script.get_heads() assert len(heads) == 1, f"expected one head, got: {heads}" initial = next(iter(script.walk_revisions())) assert initial.revision == "0001_initial"