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:
@@ -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
|
||||
Reference in New Issue
Block a user