Skip to content

Commit 5a09d32

Browse files
Add regression test for copying migrations at timestamp boundary
We'd like to add validation to migration timestamps (attempted in 2854e37), but this is problematic for copied migrations, e.g. engine migrations: migrations are copied by adding one to every migration timestamp number, which may produce an invalid timestamp. This commit adds a regression test for this case, ensuring that if / when we revisit migration timestamp validation, we handle the case for copied migrations.
1 parent 2854e37 commit 5a09d32

File tree

1 file changed

+71
-0
lines changed

1 file changed

+71
-0
lines changed

activerecord/test/cases/migration_test.rb

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,4 +1801,75 @@ def test_check_pending_with_stdlib_logger
18011801
def test_unknown_migration_version_should_raise_an_argument_error
18021802
assert_raise(ArgumentError) { ActiveRecord::Migration[1.0] }
18031803
end
1804+
1805+
class MigrationValidationTest < ActiveRecord::TestCase
1806+
def setup
1807+
@verbose_was, ActiveRecord::Migration.verbose = ActiveRecord::Migration.verbose, false
1808+
@schema_migration = ActiveRecord::Base.connection.schema_migration
1809+
@internal_metadata = ActiveRecord::Base.connection.internal_metadata
1810+
ActiveRecord::Base.connection.schema_cache.clear!
1811+
1812+
@migrations_path = MIGRATIONS_ROOT + "/temp"
1813+
@migrator = ActiveRecord::MigrationContext.new(@migrations_path, @schema_migration, @internal_metadata)
1814+
end
1815+
1816+
def teardown
1817+
@schema_migration.create_table
1818+
@schema_migration.delete_all_versions
1819+
ActiveRecord::Migration.verbose = @verbose_was
1820+
end
1821+
1822+
def test_copied_migrations_at_timestamp_boundary_are_valid
1823+
migrations_path_source = MIGRATIONS_ROOT + "/temp_source"
1824+
migrations_path_dest = MIGRATIONS_ROOT + "/temp_dest"
1825+
migrations = ["20180101010101_test_migration.rb", "20180101010102_test_migration_two.rb", "20180101010103_test_migration_three.rb"]
1826+
1827+
with_temp_migration_files(migrations_path_source, migrations) do
1828+
travel_to(Time.utc(2023, 12, 1, 10, 10, 59)) do
1829+
ActiveRecord::Migration.copy(migrations_path_dest, temp: migrations_path_source)
1830+
1831+
assert File.exist?(migrations_path_dest + "/20231201101059_test_migration.temp.rb")
1832+
assert File.exist?(migrations_path_dest + "/20231201101060_test_migration_two.temp.rb")
1833+
assert File.exist?(migrations_path_dest + "/20231201101061_test_migration_three.temp.rb")
1834+
1835+
migrator = ActiveRecord::MigrationContext.new(migrations_path_dest, @schema_migration, @internal_metadata)
1836+
migrator.up(20231201101059)
1837+
migrator.up(20231201101060)
1838+
migrator.up(20231201101061)
1839+
1840+
assert_equal 20231201101061, migrator.current_version
1841+
assert_not migrator.needs_migration?
1842+
end
1843+
end
1844+
ensure
1845+
File.delete(*Dir[migrations_path_dest + "/*.rb"])
1846+
Dir.rmdir(migrations_path_dest) if Dir.exist?(migrations_path_dest)
1847+
end
1848+
1849+
private
1850+
def with_temp_migration_files(migrations_dir, filenames)
1851+
Dir.mkdir(migrations_dir) unless Dir.exist?(migrations_dir)
1852+
1853+
paths = []
1854+
filenames.each do |filename|
1855+
path = File.join(migrations_dir, filename)
1856+
paths << path
1857+
1858+
migration_class = filename.match(ActiveRecord::Migration::MigrationFilenameRegexp)[2].camelize
1859+
1860+
File.open(path, "w+") do |file|
1861+
file << <<~MIGRATION
1862+
class #{migration_class} < ActiveRecord::Migration::Current
1863+
def change; end
1864+
end
1865+
MIGRATION
1866+
end
1867+
end
1868+
1869+
yield
1870+
ensure
1871+
paths.each { |path| File.delete(path) if File.exist?(path) }
1872+
Dir.rmdir(migrations_dir) if Dir.exist?(migrations_dir)
1873+
end
1874+
end
18041875
end

0 commit comments

Comments
 (0)