refactor(db_migrate): extract _apply_migrations_until helper; add edge-case tests
Issue 1 (refactor): run() and migrate_to_version() duplicated the inner loop body — every change to the migration loop had to be applied in both places. Pull the loop into _apply_migrations_until(engine, target_version): when target_version is None, apply all pending; otherwise stop when the file's version exceeds target. run() and migrate_to_version() are now thin wrappers around the helper. Issue 2 (tests): migrate_to_version() had no direct tests for its edge-case behavior. Add 4 tests: - idempotent: calling migrate_to_version(engine, N) twice == once - lower-than-current: migrate_to_version(engine, 3) after N=13 is a no-op - zero on fresh DB: migrate_to_version(engine, 0) is a no-op - beyond-max: migrate_to_version(engine, 999) applies every migration Tests use a fresh sa.Engine (not db.engine()) so they exercise the target-version behavior from a clean user_version=0 starting point — the conftest autouse fixture has already brought the module engine to the latest version, which would mask the no-op branches.
This commit is contained in:
@@ -587,4 +587,70 @@ def test_migration_0014_preserves_joint_fk_rows(tmp_path, monkeypatch):
|
||||
assert rows == [("CLM-J", "B-JOIN", "CLP-J", "B-JOIN-R")], (
|
||||
"matches batch_id + remittance_batch_id must be populated by 0014; "
|
||||
"got " + repr(rows)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# migrate_to_version: edge-case behavior
|
||||
# =============================================================================
|
||||
# These tests use a fresh ``sa.Engine`` (not ``db.engine()``) because the
|
||||
# autouse conftest fixture has already initialized the module engine at the
|
||||
# latest version. We need a clean engine that starts at ``user_version=0``
|
||||
# so that ``migrate_to_version(N)`` actually exercises the apply / no-op /
|
||||
# beyond-max branches.
|
||||
|
||||
|
||||
def test_migrate_to_version_idempotent(tmp_path, monkeypatch):
|
||||
"""Calling migrate_to_version(engine, N) twice is equivalent to calling once."""
|
||||
from cyclone.db_migrate import migrate_to_version
|
||||
|
||||
engine = _fresh_engine(tmp_path)
|
||||
migrate_to_version(engine, 5)
|
||||
with engine.begin() as conn:
|
||||
v1 = conn.exec_driver_sql("PRAGMA user_version").scalar()
|
||||
migrate_to_version(engine, 5)
|
||||
with engine.begin() as conn:
|
||||
v2 = conn.exec_driver_sql("PRAGMA user_version").scalar()
|
||||
assert v1 == v2 == 5
|
||||
|
||||
|
||||
def test_migrate_to_version_lower_than_current_is_noop(tmp_path, monkeypatch):
|
||||
"""migrate_to_version(engine, 3) after migrate_to_version(engine, 13) is a no-op."""
|
||||
from cyclone.db_migrate import migrate_to_version
|
||||
|
||||
engine = _fresh_engine(tmp_path)
|
||||
migrate_to_version(engine, 13)
|
||||
with engine.begin() as conn:
|
||||
v_after_13 = conn.exec_driver_sql("PRAGMA user_version").scalar()
|
||||
migrate_to_version(engine, 3) # lower, no-op
|
||||
with engine.begin() as conn:
|
||||
v_after_3 = conn.exec_driver_sql("PRAGMA user_version").scalar()
|
||||
assert v_after_13 == v_after_3 == 13
|
||||
|
||||
|
||||
def test_migrate_to_version_zero_on_fresh_db(tmp_path, monkeypatch):
|
||||
"""migrate_to_version(engine, 0) on a fresh DB is a no-op."""
|
||||
from cyclone.db_migrate import migrate_to_version
|
||||
|
||||
engine = _fresh_engine(tmp_path)
|
||||
migrate_to_version(engine, 0)
|
||||
with engine.begin() as conn:
|
||||
v = conn.exec_driver_sql("PRAGMA user_version").scalar()
|
||||
assert v == 0
|
||||
|
||||
|
||||
def test_migrate_to_version_beyond_max_applies_all(tmp_path, monkeypatch):
|
||||
"""migrate_to_version(engine, 999) applies every migration (equivalent to run)."""
|
||||
import re
|
||||
from cyclone.db_migrate import MIGRATIONS_DIR, migrate_to_version
|
||||
|
||||
engine = _fresh_engine(tmp_path)
|
||||
migrate_to_version(engine, 999)
|
||||
with engine.begin() as conn:
|
||||
v = conn.exec_driver_sql("PRAGMA user_version").scalar()
|
||||
# Should equal the highest version present in the migrations directory.
|
||||
max_version = max(
|
||||
int(re.match(r"^(\d+)", p.name).group(1))
|
||||
for p in MIGRATIONS_DIR.glob("*.sql")
|
||||
)
|
||||
assert v == max_version
|
||||
Reference in New Issue
Block a user