Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions migrations/001_create_certificates_table.sql

This file was deleted.

31 changes: 0 additions & 31 deletions migrations/002_create_discussion_tables.sql

This file was deleted.

19 changes: 0 additions & 19 deletions migrations/003_create_bookmarks_table.sql

This file was deleted.

28 changes: 0 additions & 28 deletions migrations/004_create_progress_tables.sql

This file was deleted.

11 changes: 0 additions & 11 deletions migrations/005_create_user_game_progress.sql

This file was deleted.

21 changes: 21 additions & 0 deletions migrations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Migrations

DevPath does not use SQL migration files. The database schema is defined exclusively by
the SQLAlchemy models in `src/models.py` and is created with `db.create_all()` at startup
(see `src/app.py`, `src/seed_db.py`, and `tests/conftest.py`).

## Intended data model (source of truth: `src/models.py`)

- `projects` — the `Project` model
- `users` — the `User` model
- `project_progress` — the `ProjectProgress` model
- `user_game_progress` — the `UserGameProgress` model

Learning paths are kept in memory (`src/utils/learning_path.py`), not in the database, so
no `paths` table exists.

Hand-written SQL files previously placed in this directory referenced tables that no model
defines (including a dangling `FOREIGN KEY ... REFERENCES paths(id)` on a nonexistent
`paths` table) and could not be applied by any workflow. They were removed. If a real
migration workflow is ever needed, use Flask-Migrate/Alembic so the schema stays in sync
with `src/models.py`.
53 changes: 53 additions & 0 deletions tests/test_db_schema_consistency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# tests/test_db_schema_consistency.py
# Regression test for issue #1877: every table referenced anywhere in the schema
# (SQLAlchemy foreign keys and any hand-written migration SQL) must be a real table.

import glob
import os
import re

MODEL_TABLENAME_RE = re.compile(r"__tablename__\s*=\s*['\"]([^'\"]+)['\"]")
MODEL_FK_RE = re.compile(r"db\.ForeignKey\(['\"]([^'\"]+)['\"]\)")
SQL_CREATE_TABLE_RE = re.compile(r"CREATE TABLE\s+IF NOT EXISTS\s+(\w+)", re.IGNORECASE)
SQL_REFERENCES_RE = re.compile(r"REFERENCES\s+(\w+)\s*\(", re.IGNORECASE)

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


def _model_source():
path = os.path.join(ROOT, "src", "models.py")
with open(path, encoding="utf-8") as f:
return f.read()


def _model_tables():
return set(MODEL_TABLENAME_RE.findall(_model_source()))


def test_model_foreign_keys_reference_real_tables():
"""Every db.ForeignKey(...) in src/models.py must target a defined table."""
tables = _model_tables()
content = _model_source()
refs = {m.group(1).split(".")[0] for m in MODEL_FK_RE.finditer(content)}
missing = sorted(refs - tables)
assert not missing, (
"src/models.py declares foreign keys to undefined tables: "
f"{missing}"
)


def test_sql_migrations_reference_only_known_tables():
"""Migration SQL may only reference model tables or tables created by
an earlier migration in the same set — no dangling foreign keys."""
known = set(_model_tables())
sql_files = sorted(glob.glob(os.path.join(ROOT, "migrations", "*.sql")))
for path in sql_files:
with open(path, encoding="utf-8") as f:
content = f.read()
refs = set(SQL_REFERENCES_RE.findall(content))
missing = sorted(refs - known)
assert not missing, (
f"{os.path.relpath(path, ROOT)} references unknown tables: "
f"{missing}"
)
known |= set(SQL_CREATE_TABLE_RE.findall(content))
Loading