Files
codex-py/tests/scaffold/test_db.py
Tarik Moussa cdb7d254df feat(db): idempotent schema + 'codex migrate' command (audit M-1, T-2)
M-1: schema.sql could not be re-applied (leading CREATE TABLE/INDEX lacked
IF NOT EXISTS), apply_schema was never called, and the live migration just
failed on a missing chunks.section column. Fixes:

- schema.sql: all CREATE TABLE/INDEX now use IF NOT EXISTS — the whole file is
  re-applyable as a no-op.
- codex migrate: new CLI command that applies schema.sql. Connects via
  MIGRATION_DATABASE_URL (falls back to DATABASE_URL) and catches
  InsufficientPrivilege with guidance — because the app role is DML-only and
  core tables are owned by 'postgres' (the privilege dimension found during the
  live migration incident).
- config: migration_database_url (optional privileged connection).
- db: apply_schema docstring corrected (idempotency now true) + privilege note.
- T-2: static test that schema.sql is fully idempotent; migrate privilege-error
  test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-15 15:53:25 +02:00

73 lines
2.5 KiB
Python

"""Smoke tests for codex.db — get_conn and apply_schema."""
from __future__ import annotations
from unittest.mock import MagicMock, patch
def test_get_conn_context_manager() -> None:
"""get_conn() yields a context manager that calls psycopg.connect and cleans up."""
from codex.db import get_conn
mock_conn = MagicMock()
mock_connect = MagicMock()
# psycopg.connect is itself used as a context manager inside get_conn
mock_connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
mock_connect.return_value.__exit__ = MagicMock(return_value=False)
with patch("codex.db.psycopg.connect", mock_connect), get_conn() as conn:
assert conn is mock_conn
mock_connect.assert_called_once()
def test_apply_schema_executes_sql() -> None:
"""apply_schema(conn) calls conn.execute and conn.commit."""
from codex.db import apply_schema
mock_conn = MagicMock()
# Provide a fake schema.sql so Path.read_text succeeds
with patch("codex.db.Path.read_text", return_value="CREATE TABLE IF NOT EXISTS test ();"):
apply_schema(mock_conn)
mock_conn.execute.assert_called_once()
mock_conn.commit.assert_called_once()
def test_schema_sql_is_idempotent() -> None:
"""Every CREATE TABLE/INDEX in infra/schema.sql uses IF NOT EXISTS (audit M-1, T-2).
Re-applying schema.sql must be a safe no-op so the migration can run
repeatedly against an existing database.
"""
import re
from pathlib import Path
schema = (Path(__file__).resolve().parents[2] / "infra" / "schema.sql").read_text()
# Drop line comments so commented-out example queries don't count.
code = "\n".join(line.split("--", 1)[0] for line in schema.splitlines())
non_idempotent = re.findall(
r"CREATE\s+(?:UNIQUE\s+)?(?:TABLE|INDEX)\s+(?!IF\s+NOT\s+EXISTS)(\w+)",
code,
re.IGNORECASE,
)
assert not non_idempotent, f"schema.sql has non-idempotent CREATE statements: {non_idempotent}"
def test_migrate_command_reports_privilege_error() -> None:
"""`codex migrate` surfaces InsufficientPrivilege with guidance and exits 1 (M-1)."""
import psycopg
from typer.testing import CliRunner
from codex.cli import app
def boom(*args: object, **kwargs: object) -> object:
raise psycopg.errors.InsufficientPrivilege("must be owner of table chunks")
with patch("psycopg.connect", boom):
result = CliRunner().invoke(app, ["migrate"])
assert result.exit_code == 1
assert "MIGRATION_DATABASE_URL" in result.output