Files
LegacyHUB/tests/test_alembic.py
Vadim Malanov 785d3be970 test: add Alembic migration smoke and /search contract tests
tests/test_alembic.py points Alembic at an in-process SQLite database
in --sql mode so the migration files are validated end to end without
needing the real Postgres compose service. Asserts the documents,
chunks, and processing_events tables plus the unique constraints
appear in the generated DDL, and that the revision graph stays
linear at 0001_initial.

tests/test_routes_search.py monkeypatches
app.indexing.hybrid_search.run_search so the FastAPI route can be
exercised with the real SearchRequest/SearchResponse schemas. Covers
the happy path (rank, citation, reranked flag) and that empty queries
are rejected at schema validation before the backend is called.

pytest tests/test_alembic.py tests/test_routes_search.py -q: 4 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:54:15 +03:00

76 lines
2.6 KiB
Python

"""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"