| """Schema sanity tests — invariants we want CI to enforce. |
| |
| These don't need a live database; they walk the SQLAlchemy metadata. |
| """ |
|
|
| from sqlalchemy import Table, UniqueConstraint |
|
|
| from store.models import Base |
|
|
|
|
| def _unique_column_tuples(table: Table) -> set[tuple[str, ...]]: |
| """Return every (col, col, ...) tuple covered by a UniqueConstraint or unique Index.""" |
| pairs: set[tuple[str, ...]] = set() |
| for constraint in table.constraints: |
| if isinstance(constraint, UniqueConstraint): |
| pairs.add(tuple(c.name for c in constraint.columns)) |
| for idx in table.indexes: |
| if idx.unique: |
| pairs.add(tuple(c.name for c in idx.columns)) |
| return pairs |
|
|
| |
| |
| EXPECTED_TABLES = frozenset( |
| { |
| "subreddit_profile", |
| "user_memory", |
| "investigation", |
| "evidence", |
| "feedback", |
| "audit_log", |
| } |
| ) |
|
|
|
|
| def test_all_required_tables_declared() -> None: |
| declared = set(Base.metadata.tables.keys()) |
| missing = EXPECTED_TABLES - declared |
| assert not missing, f"missing tables: {sorted(missing)}" |
|
|
|
|
| def test_every_table_has_subreddit_id() -> None: |
| """Invariant I-7: every persisted row is subreddit_id-scoped.""" |
| offenders = [] |
| for name, table in Base.metadata.tables.items(): |
| if "subreddit_id" not in table.columns: |
| offenders.append(name) |
| assert not offenders, f"tables missing subreddit_id: {offenders}" |
|
|
|
|
| def test_investigation_correlation_id_unique() -> None: |
| table = Base.metadata.tables["investigation"] |
| assert ("correlation_id",) in _unique_column_tuples(table) |
|
|
|
|
| def test_user_memory_uniqueness() -> None: |
| """Per-(subreddit, user) memory is one row, not many.""" |
| table = Base.metadata.tables["user_memory"] |
| assert ("subreddit_id", "user_id") in _unique_column_tuples(table) |
|
|
|
|
| def test_evidence_id_unique_per_investigation() -> None: |
| """`ev-N` ids must be unique within an investigation.""" |
| table = Base.metadata.tables["evidence"] |
| assert ("investigation_id", "evidence_id") in _unique_column_tuples(table) |
|
|