mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
a6d4e5e532
Apart from specific versioning support, our tests should focus on the behaviour of whatever version they're accompanying, regardless of when they were written. Application code should *not* do this.
388 lines
12 KiB
Ruby
388 lines
12 KiB
Ruby
require "cases/helper"
|
|
require "cases/migration/helper"
|
|
|
|
class MigratorTest < ActiveRecord::TestCase
|
|
self.use_transactional_tests = false
|
|
|
|
# Use this class to sense if migrations have gone
|
|
# up or down.
|
|
class Sensor < ActiveRecord::Migration::Current
|
|
attr_reader :went_up, :went_down
|
|
|
|
def initialize name = self.class.name, version = nil
|
|
super
|
|
@went_up = false
|
|
@went_down = false
|
|
end
|
|
|
|
def up; @went_up = true; end
|
|
def down; @went_down = true; end
|
|
end
|
|
|
|
def setup
|
|
super
|
|
ActiveRecord::SchemaMigration.create_table
|
|
ActiveRecord::SchemaMigration.delete_all rescue nil
|
|
@verbose_was = ActiveRecord::Migration.verbose
|
|
ActiveRecord::Migration.message_count = 0
|
|
ActiveRecord::Migration.class_eval do
|
|
undef :puts
|
|
def puts(*)
|
|
ActiveRecord::Migration.message_count += 1
|
|
end
|
|
end
|
|
end
|
|
|
|
teardown do
|
|
ActiveRecord::SchemaMigration.delete_all rescue nil
|
|
ActiveRecord::Migration.verbose = @verbose_was
|
|
ActiveRecord::Migration.class_eval do
|
|
undef :puts
|
|
def puts(*)
|
|
super
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_migrator_with_duplicate_names
|
|
assert_raises(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
|
|
list = [ActiveRecord::Migration.new('Chunky'), ActiveRecord::Migration.new('Chunky')]
|
|
ActiveRecord::Migrator.new(:up, list)
|
|
end
|
|
end
|
|
|
|
def test_migrator_with_duplicate_versions
|
|
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
|
|
list = [ActiveRecord::Migration.new('Foo', 1), ActiveRecord::Migration.new('Bar', 1)]
|
|
ActiveRecord::Migrator.new(:up, list)
|
|
end
|
|
end
|
|
|
|
def test_migrator_with_missing_version_numbers
|
|
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
|
|
list = [ActiveRecord::Migration.new('Foo', 1), ActiveRecord::Migration.new('Bar', 2)]
|
|
ActiveRecord::Migrator.new(:up, list, 3).run
|
|
end
|
|
end
|
|
|
|
def test_finds_migrations
|
|
migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid")
|
|
|
|
[[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
|
|
assert_equal migrations[i].version, pair.first
|
|
assert_equal migrations[i].name, pair.last
|
|
end
|
|
end
|
|
|
|
def test_finds_migrations_in_subdirectories
|
|
migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid_with_subdirectories")
|
|
|
|
[[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
|
|
assert_equal migrations[i].version, pair.first
|
|
assert_equal migrations[i].name, pair.last
|
|
end
|
|
end
|
|
|
|
def test_finds_migrations_from_two_directories
|
|
directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps']
|
|
migrations = ActiveRecord::Migrator.migrations directories
|
|
|
|
[[20090101010101, "PeopleHaveHobbies"],
|
|
[20090101010202, "PeopleHaveDescriptions"],
|
|
[20100101010101, "ValidWithTimestampsPeopleHaveLastNames"],
|
|
[20100201010101, "ValidWithTimestampsWeNeedReminders"],
|
|
[20100301010101, "ValidWithTimestampsInnocentJointable"]].each_with_index do |pair, i|
|
|
assert_equal pair.first, migrations[i].version
|
|
assert_equal pair.last, migrations[i].name
|
|
end
|
|
end
|
|
|
|
def test_finds_migrations_in_numbered_directory
|
|
migrations = ActiveRecord::Migrator.migrations [MIGRATIONS_ROOT + '/10_urban']
|
|
assert_equal 9, migrations[0].version
|
|
assert_equal 'AddExpressions', migrations[0].name
|
|
end
|
|
|
|
def test_relative_migrations
|
|
list = Dir.chdir(MIGRATIONS_ROOT) do
|
|
ActiveRecord::Migrator.migrations("valid")
|
|
end
|
|
|
|
migration_proxy = list.find { |item|
|
|
item.name == 'ValidPeopleHaveLastNames'
|
|
}
|
|
assert migration_proxy, 'should find pending migration'
|
|
end
|
|
|
|
def test_finds_pending_migrations
|
|
ActiveRecord::SchemaMigration.create!(:version => '1')
|
|
migration_list = [ActiveRecord::Migration.new('foo', 1), ActiveRecord::Migration.new('bar', 3)]
|
|
migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations
|
|
|
|
assert_equal 1, migrations.size
|
|
assert_equal migration_list.last, migrations.first
|
|
end
|
|
|
|
def test_migrator_interleaved_migrations
|
|
pass_one = [Sensor.new('One', 1)]
|
|
|
|
ActiveRecord::Migrator.new(:up, pass_one).migrate
|
|
assert pass_one.first.went_up
|
|
assert_not pass_one.first.went_down
|
|
|
|
pass_two = [Sensor.new('One', 1), Sensor.new('Three', 3)]
|
|
ActiveRecord::Migrator.new(:up, pass_two).migrate
|
|
assert_not pass_two[0].went_up
|
|
assert pass_two[1].went_up
|
|
assert pass_two.all? { |x| !x.went_down }
|
|
|
|
pass_three = [Sensor.new('One', 1),
|
|
Sensor.new('Two', 2),
|
|
Sensor.new('Three', 3)]
|
|
|
|
ActiveRecord::Migrator.new(:down, pass_three).migrate
|
|
assert pass_three[0].went_down
|
|
assert_not pass_three[1].went_down
|
|
assert pass_three[2].went_down
|
|
end
|
|
|
|
def test_up_calls_up
|
|
migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
|
|
ActiveRecord::Migrator.new(:up, migrations).migrate
|
|
assert migrations.all?(&:went_up)
|
|
assert migrations.all? { |m| !m.went_down }
|
|
assert_equal 2, ActiveRecord::Migrator.current_version
|
|
end
|
|
|
|
def test_down_calls_down
|
|
test_up_calls_up
|
|
|
|
migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
|
|
ActiveRecord::Migrator.new(:down, migrations).migrate
|
|
assert migrations.all? { |m| !m.went_up }
|
|
assert migrations.all?(&:went_down)
|
|
assert_equal 0, ActiveRecord::Migrator.current_version
|
|
end
|
|
|
|
def test_current_version
|
|
ActiveRecord::SchemaMigration.create!(:version => '1000')
|
|
assert_equal 1000, ActiveRecord::Migrator.current_version
|
|
end
|
|
|
|
def test_migrator_one_up
|
|
calls, migrations = sensors(3)
|
|
|
|
ActiveRecord::Migrator.new(:up, migrations, 1).migrate
|
|
assert_equal [[:up, 1]], calls
|
|
calls.clear
|
|
|
|
ActiveRecord::Migrator.new(:up, migrations, 2).migrate
|
|
assert_equal [[:up, 2]], calls
|
|
end
|
|
|
|
def test_migrator_one_down
|
|
calls, migrations = sensors(3)
|
|
|
|
ActiveRecord::Migrator.new(:up, migrations).migrate
|
|
assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls
|
|
calls.clear
|
|
|
|
ActiveRecord::Migrator.new(:down, migrations, 1).migrate
|
|
|
|
assert_equal [[:down, 3], [:down, 2]], calls
|
|
end
|
|
|
|
def test_migrator_one_up_one_down
|
|
calls, migrations = sensors(3)
|
|
|
|
ActiveRecord::Migrator.new(:up, migrations, 1).migrate
|
|
assert_equal [[:up, 1]], calls
|
|
calls.clear
|
|
|
|
ActiveRecord::Migrator.new(:down, migrations, 0).migrate
|
|
assert_equal [[:down, 1]], calls
|
|
end
|
|
|
|
def test_migrator_double_up
|
|
calls, migrations = sensors(3)
|
|
assert_equal(0, ActiveRecord::Migrator.current_version)
|
|
|
|
ActiveRecord::Migrator.new(:up, migrations, 1).migrate
|
|
assert_equal [[:up, 1]], calls
|
|
calls.clear
|
|
|
|
ActiveRecord::Migrator.new(:up, migrations, 1).migrate
|
|
assert_equal [], calls
|
|
end
|
|
|
|
def test_migrator_double_down
|
|
calls, migrations = sensors(3)
|
|
|
|
assert_equal(0, ActiveRecord::Migrator.current_version)
|
|
|
|
ActiveRecord::Migrator.new(:up, migrations, 1).run
|
|
assert_equal [[:up, 1]], calls
|
|
calls.clear
|
|
|
|
ActiveRecord::Migrator.new(:down, migrations, 1).run
|
|
assert_equal [[:down, 1]], calls
|
|
calls.clear
|
|
|
|
ActiveRecord::Migrator.new(:down, migrations, 1).run
|
|
assert_equal [], calls
|
|
|
|
assert_equal(0, ActiveRecord::Migrator.current_version)
|
|
end
|
|
|
|
def test_migrator_verbosity
|
|
_, migrations = sensors(3)
|
|
|
|
ActiveRecord::Migrator.new(:up, migrations, 1).migrate
|
|
assert_not_equal 0, ActiveRecord::Migration.message_count
|
|
|
|
ActiveRecord::Migration.message_count = 0
|
|
|
|
ActiveRecord::Migrator.new(:down, migrations, 0).migrate
|
|
assert_not_equal 0, ActiveRecord::Migration.message_count
|
|
end
|
|
|
|
def test_migrator_verbosity_off
|
|
_, migrations = sensors(3)
|
|
|
|
ActiveRecord::Migration.message_count = 0
|
|
ActiveRecord::Migration.verbose = false
|
|
ActiveRecord::Migrator.new(:up, migrations, 1).migrate
|
|
assert_equal 0, ActiveRecord::Migration.message_count
|
|
ActiveRecord::Migrator.new(:down, migrations, 0).migrate
|
|
assert_equal 0, ActiveRecord::Migration.message_count
|
|
end
|
|
|
|
def test_target_version_zero_should_run_only_once
|
|
calls, migrations = sensors(3)
|
|
|
|
# migrate up to 1
|
|
ActiveRecord::Migrator.new(:up, migrations, 1).migrate
|
|
assert_equal [[:up, 1]], calls
|
|
calls.clear
|
|
|
|
# migrate down to 0
|
|
ActiveRecord::Migrator.new(:down, migrations, 0).migrate
|
|
assert_equal [[:down, 1]], calls
|
|
calls.clear
|
|
|
|
# migrate down to 0 again
|
|
ActiveRecord::Migrator.new(:down, migrations, 0).migrate
|
|
assert_equal [], calls
|
|
end
|
|
|
|
def test_migrator_going_down_due_to_version_target
|
|
calls, migrator = migrator_class(3)
|
|
|
|
migrator.up("valid", 1)
|
|
assert_equal [[:up, 1]], calls
|
|
calls.clear
|
|
|
|
migrator.migrate("valid", 0)
|
|
assert_equal [[:down, 1]], calls
|
|
calls.clear
|
|
|
|
migrator.migrate("valid")
|
|
assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls
|
|
end
|
|
|
|
def test_migrator_rollback
|
|
_, migrator = migrator_class(3)
|
|
|
|
migrator.migrate("valid")
|
|
assert_equal(3, ActiveRecord::Migrator.current_version)
|
|
|
|
migrator.rollback("valid")
|
|
assert_equal(2, ActiveRecord::Migrator.current_version)
|
|
|
|
migrator.rollback("valid")
|
|
assert_equal(1, ActiveRecord::Migrator.current_version)
|
|
|
|
migrator.rollback("valid")
|
|
assert_equal(0, ActiveRecord::Migrator.current_version)
|
|
|
|
migrator.rollback("valid")
|
|
assert_equal(0, ActiveRecord::Migrator.current_version)
|
|
end
|
|
|
|
def test_migrator_db_has_no_schema_migrations_table
|
|
_, migrator = migrator_class(3)
|
|
|
|
ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
|
|
ActiveSupport::Deprecation.silence { assert_not ActiveRecord::Base.connection.table_exists?('schema_migrations') }
|
|
migrator.migrate("valid", 1)
|
|
ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?('schema_migrations') }
|
|
end
|
|
|
|
def test_migrator_forward
|
|
_, migrator = migrator_class(3)
|
|
migrator.migrate("/valid", 1)
|
|
assert_equal(1, ActiveRecord::Migrator.current_version)
|
|
|
|
migrator.forward("/valid", 2)
|
|
assert_equal(3, ActiveRecord::Migrator.current_version)
|
|
|
|
migrator.forward("/valid")
|
|
assert_equal(3, ActiveRecord::Migrator.current_version)
|
|
end
|
|
|
|
def test_only_loads_pending_migrations
|
|
# migrate up to 1
|
|
ActiveRecord::SchemaMigration.create!(:version => '1')
|
|
|
|
calls, migrator = migrator_class(3)
|
|
migrator.migrate("valid", nil)
|
|
|
|
assert_equal [[:up, 2], [:up, 3]], calls
|
|
end
|
|
|
|
def test_get_all_versions
|
|
_, migrator = migrator_class(3)
|
|
|
|
migrator.migrate("valid")
|
|
assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
|
|
|
|
migrator.rollback("valid")
|
|
assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
|
|
|
|
migrator.rollback("valid")
|
|
assert_equal([1], ActiveRecord::Migrator.get_all_versions)
|
|
|
|
migrator.rollback("valid")
|
|
assert_equal([], ActiveRecord::Migrator.get_all_versions)
|
|
end
|
|
|
|
private
|
|
def m(name, version)
|
|
x = Sensor.new name, version
|
|
x.extend(Module.new {
|
|
define_method(:up) { yield(:up, x); super() }
|
|
define_method(:down) { yield(:down, x); super() }
|
|
}) if block_given?
|
|
end
|
|
|
|
def sensors(count)
|
|
calls = []
|
|
migrations = count.times.map { |i|
|
|
m(nil, i + 1) { |c,migration|
|
|
calls << [c, migration.version]
|
|
}
|
|
}
|
|
[calls, migrations]
|
|
end
|
|
|
|
def migrator_class(count)
|
|
calls, migrations = sensors(count)
|
|
|
|
migrator = Class.new(ActiveRecord::Migrator).extend(Module.new {
|
|
define_method(:migrations) { |paths|
|
|
migrations
|
|
}
|
|
})
|
|
[calls, migrator]
|
|
end
|
|
end
|