mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-07-24 18:04:32 +02:00
migrate --stop-before an already-applied migration is now an error
The CLI validated --stop-before names against both pending and applied migrations, but Migrations.apply() only looked for the stop name among pending ones - naming an applied migration passed validation and then silently applied every migration after it, the exact outcome the option exists to prevent. apply() now raises ValueError before applying anything if a stop_before name matches an applied migration in its set; names not in the set are still ignored, since unqualified CLI values are offered to every set. The migrate command reports it as a clean error. Refs https://github.com/simonw/sqlite-utils/issues/769#issuecomment-4900034150 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3de8507c6b
commit
b3aa3f47b7
6 changed files with 79 additions and 3 deletions
|
|
@ -463,3 +463,25 @@ def test_list_does_not_upgrade_legacy_migrations_table(two_migrations):
|
|||
db2 = sqlite_utils.Database(db_path)
|
||||
assert db2["_sqlite_migrations"].pks == ["migration_set", "name"]
|
||||
db2.close()
|
||||
|
||||
|
||||
def test_stop_before_applied_migration_errors(two_migrations):
|
||||
path, _ = two_migrations
|
||||
db_path = str(path / "test.db")
|
||||
migrations_path = str(path / "foo" / "migrations.py")
|
||||
# Apply everything first
|
||||
first = CliRunner().invoke(
|
||||
sqlite_utils.cli.cli,
|
||||
["migrate", db_path, migrations_path, "--stop-before", "bar"],
|
||||
)
|
||||
assert first.exit_code == 0
|
||||
# foo is now applied - stopping before it is an error, and bar
|
||||
# must not be applied as a side effect
|
||||
result = CliRunner().invoke(
|
||||
sqlite_utils.cli.cli,
|
||||
["migrate", db_path, migrations_path, "--stop-before", "foo"],
|
||||
)
|
||||
assert result.exit_code != 0
|
||||
assert "already been applied" in result.output
|
||||
db = sqlite_utils.Database(db_path)
|
||||
assert not db["bar"].exists()
|
||||
|
|
|
|||
|
|
@ -214,3 +214,33 @@ def test_duplicate_migration_name_errors():
|
|||
pass
|
||||
|
||||
assert "m001" in str(ex.value)
|
||||
|
||||
|
||||
def test_stop_before_applied_migration_errors(migrations):
|
||||
# Stopping before a migration that has already been applied is
|
||||
# impossible to honor - previously the stop name was only checked
|
||||
# against pending migrations, so everything after it was applied
|
||||
db = sqlite_utils.Database(memory=True)
|
||||
migrations.apply(db, stop_before="m002") # applies m001 only
|
||||
with pytest.raises(ValueError) as ex:
|
||||
migrations.apply(db, stop_before="m001")
|
||||
assert "m001" in str(ex.value)
|
||||
assert "already been applied" in str(ex.value)
|
||||
# Nothing else was applied
|
||||
assert not db["cats"].exists()
|
||||
|
||||
|
||||
def test_stop_before_applied_migration_errors_before_any_apply(migrations):
|
||||
# The error fires before any pending migration runs, even those that
|
||||
# come before the already-applied stop target in registration order
|
||||
db = sqlite_utils.Database(memory=True)
|
||||
only_second = Migrations("test")
|
||||
|
||||
@only_second()
|
||||
def m002(db):
|
||||
db["cats"].create({"name": str})
|
||||
|
||||
only_second.apply(db) # m002 applied, m001 still pending
|
||||
with pytest.raises(ValueError):
|
||||
migrations.apply(db, stop_before="m002")
|
||||
assert not db["dogs"].exists()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue