feat(slice-1): platform/db migration runner + migration 0001 (INV-7, §6.3 schema)

Forward-only, fail-stop, advisory-lock-guarded psycopg runner; migration 0001 lays
the account/auth_code/storefront/membership schema. Tests prove migrate-from-empty
and idempotent re-migrate (INV-1 partial) and the INV-4 many-capable shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-10 08:37:09 -07:00
parent ab4317e105
commit 9462423642
4 changed files with 225 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
"""Test fixtures — a fresh, empty PostgreSQL database per test.
Each test gets its own database created on the dev/CI Postgres (every test runs the
real engine, §6.8). The admin DSN points at the `postgres` maintenance database;
ECOMM_TEST_ADMIN_URL overrides it (CI sets it to the service container).
"""
from __future__ import annotations
import os
import uuid
import psycopg
import pytest
ADMIN_URL = os.environ.get(
"ECOMM_TEST_ADMIN_URL", "postgresql://ecomm:ecomm@localhost:5432/postgres"
)
def _swap_dbname(dsn: str, dbname: str) -> str:
# ADMIN_URL ends in `/postgres`; point the same credentials at `dbname`.
base, _, _old = dsn.rpartition("/")
return f"{base}/{dbname}"
@pytest.fixture()
def fresh_db_url():
"""Create a uniquely-named empty database; drop it after the test."""
dbname = f"ecomm_test_{uuid.uuid4().hex}"
admin = psycopg.connect(ADMIN_URL, autocommit=True)
try:
admin.execute(f'CREATE DATABASE "{dbname}"')
finally:
admin.close()
try:
yield _swap_dbname(ADMIN_URL, dbname)
finally:
admin = psycopg.connect(ADMIN_URL, autocommit=True)
try:
admin.execute(
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity "
"WHERE datname = %s AND pid <> pg_backend_pid()",
(dbname,),
)
admin.execute(f'DROP DATABASE IF EXISTS "{dbname}"')
finally:
admin.close()
+43
View File
@@ -0,0 +1,43 @@
import psycopg
from app.platform import db
_TABLES = {"account", "auth_code", "storefront", "storefront_membership"}
def _table_names(conn) -> set[str]:
rows = conn.execute(
"SELECT tablename FROM pg_tables WHERE schemaname = 'public'"
).fetchall()
return {r[0] for r in rows}
def test_migrate_from_empty_applies_0001(fresh_db_url):
with psycopg.connect(fresh_db_url) as conn:
applied = db.migrate(conn)
assert applied == ["0001_init.sql"]
with psycopg.connect(fresh_db_url) as conn:
assert _TABLES.issubset(_table_names(conn))
def test_migrate_is_idempotent(fresh_db_url):
with psycopg.connect(fresh_db_url) as conn:
db.migrate(conn)
with psycopg.connect(fresh_db_url) as conn:
applied_again = db.migrate(conn)
assert applied_again == []
def test_membership_has_no_unique_account_constraint(fresh_db_url):
# INV-4: the schema must keep many-storefronts-per-account open — no UNIQUE on
# storefront_membership.account_id anywhere.
with psycopg.connect(fresh_db_url) as conn:
db.migrate(conn)
rows = conn.execute(
"""
SELECT indexdef FROM pg_indexes
WHERE schemaname = 'public' AND tablename = 'storefront_membership'
"""
).fetchall()
defs = " ".join(r[0] for r in rows).lower()
assert "unique" not in defs.replace("primary key", "") or "(account_id)" not in defs