2017-07-09 17:41:28 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-08-06 16:26:20 +00:00
|
|
|
require "cases/helper"
|
|
|
|
require "cases/migration/helper"
|
|
|
|
require "bigdecimal/util"
|
|
|
|
require "concurrent/atomic/count_down_latch"
|
2006-07-08 20:35:56 +00:00
|
|
|
|
2016-08-06 16:26:20 +00:00
|
|
|
require "models/person"
|
|
|
|
require "models/topic"
|
|
|
|
require "models/developer"
|
|
|
|
require "models/computer"
|
2008-01-21 17:20:51 +00:00
|
|
|
|
|
|
|
require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
|
2011-11-26 14:23:20 +00:00
|
|
|
require MIGRATIONS_ROOT + "/rename/1_we_need_things"
|
|
|
|
require MIGRATIONS_ROOT + "/rename/2_rename_things"
|
2008-01-21 17:20:51 +00:00
|
|
|
require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2014-05-24 12:30:26 +00:00
|
|
|
class BigNumber < ActiveRecord::Base
|
2014-05-27 18:12:44 +00:00
|
|
|
unless current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter)
|
2015-02-06 18:05:38 +00:00
|
|
|
attribute :value_of_e, :integer
|
2014-05-27 18:12:44 +00:00
|
|
|
end
|
2015-02-06 18:05:38 +00:00
|
|
|
attribute :my_house_population, :integer
|
2014-05-24 12:30:26 +00:00
|
|
|
end
|
2006-07-08 20:35:56 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
class Reminder < ActiveRecord::Base; end
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
class Thing < ActiveRecord::Base; end
|
2011-11-26 14:23:20 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
class MigrationTest < ActiveRecord::TestCase
|
2015-03-11 02:21:19 +00:00
|
|
|
self.use_transactional_tests = false
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
fixtures :people
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def setup
|
|
|
|
super
|
2013-01-06 23:23:15 +00:00
|
|
|
%w(reminders people_reminders prefix_reminders_suffix p_things_s).each do |table|
|
2012-01-16 19:01:58 +00:00
|
|
|
Reminder.connection.drop_table(table) rescue nil
|
|
|
|
end
|
|
|
|
Reminder.reset_column_information
|
2014-08-29 01:06:05 +00:00
|
|
|
@verbose_was, ActiveRecord::Migration.verbose = ActiveRecord::Migration.verbose, false
|
2019-05-30 12:25:05 +00:00
|
|
|
@schema_migration = ActiveRecord::Base.connection.schema_migration
|
2013-01-18 10:26:01 +00:00
|
|
|
ActiveRecord::Base.connection.schema_cache.clear!
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-11-26 14:23:20 +00:00
|
|
|
|
2014-03-14 04:35:58 +00:00
|
|
|
teardown do
|
2013-01-09 06:05:10 +00:00
|
|
|
ActiveRecord::Base.table_name_prefix = ""
|
|
|
|
ActiveRecord::Base.table_name_suffix = ""
|
|
|
|
|
2017-01-19 15:21:30 +00:00
|
|
|
ActiveRecord::SchemaMigration.create_table
|
|
|
|
ActiveRecord::SchemaMigration.delete_all
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2017-01-19 15:21:30 +00:00
|
|
|
%w(things awesome_things prefix_things_suffix p_awesome_things_s).each do |table|
|
2012-01-10 22:48:51 +00:00
|
|
|
Thing.connection.drop_table(table) rescue nil
|
2005-07-04 18:51:02 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
Thing.reset_column_information
|
2005-12-20 21:43:47 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
%w(reminders people_reminders prefix_reminders_suffix).each do |table|
|
|
|
|
Reminder.connection.drop_table(table) rescue nil
|
|
|
|
end
|
2013-01-09 06:05:10 +00:00
|
|
|
Reminder.reset_table_name
|
2012-01-10 22:48:51 +00:00
|
|
|
Reminder.reset_column_information
|
2006-07-07 10:48:43 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
%w(last_name key bio age height wealth birthday favorite_day
|
|
|
|
moment_of_truth male administrator funny).each do |column|
|
2016-08-06 16:26:20 +00:00
|
|
|
Person.connection.remove_column("people", column) rescue nil
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
|
|
|
Person.connection.remove_column("people", "first_name") rescue nil
|
|
|
|
Person.connection.remove_column("people", "middle_name") rescue nil
|
2014-07-22 07:05:33 +00:00
|
|
|
Person.connection.add_column("people", "first_name", :string)
|
2012-01-10 22:48:51 +00:00
|
|
|
Person.reset_column_information
|
2014-08-29 00:27:26 +00:00
|
|
|
|
|
|
|
ActiveRecord::Migration.verbose = @verbose_was
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2005-07-05 07:19:20 +00:00
|
|
|
|
2018-12-28 00:21:09 +00:00
|
|
|
def test_passing_migrations_paths_to_assume_migrated_upto_version_is_deprecated
|
|
|
|
ActiveRecord::SchemaMigration.create_table
|
|
|
|
assert_deprecated do
|
|
|
|
ActiveRecord::Base.connection.assume_migrated_upto_version(0, [])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-30 03:15:30 +00:00
|
|
|
def test_migration_version_matches_component_version
|
|
|
|
assert_equal ActiveRecord::VERSION::STRING.to_f, ActiveRecord::Migration.current_version
|
|
|
|
end
|
|
|
|
|
2012-06-03 20:07:11 +00:00
|
|
|
def test_migrator_versions
|
|
|
|
migrations_path = MIGRATIONS_ROOT + "/valid"
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
|
2012-06-03 20:07:11 +00:00
|
|
|
|
2018-01-10 15:25:13 +00:00
|
|
|
migrator.up
|
|
|
|
assert_equal 3, migrator.current_version
|
|
|
|
assert_equal false, migrator.needs_migration?
|
2012-06-03 20:07:11 +00:00
|
|
|
|
2018-01-10 15:25:13 +00:00
|
|
|
migrator.down
|
|
|
|
assert_equal 0, migrator.current_version
|
|
|
|
assert_equal true, migrator.needs_migration?
|
2014-07-30 19:10:28 +00:00
|
|
|
|
2015-09-09 14:30:48 +00:00
|
|
|
ActiveRecord::SchemaMigration.create!(version: 3)
|
2018-01-10 15:25:13 +00:00
|
|
|
assert_equal true, migrator.needs_migration?
|
2014-07-30 19:10:28 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_migration_detection_without_schema_migration_table
|
2016-08-06 16:26:20 +00:00
|
|
|
ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
|
2014-07-30 19:10:28 +00:00
|
|
|
|
|
|
|
migrations_path = MIGRATIONS_ROOT + "/valid"
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
|
2014-07-30 19:10:28 +00:00
|
|
|
|
2018-01-10 15:25:13 +00:00
|
|
|
assert_equal true, migrator.needs_migration?
|
2012-06-03 20:07:11 +00:00
|
|
|
end
|
|
|
|
|
2014-10-31 21:20:44 +00:00
|
|
|
def test_any_migrations
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid", @schema_migration)
|
2014-10-31 21:20:44 +00:00
|
|
|
|
2018-01-25 23:14:09 +00:00
|
|
|
assert_predicate migrator, :any_migrations?
|
2014-10-31 21:20:44 +00:00
|
|
|
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator_empty = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/empty", @schema_migration)
|
2014-10-31 21:20:44 +00:00
|
|
|
|
2018-01-25 23:14:09 +00:00
|
|
|
assert_not_predicate migrator_empty, :any_migrations?
|
2014-10-31 21:20:44 +00:00
|
|
|
end
|
|
|
|
|
2013-12-17 21:02:52 +00:00
|
|
|
def test_migration_version
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/version_check", @schema_migration)
|
2018-01-10 15:25:13 +00:00
|
|
|
assert_equal 0, migrator.current_version
|
|
|
|
migrator.up(20131219224947)
|
|
|
|
assert_equal 20131219224947, migrator.current_version
|
2013-12-17 21:02:52 +00:00
|
|
|
end
|
|
|
|
|
2017-12-09 16:02:51 +00:00
|
|
|
def test_create_table_raises_if_already_exists
|
|
|
|
connection = Person.connection
|
|
|
|
connection.create_table :testings, force: true do |t|
|
|
|
|
t.string :foo
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_raise(ActiveRecord::StatementInvalid) do
|
|
|
|
connection.create_table :testings do |t|
|
|
|
|
t.string :foo
|
|
|
|
end
|
|
|
|
end
|
|
|
|
ensure
|
|
|
|
connection.drop_table :testings, if_exists: true
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_create_table_with_if_not_exists_true
|
|
|
|
connection = Person.connection
|
|
|
|
connection.create_table :testings, force: true do |t|
|
|
|
|
t.string :foo
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_nothing_raised do
|
|
|
|
connection.create_table :testings, if_not_exists: true do |t|
|
|
|
|
t.string :foo
|
|
|
|
end
|
|
|
|
end
|
|
|
|
ensure
|
|
|
|
connection.drop_table :testings, if_exists: true
|
|
|
|
end
|
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_create_table_with_force_true_does_not_drop_nonexisting_table
|
|
|
|
# using a copy as we need the drop_table method to
|
|
|
|
# continue to work for the ensure block of the test
|
|
|
|
temp_conn = Person.connection.dup
|
2012-01-16 18:03:33 +00:00
|
|
|
|
|
|
|
assert_not_equal temp_conn, Person.connection
|
|
|
|
|
2016-08-06 17:37:57 +00:00
|
|
|
temp_conn.create_table :testings2, force: true do |t|
|
2012-01-10 22:48:51 +00:00
|
|
|
t.column :foo, :string
|
2010-05-16 16:50:25 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
ensure
|
2015-02-12 11:20:03 +00:00
|
|
|
Person.connection.drop_table :testings2, if_exists: true
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2010-05-16 16:50:25 +00:00
|
|
|
|
2012-01-16 18:14:09 +00:00
|
|
|
def test_migration_instance_has_connection
|
2015-12-05 20:14:22 +00:00
|
|
|
migration = Class.new(ActiveRecord::Migration::Current).new
|
2015-08-23 13:59:24 +00:00
|
|
|
assert_equal ActiveRecord::Base.connection, migration.connection
|
2012-01-16 18:14:09 +00:00
|
|
|
end
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2012-01-16 18:14:09 +00:00
|
|
|
def test_method_missing_delegates_to_connection
|
2015-12-05 20:14:22 +00:00
|
|
|
migration = Class.new(ActiveRecord::Migration::Current) {
|
2012-01-16 18:14:09 +00:00
|
|
|
def connection
|
|
|
|
Class.new {
|
|
|
|
def create_table; "hi mom!"; end
|
|
|
|
}.new
|
|
|
|
end
|
|
|
|
}.new
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2012-01-16 18:14:09 +00:00
|
|
|
assert_equal "hi mom!", migration.method_missing(:create_table)
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_add_table_with_decimals
|
|
|
|
Person.connection.drop_table :big_numbers rescue nil
|
|
|
|
|
2018-01-25 23:14:09 +00:00
|
|
|
assert_not_predicate BigNumber, :table_exists?
|
2012-01-10 22:48:51 +00:00
|
|
|
GiveMeBigNumbers.up
|
2019-06-11 10:57:38 +00:00
|
|
|
assert_predicate BigNumber, :table_exists?
|
Attribute assignment and type casting has nothing to do with columns
It's finally finished!!!!!!! The reason the Attributes API was kept
private in 4.2 was due to some publicly visible implementation details.
It was previously implemented by overloading `columns` and
`columns_hash`, to make them return column objects which were modified
with the attribute information.
This meant that those methods LIED! We didn't change the database
schema. We changed the attribute information on the class. That is
wrong! It should be the other way around, where schema loading just
calls the attributes API for you. And now it does!
Yes, this means that there is nothing that happens in automatic schema
loading that you couldn't manually do yourself. (There's still some
funky cases where we hit the connection adapter that I need to handle,
before we can turn off automatic schema detection entirely.)
There were a few weird test failures caused by this that had to be
fixed. The main source came from the fact that the attribute methods are
now defined in terms of `attribute_names`, which has a clause like
`return [] unless table_exists?`. I don't *think* this is an issue,
since the only place this caused failures were in a fake adapter which
didn't override `table_exists?`.
Additionally, there were a few cases where tests were failing because a
migration was run, but the model was not reloaded. I'm not sure why
these started failing from this change, I might need to clear an
additional cache in `reload_schema_from_cache`. Again, since this is not
normal usage, and it's expected that `reset_column_information` will be
called after the table is modified, I don't think it's a problem.
Still, test failures that were unrelated to the change are worrying, and
I need to dig into them further.
Finally, I spent a lot of time debugging issues with the mutex used in
`define_attribute_methods`. I think we can just remove that method
entirely, and define the attribute methods *manually* in the call to
`define_attribute`, which would simplify the code *tremendously*.
Ok. now to make this damn thing public, and work on moving it up to
Active Model.
2015-01-30 21:03:36 +00:00
|
|
|
BigNumber.reset_column_information
|
2012-01-10 22:48:51 +00:00
|
|
|
|
|
|
|
assert BigNumber.create(
|
2016-08-06 17:37:57 +00:00
|
|
|
bank_balance: 1586.43,
|
|
|
|
big_bank_balance: BigDecimal("1000234000567.95"),
|
2018-03-03 19:25:29 +00:00
|
|
|
world_population: 2**62,
|
2016-08-06 17:37:57 +00:00
|
|
|
my_house_population: 3,
|
|
|
|
value_of_e: BigDecimal("2.7182818284590452353602875")
|
2012-01-10 22:48:51 +00:00
|
|
|
)
|
|
|
|
|
2012-04-26 17:32:55 +00:00
|
|
|
b = BigNumber.first
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_not_nil b
|
|
|
|
|
|
|
|
assert_not_nil b.bank_balance
|
|
|
|
assert_not_nil b.big_bank_balance
|
|
|
|
assert_not_nil b.world_population
|
|
|
|
assert_not_nil b.my_house_population
|
|
|
|
assert_not_nil b.value_of_e
|
|
|
|
|
|
|
|
assert_kind_of Integer, b.world_population
|
2018-03-03 19:25:29 +00:00
|
|
|
assert_equal 2**62, b.world_population
|
2016-05-17 14:56:08 +00:00
|
|
|
assert_kind_of Integer, b.my_house_population
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal 3, b.my_house_population
|
|
|
|
assert_kind_of BigDecimal, b.bank_balance
|
|
|
|
assert_equal BigDecimal("1586.43"), b.bank_balance
|
|
|
|
assert_kind_of BigDecimal, b.big_bank_balance
|
|
|
|
assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
|
|
|
|
|
|
|
|
# This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
|
|
|
|
# precision/scale explicitly left out. By the SQL standard, numbers
|
|
|
|
# assigned to this field should be truncated but that's seldom respected.
|
|
|
|
if current_adapter?(:PostgreSQLAdapter)
|
|
|
|
# - PostgreSQL changes the SQL spec on columns declared simply as
|
|
|
|
# "decimal" to something more useful: instead of being given a scale
|
|
|
|
# of 0, they take on the compile-time limit for precision and scale,
|
|
|
|
# so the following should succeed unless you have used really wacky
|
|
|
|
# compilation options
|
|
|
|
assert_kind_of BigDecimal, b.value_of_e
|
|
|
|
assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
|
|
|
|
elsif current_adapter?(:SQLite3Adapter)
|
|
|
|
# - SQLite3 stores a float, in violation of SQL
|
|
|
|
assert_kind_of BigDecimal, b.value_of_e
|
|
|
|
assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
|
|
|
|
else
|
|
|
|
# - SQL standard is an integer
|
2016-05-17 14:56:08 +00:00
|
|
|
assert_kind_of Integer, b.value_of_e
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal 2, b.value_of_e
|
|
|
|
end
|
|
|
|
|
|
|
|
GiveMeBigNumbers.down
|
2012-04-26 17:32:55 +00:00
|
|
|
assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first }
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-12-09 19:07:29 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_filtering_migrations
|
2013-07-16 12:19:24 +00:00
|
|
|
assert_no_column Person, :last_name
|
2018-01-25 23:14:09 +00:00
|
|
|
assert_not_predicate Reminder, :table_exists?
|
2010-11-17 21:55:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid", @schema_migration)
|
2018-01-10 15:25:13 +00:00
|
|
|
migrator.up(&name_filter)
|
2010-11-17 21:55:03 +00:00
|
|
|
|
2013-07-16 12:19:24 +00:00
|
|
|
assert_column Person, :last_name
|
2012-04-26 17:32:55 +00:00
|
|
|
assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
|
2010-11-17 21:55:03 +00:00
|
|
|
|
2018-01-10 15:25:13 +00:00
|
|
|
migrator.down(&name_filter)
|
2012-01-10 22:48:51 +00:00
|
|
|
|
2013-07-16 12:19:24 +00:00
|
|
|
assert_no_column Person, :last_name
|
2012-04-26 17:32:55 +00:00
|
|
|
assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
|
|
|
|
2015-12-05 20:14:22 +00:00
|
|
|
class MockMigration < ActiveRecord::Migration::Current
|
2012-01-10 22:48:51 +00:00
|
|
|
attr_reader :went_up, :went_down
|
|
|
|
def initialize
|
|
|
|
@went_up = false
|
|
|
|
@went_down = false
|
2010-11-17 21:55:03 +00:00
|
|
|
end
|
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def up
|
|
|
|
@went_up = true
|
|
|
|
super
|
|
|
|
end
|
2010-11-17 21:55:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def down
|
|
|
|
@went_down = true
|
|
|
|
super
|
2010-11-17 21:55:03 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2010-11-17 21:55:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_instance_based_migration_up
|
|
|
|
migration = MockMigration.new
|
2018-05-13 02:26:10 +00:00
|
|
|
assert_not migration.went_up, "have not gone up"
|
|
|
|
assert_not migration.went_down, "have not gone down"
|
2006-07-07 10:48:43 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
migration.migrate :up
|
2016-08-06 16:26:20 +00:00
|
|
|
assert migration.went_up, "have gone up"
|
2018-05-13 02:26:10 +00:00
|
|
|
assert_not migration.went_down, "have not gone down"
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_instance_based_migration_down
|
|
|
|
migration = MockMigration.new
|
2018-05-13 02:26:10 +00:00
|
|
|
assert_not migration.went_up, "have not gone up"
|
|
|
|
assert_not migration.went_down, "have not gone down"
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
migration.migrate :down
|
2018-05-13 02:26:10 +00:00
|
|
|
assert_not migration.went_up, "have gone up"
|
2016-08-06 16:26:20 +00:00
|
|
|
assert migration.went_down, "have not gone down"
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2005-07-04 18:51:02 +00:00
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
if ActiveRecord::Base.connection.supports_ddl_transactions?
|
|
|
|
def test_migrator_one_up_with_exception_and_rollback
|
|
|
|
assert_no_column Person, :last_name
|
|
|
|
|
2015-12-05 20:14:22 +00:00
|
|
|
migration = Class.new(ActiveRecord::Migration::Current) {
|
2013-11-08 15:57:51 +00:00
|
|
|
def version; 100 end
|
|
|
|
def migrate(x)
|
|
|
|
add_column "people", "last_name", :string
|
2016-08-06 16:26:20 +00:00
|
|
|
raise "Something broke"
|
2013-11-08 15:57:51 +00:00
|
|
|
end
|
|
|
|
}.new
|
2012-01-11 22:53:37 +00:00
|
|
|
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
|
2012-01-11 22:53:37 +00:00
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
e = assert_raise(StandardError) { migrator.migrate }
|
2012-01-11 21:49:10 +00:00
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
|
2012-01-11 21:49:10 +00:00
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
assert_no_column Person, :last_name,
|
|
|
|
"On error, the Migrator should revert schema changes but it did not."
|
2013-02-25 15:05:22 +00:00
|
|
|
end
|
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
def test_migrator_one_up_with_exception_and_rollback_using_run
|
|
|
|
assert_no_column Person, :last_name
|
2013-02-25 15:05:22 +00:00
|
|
|
|
2015-12-05 20:14:22 +00:00
|
|
|
migration = Class.new(ActiveRecord::Migration::Current) {
|
2013-11-08 15:57:51 +00:00
|
|
|
def version; 100 end
|
|
|
|
def migrate(x)
|
|
|
|
add_column "people", "last_name", :string
|
2016-08-06 16:26:20 +00:00
|
|
|
raise "Something broke"
|
2013-11-08 15:57:51 +00:00
|
|
|
end
|
|
|
|
}.new
|
2013-02-25 15:05:22 +00:00
|
|
|
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
|
2013-02-25 15:05:22 +00:00
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
e = assert_raise(StandardError) { migrator.run }
|
2013-02-25 15:05:22 +00:00
|
|
|
|
2016-01-14 21:09:18 +00:00
|
|
|
assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
|
2013-02-25 15:05:22 +00:00
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
assert_no_column Person, :last_name,
|
|
|
|
"On error, the Migrator should revert schema changes but it did not."
|
2013-03-01 10:39:39 +00:00
|
|
|
end
|
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
def test_migration_without_transaction
|
|
|
|
assert_no_column Person, :last_name
|
2013-03-01 10:39:39 +00:00
|
|
|
|
2015-12-05 20:14:22 +00:00
|
|
|
migration = Class.new(ActiveRecord::Migration::Current) {
|
2016-08-07 23:05:28 +00:00
|
|
|
disable_ddl_transaction!
|
2013-03-01 10:39:39 +00:00
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
def version; 101 end
|
|
|
|
def migrate(x)
|
|
|
|
add_column "people", "last_name", :string
|
2016-08-06 16:26:20 +00:00
|
|
|
raise "Something broke"
|
2013-11-08 15:57:51 +00:00
|
|
|
end
|
|
|
|
}.new
|
2013-03-01 10:39:39 +00:00
|
|
|
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 101)
|
2013-11-08 15:57:51 +00:00
|
|
|
e = assert_raise(StandardError) { migrator.migrate }
|
|
|
|
assert_equal "An error has occurred, all later migrations canceled:\n\nSomething broke", e.message
|
2013-03-01 10:39:39 +00:00
|
|
|
|
2013-11-08 15:57:51 +00:00
|
|
|
assert_column Person, :last_name,
|
|
|
|
"without ddl transactions, the Migrator should not rollback on error but it did."
|
|
|
|
ensure
|
|
|
|
Person.reset_column_information
|
2016-08-06 16:26:20 +00:00
|
|
|
if Person.column_names.include?("last_name")
|
|
|
|
Person.connection.remove_column("people", "last_name")
|
2013-11-08 15:57:51 +00:00
|
|
|
end
|
2013-03-01 10:39:39 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-12-09 02:15:59 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_schema_migrations_table_name
|
2017-03-05 11:28:03 +00:00
|
|
|
original_schema_migrations_table_name = ActiveRecord::Base.schema_migrations_table_name
|
2013-10-18 00:00:22 +00:00
|
|
|
|
2017-03-05 11:28:03 +00:00
|
|
|
assert_equal "schema_migrations", ActiveRecord::SchemaMigration.table_name
|
2012-01-10 22:48:51 +00:00
|
|
|
ActiveRecord::Base.table_name_prefix = "prefix_"
|
|
|
|
ActiveRecord::Base.table_name_suffix = "_suffix"
|
|
|
|
Reminder.reset_table_name
|
2017-03-05 11:28:03 +00:00
|
|
|
assert_equal "prefix_schema_migrations_suffix", ActiveRecord::SchemaMigration.table_name
|
2013-10-18 00:00:22 +00:00
|
|
|
ActiveRecord::Base.schema_migrations_table_name = "changed"
|
|
|
|
Reminder.reset_table_name
|
2017-03-05 11:28:03 +00:00
|
|
|
assert_equal "prefix_changed_suffix", ActiveRecord::SchemaMigration.table_name
|
2012-01-10 22:48:51 +00:00
|
|
|
ActiveRecord::Base.table_name_prefix = ""
|
|
|
|
ActiveRecord::Base.table_name_suffix = ""
|
|
|
|
Reminder.reset_table_name
|
2017-03-05 11:28:03 +00:00
|
|
|
assert_equal "changed", ActiveRecord::SchemaMigration.table_name
|
2013-10-18 00:00:22 +00:00
|
|
|
ensure
|
|
|
|
ActiveRecord::Base.schema_migrations_table_name = original_schema_migrations_table_name
|
2019-04-03 21:28:59 +00:00
|
|
|
ActiveRecord::SchemaMigration.reset_table_name
|
2013-10-18 00:00:22 +00:00
|
|
|
Reminder.reset_table_name
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
|
|
|
|
2015-08-14 16:31:33 +00:00
|
|
|
def test_internal_metadata_table_name
|
|
|
|
original_internal_metadata_table_name = ActiveRecord::Base.internal_metadata_table_name
|
|
|
|
|
2016-01-12 01:51:54 +00:00
|
|
|
assert_equal "ar_internal_metadata", ActiveRecord::InternalMetadata.table_name
|
|
|
|
ActiveRecord::Base.table_name_prefix = "p_"
|
|
|
|
ActiveRecord::Base.table_name_suffix = "_s"
|
2015-08-14 16:31:33 +00:00
|
|
|
Reminder.reset_table_name
|
2016-01-12 01:51:54 +00:00
|
|
|
assert_equal "p_ar_internal_metadata_s", ActiveRecord::InternalMetadata.table_name
|
2015-08-14 16:31:33 +00:00
|
|
|
ActiveRecord::Base.internal_metadata_table_name = "changed"
|
|
|
|
Reminder.reset_table_name
|
2016-01-12 01:51:54 +00:00
|
|
|
assert_equal "p_changed_s", ActiveRecord::InternalMetadata.table_name
|
2015-08-14 16:31:33 +00:00
|
|
|
ActiveRecord::Base.table_name_prefix = ""
|
|
|
|
ActiveRecord::Base.table_name_suffix = ""
|
|
|
|
Reminder.reset_table_name
|
|
|
|
assert_equal "changed", ActiveRecord::InternalMetadata.table_name
|
|
|
|
ensure
|
|
|
|
ActiveRecord::Base.internal_metadata_table_name = original_internal_metadata_table_name
|
2019-04-03 21:28:59 +00:00
|
|
|
ActiveRecord::InternalMetadata.reset_table_name
|
2015-08-14 16:31:33 +00:00
|
|
|
Reminder.reset_table_name
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_internal_metadata_stores_environment
|
|
|
|
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
|
|
|
|
migrations_path = MIGRATIONS_ROOT + "/valid"
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
|
2015-08-14 16:31:33 +00:00
|
|
|
|
2018-01-10 15:25:13 +00:00
|
|
|
migrator.up
|
2016-01-08 15:27:25 +00:00
|
|
|
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
|
2015-08-14 16:31:33 +00:00
|
|
|
|
|
|
|
original_rails_env = ENV["RAILS_ENV"]
|
|
|
|
original_rack_env = ENV["RACK_ENV"]
|
|
|
|
ENV["RAILS_ENV"] = ENV["RACK_ENV"] = "foofoo"
|
2016-10-29 03:05:58 +00:00
|
|
|
new_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
|
2015-08-14 16:31:33 +00:00
|
|
|
|
2018-04-04 01:34:51 +00:00
|
|
|
assert_not_equal current_env, new_env
|
2015-08-14 16:31:33 +00:00
|
|
|
|
|
|
|
sleep 1 # mysql by default does not store fractional seconds in the database
|
2018-01-10 15:25:13 +00:00
|
|
|
migrator.up
|
2016-01-08 15:27:25 +00:00
|
|
|
assert_equal new_env, ActiveRecord::InternalMetadata[:environment]
|
2015-08-14 16:31:33 +00:00
|
|
|
ensure
|
|
|
|
ENV["RAILS_ENV"] = original_rails_env
|
|
|
|
ENV["RACK_ENV"] = original_rack_env
|
2018-01-10 15:25:13 +00:00
|
|
|
migrator.up
|
2015-08-14 16:31:33 +00:00
|
|
|
end
|
|
|
|
|
2016-04-25 10:35:16 +00:00
|
|
|
def test_internal_metadata_stores_environment_when_other_data_exists
|
|
|
|
ActiveRecord::InternalMetadata.delete_all
|
2016-10-29 03:05:58 +00:00
|
|
|
ActiveRecord::InternalMetadata[:foo] = "bar"
|
2016-04-25 10:35:16 +00:00
|
|
|
|
|
|
|
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
|
|
|
|
migrations_path = MIGRATIONS_ROOT + "/valid"
|
|
|
|
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
|
2018-01-10 15:25:13 +00:00
|
|
|
migrator.up
|
2016-04-25 10:35:16 +00:00
|
|
|
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
|
2016-08-06 16:26:20 +00:00
|
|
|
assert_equal "bar", ActiveRecord::InternalMetadata[:foo]
|
2016-04-25 10:35:16 +00:00
|
|
|
end
|
|
|
|
|
2013-08-22 20:15:11 +00:00
|
|
|
def test_proper_table_name_on_migration
|
2014-01-15 19:33:37 +00:00
|
|
|
reminder_class = new_isolated_reminder_class
|
2013-08-22 20:15:11 +00:00
|
|
|
migration = ActiveRecord::Migration.new
|
2016-08-06 16:26:20 +00:00
|
|
|
assert_equal "table", migration.proper_table_name("table")
|
2013-08-22 20:15:11 +00:00
|
|
|
assert_equal "table", migration.proper_table_name(:table)
|
2014-01-15 19:33:37 +00:00
|
|
|
assert_equal "reminders", migration.proper_table_name(reminder_class)
|
|
|
|
reminder_class.reset_table_name
|
|
|
|
assert_equal reminder_class.table_name, migration.proper_table_name(reminder_class)
|
2013-08-22 20:15:11 +00:00
|
|
|
|
|
|
|
# Use the model's own prefix/suffix if a model is given
|
|
|
|
ActiveRecord::Base.table_name_prefix = "ARprefix_"
|
|
|
|
ActiveRecord::Base.table_name_suffix = "_ARsuffix"
|
2016-08-06 16:26:20 +00:00
|
|
|
reminder_class.table_name_prefix = "prefix_"
|
|
|
|
reminder_class.table_name_suffix = "_suffix"
|
2014-01-15 19:33:37 +00:00
|
|
|
reminder_class.reset_table_name
|
|
|
|
assert_equal "prefix_reminders_suffix", migration.proper_table_name(reminder_class)
|
2016-08-06 16:26:20 +00:00
|
|
|
reminder_class.table_name_prefix = ""
|
|
|
|
reminder_class.table_name_suffix = ""
|
2014-01-15 19:33:37 +00:00
|
|
|
reminder_class.reset_table_name
|
2013-08-22 20:15:11 +00:00
|
|
|
|
|
|
|
# Use AR::Base's prefix/suffix if string or symbol is given
|
|
|
|
ActiveRecord::Base.table_name_prefix = "prefix_"
|
|
|
|
ActiveRecord::Base.table_name_suffix = "_suffix"
|
2014-01-15 19:33:37 +00:00
|
|
|
reminder_class.reset_table_name
|
2016-08-06 16:26:20 +00:00
|
|
|
assert_equal "prefix_table_suffix", migration.proper_table_name("table", migration.table_name_options)
|
2013-08-22 20:15:11 +00:00
|
|
|
assert_equal "prefix_table_suffix", migration.proper_table_name(:table, migration.table_name_options)
|
|
|
|
end
|
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_rename_table_with_prefix_and_suffix
|
2018-01-25 23:14:09 +00:00
|
|
|
assert_not_predicate Thing, :table_exists?
|
2016-08-06 16:26:20 +00:00
|
|
|
ActiveRecord::Base.table_name_prefix = "p_"
|
|
|
|
ActiveRecord::Base.table_name_suffix = "_s"
|
2012-01-10 22:48:51 +00:00
|
|
|
Thing.reset_table_name
|
|
|
|
Thing.reset_sequence_name
|
|
|
|
WeNeedThings.up
|
2019-06-11 10:57:38 +00:00
|
|
|
assert_predicate Thing, :table_exists?
|
Attribute assignment and type casting has nothing to do with columns
It's finally finished!!!!!!! The reason the Attributes API was kept
private in 4.2 was due to some publicly visible implementation details.
It was previously implemented by overloading `columns` and
`columns_hash`, to make them return column objects which were modified
with the attribute information.
This meant that those methods LIED! We didn't change the database
schema. We changed the attribute information on the class. That is
wrong! It should be the other way around, where schema loading just
calls the attributes API for you. And now it does!
Yes, this means that there is nothing that happens in automatic schema
loading that you couldn't manually do yourself. (There's still some
funky cases where we hit the connection adapter that I need to handle,
before we can turn off automatic schema detection entirely.)
There were a few weird test failures caused by this that had to be
fixed. The main source came from the fact that the attribute methods are
now defined in terms of `attribute_names`, which has a clause like
`return [] unless table_exists?`. I don't *think* this is an issue,
since the only place this caused failures were in a fake adapter which
didn't override `table_exists?`.
Additionally, there were a few cases where tests were failing because a
migration was run, but the model was not reloaded. I'm not sure why
these started failing from this change, I might need to clear an
additional cache in `reload_schema_from_cache`. Again, since this is not
normal usage, and it's expected that `reset_column_information` will be
called after the table is modified, I don't think it's a problem.
Still, test failures that were unrelated to the change are worrying, and
I need to dig into them further.
Finally, I spent a lot of time debugging issues with the mutex used in
`define_attribute_methods`. I think we can just remove that method
entirely, and define the attribute methods *manually* in the call to
`define_attribute`, which would simplify the code *tremendously*.
Ok. now to make this damn thing public, and work on moving it up to
Active Model.
2015-01-30 21:03:36 +00:00
|
|
|
Thing.reset_column_information
|
2012-01-10 22:48:51 +00:00
|
|
|
|
|
|
|
assert Thing.create("content" => "hello world")
|
2012-04-26 17:32:55 +00:00
|
|
|
assert_equal "hello world", Thing.first.content
|
2012-01-10 22:48:51 +00:00
|
|
|
|
|
|
|
RenameThings.up
|
2012-04-13 11:19:38 +00:00
|
|
|
Thing.table_name = "p_awesome_things_s"
|
2012-01-10 22:48:51 +00:00
|
|
|
|
2012-04-26 17:32:55 +00:00
|
|
|
assert_equal "hello world", Thing.first.content
|
2012-01-10 22:48:51 +00:00
|
|
|
ensure
|
|
|
|
Thing.reset_table_name
|
|
|
|
Thing.reset_sequence_name
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_add_drop_table_with_prefix_and_suffix
|
2018-01-25 23:14:09 +00:00
|
|
|
assert_not_predicate Reminder, :table_exists?
|
2016-08-06 16:26:20 +00:00
|
|
|
ActiveRecord::Base.table_name_prefix = "prefix_"
|
|
|
|
ActiveRecord::Base.table_name_suffix = "_suffix"
|
2012-01-10 22:48:51 +00:00
|
|
|
Reminder.reset_table_name
|
|
|
|
Reminder.reset_sequence_name
|
|
|
|
WeNeedReminders.up
|
2019-06-11 10:57:38 +00:00
|
|
|
assert_predicate Reminder, :table_exists?
|
|
|
|
Reminder.reset_column_information
|
2012-01-10 22:48:51 +00:00
|
|
|
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
|
2012-04-26 17:32:55 +00:00
|
|
|
assert_equal "hello world", Reminder.first.content
|
2012-01-10 22:48:51 +00:00
|
|
|
|
|
|
|
WeNeedReminders.down
|
2012-04-26 17:32:55 +00:00
|
|
|
assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
|
2012-01-10 22:48:51 +00:00
|
|
|
ensure
|
|
|
|
Reminder.reset_sequence_name
|
|
|
|
end
|
2006-07-07 10:48:43 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_create_table_with_binary_column
|
|
|
|
assert_nothing_raised {
|
|
|
|
Person.connection.create_table :binary_testings do |t|
|
2016-08-06 17:37:57 +00:00
|
|
|
t.column "data", :binary, null: false
|
2008-09-14 11:06:10 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
columns = Person.connection.columns(:binary_testings)
|
|
|
|
data_column = columns.detect { |c| c.name == "data" }
|
2006-04-04 16:33:31 +00:00
|
|
|
|
2012-10-19 14:17:06 +00:00
|
|
|
assert_nil data_column.default
|
2016-02-24 06:14:43 +00:00
|
|
|
ensure
|
2015-02-12 11:20:03 +00:00
|
|
|
Person.connection.drop_table :binary_testings, if_exists: true
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
|
|
|
|
2014-09-02 05:35:56 +00:00
|
|
|
unless mysql_enforcing_gtid_consistency?
|
|
|
|
def test_create_table_with_query
|
2017-06-13 01:55:29 +00:00
|
|
|
Person.connection.create_table :table_from_query_testings, as: "SELECT id FROM people WHERE id = 1"
|
2013-12-13 22:20:03 +00:00
|
|
|
|
2014-09-02 05:35:56 +00:00
|
|
|
columns = Person.connection.columns(:table_from_query_testings)
|
2017-06-13 01:55:29 +00:00
|
|
|
assert_equal [1], Person.connection.select_values("SELECT * FROM table_from_query_testings")
|
2014-09-02 05:35:56 +00:00
|
|
|
assert_equal 1, columns.length
|
|
|
|
assert_equal "id", columns.first.name
|
2016-02-24 06:14:43 +00:00
|
|
|
ensure
|
2014-09-02 05:35:56 +00:00
|
|
|
Person.connection.drop_table :table_from_query_testings rescue nil
|
|
|
|
end
|
2013-12-13 22:20:03 +00:00
|
|
|
|
2014-09-02 05:35:56 +00:00
|
|
|
def test_create_table_with_query_from_relation
|
2017-06-13 01:55:29 +00:00
|
|
|
Person.connection.create_table :table_from_query_testings, as: Person.select(:id).where(id: 1)
|
2013-12-13 22:20:03 +00:00
|
|
|
|
2014-09-02 05:35:56 +00:00
|
|
|
columns = Person.connection.columns(:table_from_query_testings)
|
2017-06-13 01:55:29 +00:00
|
|
|
assert_equal [1], Person.connection.select_values("SELECT * FROM table_from_query_testings")
|
2014-09-02 05:35:56 +00:00
|
|
|
assert_equal 1, columns.length
|
|
|
|
assert_equal "id", columns.first.name
|
2016-02-24 06:14:43 +00:00
|
|
|
ensure
|
2014-09-02 05:35:56 +00:00
|
|
|
Person.connection.drop_table :table_from_query_testings rescue nil
|
|
|
|
end
|
2013-12-13 22:20:03 +00:00
|
|
|
end
|
|
|
|
|
Added nil case handling to allow rollback migration in case of
invalid column type
/activerecord/lib/active_record/connection_adapters
/abstract/schema_definitions.rb:306
type = type.to_sym
Changed to the following to handle nil case:
type = type.to_sym if type
Added regression test for this case:
/activerecord/test/cases/migration_test.rb:554
if current_adapter?(:SQLite3Adapter)
def test_allows_sqlite3_rollback_on_invalid_column_type
Person.connection.create_table :something, force: true do |t|
t.column :number, :integer
t.column :name, :string
t.column :foo, :bar
end
assert Person.connection.column_exists?(:something, :foo)
assert_nothing_raised { Person.connection.remove_column :something, :foo, :bar }
assert !Person.connection.column_exists?(:something, :foo)
assert Person.connection.column_exists?(:something, :name)
assert Person.connection.column_exists?(:something, :number)
ensure
Person.connection.drop_table :something, if_exists: true
end
end
2016-08-18 00:21:54 +00:00
|
|
|
if current_adapter?(:SQLite3Adapter)
|
|
|
|
def test_allows_sqlite3_rollback_on_invalid_column_type
|
|
|
|
Person.connection.create_table :something, force: true do |t|
|
|
|
|
t.column :number, :integer
|
|
|
|
t.column :name, :string
|
|
|
|
t.column :foo, :bar
|
|
|
|
end
|
|
|
|
assert Person.connection.column_exists?(:something, :foo)
|
|
|
|
assert_nothing_raised { Person.connection.remove_column :something, :foo, :bar }
|
2018-04-17 22:21:34 +00:00
|
|
|
assert_not Person.connection.column_exists?(:something, :foo)
|
Added nil case handling to allow rollback migration in case of
invalid column type
/activerecord/lib/active_record/connection_adapters
/abstract/schema_definitions.rb:306
type = type.to_sym
Changed to the following to handle nil case:
type = type.to_sym if type
Added regression test for this case:
/activerecord/test/cases/migration_test.rb:554
if current_adapter?(:SQLite3Adapter)
def test_allows_sqlite3_rollback_on_invalid_column_type
Person.connection.create_table :something, force: true do |t|
t.column :number, :integer
t.column :name, :string
t.column :foo, :bar
end
assert Person.connection.column_exists?(:something, :foo)
assert_nothing_raised { Person.connection.remove_column :something, :foo, :bar }
assert !Person.connection.column_exists?(:something, :foo)
assert Person.connection.column_exists?(:something, :name)
assert Person.connection.column_exists?(:something, :number)
ensure
Person.connection.drop_table :something, if_exists: true
end
end
2016-08-18 00:21:54 +00:00
|
|
|
assert Person.connection.column_exists?(:something, :name)
|
|
|
|
assert Person.connection.column_exists?(:something, :number)
|
|
|
|
ensure
|
|
|
|
Person.connection.drop_table :something, if_exists: true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-25 17:30:41 +00:00
|
|
|
def test_decimal_scale_without_precision_should_raise
|
|
|
|
e = assert_raise(ArgumentError) do
|
|
|
|
Person.connection.create_table :test_decimal_scales, force: true do |t|
|
|
|
|
t.decimal :scaleonly, scale: 10
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_equal "Error adding decimal column: precision cannot be empty if scale is specified", e.message
|
|
|
|
ensure
|
|
|
|
Person.connection.drop_table :test_decimal_scales, if_exists: true
|
|
|
|
end
|
|
|
|
|
2015-12-15 22:01:30 +00:00
|
|
|
if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
|
2016-02-24 01:39:07 +00:00
|
|
|
def test_out_of_range_integer_limit_should_raise
|
Raise `ArgumentError` for invalid `:limit` and `:precision` like as other options
When I've added new `:size` option in #35071, I've found that invalid
`:limit` and `:precision` raises `ActiveRecordError` unlike other
invalid options.
I think that is hard to distinguish argument errors and statement
invalid errors since the `StatementInvalid` is a subclass of the
`ActiveRecordError`.
https://github.com/rails/rails/blob/c9e4c848eeeb8999b778fa1ae52185ca5537fffe/activerecord/lib/active_record/errors.rb#L103
```ruby
begin
# execute any migration
rescue ActiveRecord::StatementInvalid
# statement invalid
rescue ActiveRecord::ActiveRecordError, ArgumentError
# `ActiveRecordError` except `StatementInvalid` is maybe an argument error
end
```
I'd say this is the inconsistency worth fixing.
Before:
```ruby
add_column :items, :attr1, :binary, size: 10 # => ArgumentError
add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
add_column :items, :attr3, :integer, limit: 10 # => ActiveRecordError
add_column :items, :attr4, :datetime, precision: 10 # => ActiveRecordError
```
After:
```ruby
add_column :items, :attr1, :binary, size: 10 # => ArgumentError
add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
add_column :items, :attr3, :integer, limit: 10 # => ArgumentError
add_column :items, :attr4, :datetime, precision: 10 # => ArgumentError
```
2019-02-12 22:13:23 +00:00
|
|
|
e = assert_raise(ArgumentError) do
|
2016-08-06 17:37:57 +00:00
|
|
|
Person.connection.create_table :test_integer_limits, force: true do |t|
|
|
|
|
t.column :bigone, :integer, limit: 10
|
2013-11-08 15:57:51 +00:00
|
|
|
end
|
2012-05-11 19:45:26 +00:00
|
|
|
end
|
|
|
|
|
2019-02-25 17:30:41 +00:00
|
|
|
assert_includes e.message, "No integer type has byte size 10"
|
2016-02-24 01:39:07 +00:00
|
|
|
ensure
|
|
|
|
Person.connection.drop_table :test_integer_limits, if_exists: true
|
|
|
|
end
|
2014-11-08 02:27:18 +00:00
|
|
|
|
2016-02-24 01:39:07 +00:00
|
|
|
def test_out_of_range_text_limit_should_raise
|
Raise `ArgumentError` for invalid `:limit` and `:precision` like as other options
When I've added new `:size` option in #35071, I've found that invalid
`:limit` and `:precision` raises `ActiveRecordError` unlike other
invalid options.
I think that is hard to distinguish argument errors and statement
invalid errors since the `StatementInvalid` is a subclass of the
`ActiveRecordError`.
https://github.com/rails/rails/blob/c9e4c848eeeb8999b778fa1ae52185ca5537fffe/activerecord/lib/active_record/errors.rb#L103
```ruby
begin
# execute any migration
rescue ActiveRecord::StatementInvalid
# statement invalid
rescue ActiveRecord::ActiveRecordError, ArgumentError
# `ActiveRecordError` except `StatementInvalid` is maybe an argument error
end
```
I'd say this is the inconsistency worth fixing.
Before:
```ruby
add_column :items, :attr1, :binary, size: 10 # => ArgumentError
add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
add_column :items, :attr3, :integer, limit: 10 # => ActiveRecordError
add_column :items, :attr4, :datetime, precision: 10 # => ActiveRecordError
```
After:
```ruby
add_column :items, :attr1, :binary, size: 10 # => ArgumentError
add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
add_column :items, :attr3, :integer, limit: 10 # => ArgumentError
add_column :items, :attr4, :datetime, precision: 10 # => ArgumentError
```
2019-02-12 22:13:23 +00:00
|
|
|
e = assert_raise(ArgumentError) do
|
2016-02-24 01:39:07 +00:00
|
|
|
Person.connection.create_table :test_text_limits, force: true do |t|
|
|
|
|
t.text :bigtext, limit: 0xfffffffff
|
2012-05-11 19:45:26 +00:00
|
|
|
end
|
|
|
|
end
|
2016-02-24 01:39:07 +00:00
|
|
|
|
2019-02-25 17:30:41 +00:00
|
|
|
assert_includes e.message, "No text type has byte size #{0xfffffffff}"
|
|
|
|
ensure
|
|
|
|
Person.connection.drop_table :test_text_limits, if_exists: true
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_out_of_range_binary_limit_should_raise
|
Raise `ArgumentError` for invalid `:limit` and `:precision` like as other options
When I've added new `:size` option in #35071, I've found that invalid
`:limit` and `:precision` raises `ActiveRecordError` unlike other
invalid options.
I think that is hard to distinguish argument errors and statement
invalid errors since the `StatementInvalid` is a subclass of the
`ActiveRecordError`.
https://github.com/rails/rails/blob/c9e4c848eeeb8999b778fa1ae52185ca5537fffe/activerecord/lib/active_record/errors.rb#L103
```ruby
begin
# execute any migration
rescue ActiveRecord::StatementInvalid
# statement invalid
rescue ActiveRecord::ActiveRecordError, ArgumentError
# `ActiveRecordError` except `StatementInvalid` is maybe an argument error
end
```
I'd say this is the inconsistency worth fixing.
Before:
```ruby
add_column :items, :attr1, :binary, size: 10 # => ArgumentError
add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
add_column :items, :attr3, :integer, limit: 10 # => ActiveRecordError
add_column :items, :attr4, :datetime, precision: 10 # => ActiveRecordError
```
After:
```ruby
add_column :items, :attr1, :binary, size: 10 # => ArgumentError
add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
add_column :items, :attr3, :integer, limit: 10 # => ArgumentError
add_column :items, :attr4, :datetime, precision: 10 # => ArgumentError
```
2019-02-12 22:13:23 +00:00
|
|
|
e = assert_raise(ArgumentError) do
|
|
|
|
Person.connection.create_table :test_binary_limits, force: true do |t|
|
2019-02-25 17:30:41 +00:00
|
|
|
t.binary :bigbinary, limit: 0xfffffffff
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_includes e.message, "No binary type has byte size #{0xfffffffff}"
|
2016-02-23 15:21:53 +00:00
|
|
|
ensure
|
Raise `ArgumentError` for invalid `:limit` and `:precision` like as other options
When I've added new `:size` option in #35071, I've found that invalid
`:limit` and `:precision` raises `ActiveRecordError` unlike other
invalid options.
I think that is hard to distinguish argument errors and statement
invalid errors since the `StatementInvalid` is a subclass of the
`ActiveRecordError`.
https://github.com/rails/rails/blob/c9e4c848eeeb8999b778fa1ae52185ca5537fffe/activerecord/lib/active_record/errors.rb#L103
```ruby
begin
# execute any migration
rescue ActiveRecord::StatementInvalid
# statement invalid
rescue ActiveRecord::ActiveRecordError, ArgumentError
# `ActiveRecordError` except `StatementInvalid` is maybe an argument error
end
```
I'd say this is the inconsistency worth fixing.
Before:
```ruby
add_column :items, :attr1, :binary, size: 10 # => ArgumentError
add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
add_column :items, :attr3, :integer, limit: 10 # => ActiveRecordError
add_column :items, :attr4, :datetime, precision: 10 # => ActiveRecordError
```
After:
```ruby
add_column :items, :attr1, :binary, size: 10 # => ArgumentError
add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
add_column :items, :attr3, :integer, limit: 10 # => ArgumentError
add_column :items, :attr4, :datetime, precision: 10 # => ArgumentError
```
2019-02-12 22:13:23 +00:00
|
|
|
Person.connection.drop_table :test_binary_limits, if_exists: true
|
2013-11-08 15:57:51 +00:00
|
|
|
end
|
2019-02-25 17:30:41 +00:00
|
|
|
end
|
2019-01-25 13:01:07 +00:00
|
|
|
|
2019-02-25 17:30:41 +00:00
|
|
|
if current_adapter?(:Mysql2Adapter)
|
2019-01-25 13:01:07 +00:00
|
|
|
def test_invalid_text_size_should_raise
|
|
|
|
e = assert_raise(ArgumentError) do
|
|
|
|
Person.connection.create_table :test_text_sizes, force: true do |t|
|
|
|
|
t.text :bigtext, size: 0xfffffffff
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-25 17:30:41 +00:00
|
|
|
assert_equal "#{0xfffffffff} is invalid :size value. Only :tiny, :medium, and :long are allowed.", e.message
|
2019-01-25 13:01:07 +00:00
|
|
|
ensure
|
|
|
|
Person.connection.drop_table :test_text_sizes, if_exists: true
|
|
|
|
end
|
2012-05-11 19:45:26 +00:00
|
|
|
end
|
|
|
|
|
2015-10-29 21:26:54 +00:00
|
|
|
if ActiveRecord::Base.connection.supports_advisory_locks?
|
2015-11-18 14:53:38 +00:00
|
|
|
def test_migrator_generates_valid_lock_id
|
2015-12-05 20:14:22 +00:00
|
|
|
migration = Class.new(ActiveRecord::Migration::Current).new
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
|
2015-10-29 21:26:54 +00:00
|
|
|
|
2015-11-18 14:53:38 +00:00
|
|
|
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
|
2015-10-29 21:26:54 +00:00
|
|
|
|
2015-11-18 14:53:38 +00:00
|
|
|
assert ActiveRecord::Base.connection.get_advisory_lock(lock_id),
|
|
|
|
"the Migrator should have generated a valid lock id, but it didn't"
|
|
|
|
assert ActiveRecord::Base.connection.release_advisory_lock(lock_id),
|
|
|
|
"the Migrator should have generated a valid lock id, but it didn't"
|
2015-10-29 21:26:54 +00:00
|
|
|
end
|
|
|
|
|
2015-11-18 14:53:38 +00:00
|
|
|
def test_generate_migrator_advisory_lock_id
|
2015-10-29 21:26:54 +00:00
|
|
|
# It is important we are consistent with how we generate this so that
|
|
|
|
# exclusive locking works across migrator versions
|
2015-12-05 20:14:22 +00:00
|
|
|
migration = Class.new(ActiveRecord::Migration::Current).new
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
|
2015-10-29 21:26:54 +00:00
|
|
|
|
2015-11-18 14:53:38 +00:00
|
|
|
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
|
2015-10-29 21:26:54 +00:00
|
|
|
|
|
|
|
current_database = ActiveRecord::Base.connection.current_database
|
|
|
|
salt = ActiveRecord::Migrator::MIGRATOR_SALT
|
2015-11-18 14:53:38 +00:00
|
|
|
expected_id = Zlib.crc32(current_database) * salt
|
2015-10-29 21:26:54 +00:00
|
|
|
|
2015-11-18 14:53:38 +00:00
|
|
|
assert lock_id == expected_id, "expected lock id generated by the migrator to be #{expected_id}, but it was #{lock_id} instead"
|
|
|
|
assert lock_id.bit_length <= 63, "lock id must be a signed integer of max 63 bits magnitude"
|
2015-10-29 21:26:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_migrator_one_up_with_unavailable_lock
|
|
|
|
assert_no_column Person, :last_name
|
|
|
|
|
2015-12-05 20:14:22 +00:00
|
|
|
migration = Class.new(ActiveRecord::Migration::Current) {
|
2015-10-29 21:26:54 +00:00
|
|
|
def version; 100 end
|
|
|
|
def migrate(x)
|
|
|
|
add_column "people", "last_name", :string
|
|
|
|
end
|
|
|
|
}.new
|
|
|
|
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
|
2015-11-18 14:53:38 +00:00
|
|
|
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
|
2015-10-29 21:26:54 +00:00
|
|
|
|
2015-11-18 14:53:38 +00:00
|
|
|
with_another_process_holding_lock(lock_id) do
|
2015-10-29 21:26:54 +00:00
|
|
|
assert_raise(ActiveRecord::ConcurrentMigrationError) { migrator.migrate }
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_no_column Person, :last_name,
|
|
|
|
"without an advisory lock, the Migrator should not make any changes, but it did."
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_migrator_one_up_with_unavailable_lock_using_run
|
|
|
|
assert_no_column Person, :last_name
|
|
|
|
|
2015-12-05 20:14:22 +00:00
|
|
|
migration = Class.new(ActiveRecord::Migration::Current) {
|
2015-10-29 21:26:54 +00:00
|
|
|
def version; 100 end
|
|
|
|
def migrate(x)
|
|
|
|
add_column "people", "last_name", :string
|
|
|
|
end
|
|
|
|
}.new
|
|
|
|
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
|
2015-11-18 14:53:38 +00:00
|
|
|
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
|
2015-10-29 21:26:54 +00:00
|
|
|
|
2015-11-18 14:53:38 +00:00
|
|
|
with_another_process_holding_lock(lock_id) do
|
2015-10-29 21:26:54 +00:00
|
|
|
assert_raise(ActiveRecord::ConcurrentMigrationError) { migrator.run }
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_no_column Person, :last_name,
|
|
|
|
"without an advisory lock, the Migrator should not make any changes, but it did."
|
|
|
|
end
|
2017-11-21 05:20:15 +00:00
|
|
|
|
|
|
|
def test_with_advisory_lock_raises_the_right_error_when_it_fails_to_release_lock
|
|
|
|
migration = Class.new(ActiveRecord::Migration::Current).new
|
2019-05-30 12:25:05 +00:00
|
|
|
migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
|
2017-11-21 05:20:15 +00:00
|
|
|
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
|
|
|
|
|
|
|
|
e = assert_raises(ActiveRecord::ConcurrentMigrationError) do
|
|
|
|
silence_stream($stderr) do
|
|
|
|
migrator.send(:with_advisory_lock) do
|
|
|
|
ActiveRecord::Base.connection.release_advisory_lock(lock_id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_match(
|
|
|
|
/#{ActiveRecord::ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE}/,
|
|
|
|
e.message
|
|
|
|
)
|
|
|
|
end
|
2015-10-29 21:26:54 +00:00
|
|
|
end
|
|
|
|
|
2016-12-23 06:51:03 +00:00
|
|
|
private
|
2014-01-15 19:33:37 +00:00
|
|
|
# This is needed to isolate class_attribute assignments like `table_name_prefix`
|
|
|
|
# for each test case.
|
|
|
|
def new_isolated_reminder_class
|
|
|
|
Class.new(Reminder) {
|
|
|
|
def self.name; "Reminder"; end
|
|
|
|
def self.base_class; self; end
|
|
|
|
}
|
|
|
|
end
|
2015-10-29 21:26:54 +00:00
|
|
|
|
2015-11-18 14:53:38 +00:00
|
|
|
def with_another_process_holding_lock(lock_id)
|
2015-10-30 17:30:21 +00:00
|
|
|
thread_lock = Concurrent::CountDownLatch.new
|
|
|
|
test_terminated = Concurrent::CountDownLatch.new
|
2015-10-29 21:26:54 +00:00
|
|
|
|
|
|
|
other_process = Thread.new do
|
2018-12-20 17:44:01 +00:00
|
|
|
conn = ActiveRecord::Base.connection_pool.checkout
|
|
|
|
conn.get_advisory_lock(lock_id)
|
|
|
|
thread_lock.count_down
|
|
|
|
test_terminated.wait # hold the lock open until we tested everything
|
|
|
|
ensure
|
|
|
|
conn.release_advisory_lock(lock_id)
|
|
|
|
ActiveRecord::Base.connection_pool.checkin(conn)
|
2015-10-29 21:26:54 +00:00
|
|
|
end
|
|
|
|
|
2015-10-30 17:30:21 +00:00
|
|
|
thread_lock.wait # wait until the 'other process' has the lock
|
2015-10-29 21:26:54 +00:00
|
|
|
|
|
|
|
yield
|
|
|
|
|
2015-10-30 17:30:21 +00:00
|
|
|
test_terminated.count_down
|
2015-10-29 21:26:54 +00:00
|
|
|
other_process.join
|
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2010-04-06 07:18:22 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
class ReservedWordsMigrationTest < ActiveRecord::TestCase
|
|
|
|
def test_drop_index_from_table_named_values
|
|
|
|
connection = Person.connection
|
2016-08-06 17:37:57 +00:00
|
|
|
connection.create_table :values, force: true do |t|
|
2012-01-10 22:48:51 +00:00
|
|
|
t.integer :value
|
2009-02-04 02:25:37 +00:00
|
|
|
end
|
2008-05-03 16:29:47 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_nothing_raised do
|
|
|
|
connection.add_index :values, :value
|
2019-09-10 21:21:20 +00:00
|
|
|
connection.remove_index :values, :value
|
2009-02-04 02:25:37 +00:00
|
|
|
end
|
2016-02-24 06:14:43 +00:00
|
|
|
ensure
|
2012-01-10 22:48:51 +00:00
|
|
|
connection.drop_table :values rescue nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-01-10 08:50:52 +00:00
|
|
|
class ExplicitlyNamedIndexMigrationTest < ActiveRecord::TestCase
|
|
|
|
def test_drop_index_by_name
|
|
|
|
connection = Person.connection
|
|
|
|
connection.create_table :values, force: true do |t|
|
|
|
|
t.integer :value
|
|
|
|
end
|
|
|
|
|
2016-02-20 04:05:49 +00:00
|
|
|
assert_nothing_raised do
|
2016-08-06 16:26:20 +00:00
|
|
|
connection.add_index :values, :value, name: "a_different_name"
|
2019-09-10 21:21:20 +00:00
|
|
|
connection.remove_index :values, :value, name: "a_different_name"
|
2013-01-10 08:50:52 +00:00
|
|
|
end
|
2016-02-24 06:14:43 +00:00
|
|
|
ensure
|
2013-01-10 08:50:52 +00:00
|
|
|
connection.drop_table :values rescue nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
if ActiveRecord::Base.connection.supports_bulk_alter?
|
|
|
|
class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
|
|
|
|
def setup
|
|
|
|
@connection = Person.connection
|
2016-08-16 07:30:11 +00:00
|
|
|
@connection.create_table(:delete_me, force: true) { |t| }
|
2013-01-18 10:26:01 +00:00
|
|
|
Person.reset_column_information
|
|
|
|
Person.reset_sequence_name
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2014-03-14 04:35:58 +00:00
|
|
|
teardown do
|
2012-01-10 22:48:51 +00:00
|
|
|
Person.connection.drop_table(:delete_me) rescue nil
|
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_adding_multiple_columns
|
2018-08-15 03:03:40 +00:00
|
|
|
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
|
|
|
|
expected_query_count = {
|
|
|
|
"Mysql2Adapter" => 1,
|
|
|
|
"PostgreSQLAdapter" => 2, # one for bulk change, one for comment
|
|
|
|
}.fetch(classname) {
|
|
|
|
raise "need an expected query count for #{classname}"
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_queries(expected_query_count) do
|
2011-01-31 13:21:03 +00:00
|
|
|
with_bulk_change_table do |t|
|
2012-01-10 22:48:51 +00:00
|
|
|
t.column :name, :string
|
2011-01-31 14:10:51 +00:00
|
|
|
t.string :qualification, :experience
|
2016-08-06 17:37:57 +00:00
|
|
|
t.integer :age, default: 0
|
2018-08-15 03:03:40 +00:00
|
|
|
t.date :birthdate, comment: "This is a comment"
|
2014-08-12 20:23:14 +00:00
|
|
|
t.timestamps null: true
|
2011-01-31 13:21:03 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal 8, columns.size
|
2016-08-16 07:30:11 +00:00
|
|
|
[:name, :qualification, :experience].each { |s| assert_equal :string, column(s).type }
|
2016-08-06 16:26:20 +00:00
|
|
|
assert_equal "0", column(:age).default
|
2018-08-15 03:03:40 +00:00
|
|
|
assert_equal "This is a comment", column(:birthdate).comment
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-01-31 14:10:51 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_removing_columns
|
|
|
|
with_bulk_change_table do |t|
|
|
|
|
t.string :qualification, :experience
|
2011-01-31 13:21:03 +00:00
|
|
|
end
|
|
|
|
|
2016-08-16 07:30:11 +00:00
|
|
|
[:qualification, :experience].each { |c| assert column(c) }
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_queries(1) do
|
|
|
|
with_bulk_change_table do |t|
|
|
|
|
t.remove :qualification, :experience
|
|
|
|
t.string :qualification_experience
|
2011-01-31 14:10:51 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2018-04-17 22:21:34 +00:00
|
|
|
[:qualification, :experience].each { |c| assert_not column(c) }
|
2012-01-10 22:48:51 +00:00
|
|
|
assert column(:qualification_experience)
|
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_adding_indexes
|
|
|
|
with_bulk_change_table do |t|
|
|
|
|
t.string :username
|
|
|
|
t.string :name
|
|
|
|
t.integer :age
|
2011-01-31 13:21:03 +00:00
|
|
|
end
|
|
|
|
|
2017-12-06 10:09:24 +00:00
|
|
|
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
|
|
|
|
expected_query_count = {
|
Remove database specific sql statements from SQLCounter
Every database executes different type of sql statement to get metadata then `ActiveRecord::TestCase` ignores these database specific sql statements to make `assert_queries` or `assert_no_queries` work consistently.
Connection adapter already labels these statement by setting "SCHEMA" argument, this pull request makes use of "SCHEMA" argument to ignore metadata queries.
Here are the details of these changes:
* PostgresqlConnectionTest
Each of PostgresqlConnectionTest modified just executes corresponding methods
https://github.com/rails/rails/blob/fef174f5c524edacbcad846d68400e7fe114a15a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb#L182-L195
```ruby
# Returns the current database encoding format.
def encoding
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
end
# Returns the current database collation.
def collation
query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
end
# Returns the current database ctype.
def ctype
query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
end
```
* BulkAlterTableMigrationsTest
mysql2 adapter executes `SHOW KEYS FROM ...` to see if there is an index already created as below. I think the main concerns of these tests are how each database adapter creates or drops indexes then ignoring `SHOW KEYS FROM` statement makes sense.
https://github.com/rails/rails/blob/fef174f5c524edacbcad846d68400e7fe114a15a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb#L11
```ruby
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
```
* Temporary change not included in this commit to show which statements executed
```diff
$ git diff
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 8e8ed494d9..df05f9bd16 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -854,7 +854,7 @@ def test_adding_indexes
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
expected_query_count = {
- "Mysql2Adapter" => 3, # Adding an index fires a query every time to check if an index already exists or not
+ "Mysql2Adapter" => 1, # Adding an index fires a query every time to check if an index already exists or not
"PostgreSQLAdapter" => 2,
}.fetch(classname) {
raise "need an expected query count for #{classname}"
@@ -886,7 +886,7 @@ def test_removing_index
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
expected_query_count = {
- "Mysql2Adapter" => 3, # Adding an index fires a query every time to check if an index already exists or not
+ "Mysql2Adapter" => 1, # Adding an index fires a query every time to check if an index already exists or not
"PostgreSQLAdapter" => 2,
}.fetch(classname) {
raise "need an expected query count for #{classname}"
$
```
* Executed these modified tests
```ruby
$ ARCONN=mysql2 bin/test test/cases/migration_test.rb -n /index/
Using mysql2
Run options: -n /index/ --seed 8462
F
Failure:
BulkAlterTableMigrationsTest#test_adding_indexes [/home/yahonda/git/rails/activerecord/test/cases/migration_test.rb:863]:
3 instead of 1 queries were executed.
Queries:
SHOW KEYS FROM `delete_me`
SHOW KEYS FROM `delete_me`
ALTER TABLE `delete_me` ADD UNIQUE INDEX `awesome_username_index` (`username`), ADD INDEX `index_delete_me_on_name_and_age` (`name`, `age`).
Expected: 1
Actual: 3
bin/test test/cases/migration_test.rb:848
F
Failure:
BulkAlterTableMigrationsTest#test_removing_index [/home/yahonda/git/rails/activerecord/test/cases/migration_test.rb:895]:
3 instead of 1 queries were executed.
Queries:
SHOW KEYS FROM `delete_me`
SHOW KEYS FROM `delete_me`
ALTER TABLE `delete_me` DROP INDEX `index_delete_me_on_name`, ADD UNIQUE INDEX `new_name_index` (`name`).
Expected: 1
Actual: 3
bin/test test/cases/migration_test.rb:879
..
Finished in 0.379245s, 10.5473 runs/s, 7.9105 assertions/s.
4 runs, 3 assertions, 2 failures, 0 errors, 0 skips
$
```
* ActiveRecord::ConnectionAdapters::Savepoints
Left `self.ignored_sql` to ignore savepoint related statements because these SQL statements are not related "SCHEMA"
```
self.ignored_sql = [/^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/]
```
https://github.com/rails/rails/blob/fef174f5c524edacbcad846d68400e7fe114a15a/activerecord/lib/active_record/connection_adapters/abstract/savepoints.rb#L10-L20
```ruby
def create_savepoint(name = current_savepoint_name)
execute("SAVEPOINT #{name}")
end
def exec_rollback_to_savepoint(name = current_savepoint_name)
execute("ROLLBACK TO SAVEPOINT #{name}")
end
def release_savepoint(name = current_savepoint_name)
execute("RELEASE SAVEPOINT #{name}")
end
```
2019-04-30 22:42:59 +00:00
|
|
|
"Mysql2Adapter" => 1, # mysql2 supports creating two indexes using one statement
|
2017-12-06 10:09:24 +00:00
|
|
|
"PostgreSQLAdapter" => 2,
|
|
|
|
}.fetch(classname) {
|
|
|
|
raise "need an expected query count for #{classname}"
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_queries(expected_query_count) do
|
2011-01-31 13:21:03 +00:00
|
|
|
with_bulk_change_table do |t|
|
2016-08-06 17:37:57 +00:00
|
|
|
t.index :username, unique: true, name: :awesome_username_index
|
2012-01-10 22:48:51 +00:00
|
|
|
t.index [:name, :age]
|
2011-01-31 13:21:03 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal 2, indexes.size
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
name_age_index = index(:index_delete_me_on_name_and_age)
|
2016-08-06 16:26:20 +00:00
|
|
|
assert_equal ["name", "age"].sort, name_age_index.columns.sort
|
2018-04-17 22:21:34 +00:00
|
|
|
assert_not name_age_index.unique
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
assert index(:awesome_username_index).unique
|
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_removing_index
|
|
|
|
with_bulk_change_table do |t|
|
|
|
|
t.string :name
|
|
|
|
t.index :name
|
2011-01-31 14:10:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
assert index(:index_delete_me_on_name)
|
|
|
|
|
2017-12-06 10:09:24 +00:00
|
|
|
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
|
|
|
|
expected_query_count = {
|
Remove database specific sql statements from SQLCounter
Every database executes different type of sql statement to get metadata then `ActiveRecord::TestCase` ignores these database specific sql statements to make `assert_queries` or `assert_no_queries` work consistently.
Connection adapter already labels these statement by setting "SCHEMA" argument, this pull request makes use of "SCHEMA" argument to ignore metadata queries.
Here are the details of these changes:
* PostgresqlConnectionTest
Each of PostgresqlConnectionTest modified just executes corresponding methods
https://github.com/rails/rails/blob/fef174f5c524edacbcad846d68400e7fe114a15a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb#L182-L195
```ruby
# Returns the current database encoding format.
def encoding
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
end
# Returns the current database collation.
def collation
query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
end
# Returns the current database ctype.
def ctype
query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
end
```
* BulkAlterTableMigrationsTest
mysql2 adapter executes `SHOW KEYS FROM ...` to see if there is an index already created as below. I think the main concerns of these tests are how each database adapter creates or drops indexes then ignoring `SHOW KEYS FROM` statement makes sense.
https://github.com/rails/rails/blob/fef174f5c524edacbcad846d68400e7fe114a15a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb#L11
```ruby
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
```
* Temporary change not included in this commit to show which statements executed
```diff
$ git diff
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 8e8ed494d9..df05f9bd16 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -854,7 +854,7 @@ def test_adding_indexes
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
expected_query_count = {
- "Mysql2Adapter" => 3, # Adding an index fires a query every time to check if an index already exists or not
+ "Mysql2Adapter" => 1, # Adding an index fires a query every time to check if an index already exists or not
"PostgreSQLAdapter" => 2,
}.fetch(classname) {
raise "need an expected query count for #{classname}"
@@ -886,7 +886,7 @@ def test_removing_index
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
expected_query_count = {
- "Mysql2Adapter" => 3, # Adding an index fires a query every time to check if an index already exists or not
+ "Mysql2Adapter" => 1, # Adding an index fires a query every time to check if an index already exists or not
"PostgreSQLAdapter" => 2,
}.fetch(classname) {
raise "need an expected query count for #{classname}"
$
```
* Executed these modified tests
```ruby
$ ARCONN=mysql2 bin/test test/cases/migration_test.rb -n /index/
Using mysql2
Run options: -n /index/ --seed 8462
F
Failure:
BulkAlterTableMigrationsTest#test_adding_indexes [/home/yahonda/git/rails/activerecord/test/cases/migration_test.rb:863]:
3 instead of 1 queries were executed.
Queries:
SHOW KEYS FROM `delete_me`
SHOW KEYS FROM `delete_me`
ALTER TABLE `delete_me` ADD UNIQUE INDEX `awesome_username_index` (`username`), ADD INDEX `index_delete_me_on_name_and_age` (`name`, `age`).
Expected: 1
Actual: 3
bin/test test/cases/migration_test.rb:848
F
Failure:
BulkAlterTableMigrationsTest#test_removing_index [/home/yahonda/git/rails/activerecord/test/cases/migration_test.rb:895]:
3 instead of 1 queries were executed.
Queries:
SHOW KEYS FROM `delete_me`
SHOW KEYS FROM `delete_me`
ALTER TABLE `delete_me` DROP INDEX `index_delete_me_on_name`, ADD UNIQUE INDEX `new_name_index` (`name`).
Expected: 1
Actual: 3
bin/test test/cases/migration_test.rb:879
..
Finished in 0.379245s, 10.5473 runs/s, 7.9105 assertions/s.
4 runs, 3 assertions, 2 failures, 0 errors, 0 skips
$
```
* ActiveRecord::ConnectionAdapters::Savepoints
Left `self.ignored_sql` to ignore savepoint related statements because these SQL statements are not related "SCHEMA"
```
self.ignored_sql = [/^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/]
```
https://github.com/rails/rails/blob/fef174f5c524edacbcad846d68400e7fe114a15a/activerecord/lib/active_record/connection_adapters/abstract/savepoints.rb#L10-L20
```ruby
def create_savepoint(name = current_savepoint_name)
execute("SAVEPOINT #{name}")
end
def exec_rollback_to_savepoint(name = current_savepoint_name)
execute("ROLLBACK TO SAVEPOINT #{name}")
end
def release_savepoint(name = current_savepoint_name)
execute("RELEASE SAVEPOINT #{name}")
end
```
2019-04-30 22:42:59 +00:00
|
|
|
"Mysql2Adapter" => 1, # mysql2 supports dropping and creating two indexes using one statement
|
2017-12-06 10:09:24 +00:00
|
|
|
"PostgreSQLAdapter" => 2,
|
|
|
|
}.fetch(classname) {
|
|
|
|
raise "need an expected query count for #{classname}"
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_queries(expected_query_count) do
|
2011-01-31 13:21:03 +00:00
|
|
|
with_bulk_change_table do |t|
|
2012-01-10 22:48:51 +00:00
|
|
|
t.remove_index :name
|
2016-08-06 17:37:57 +00:00
|
|
|
t.index :name, name: :new_name_index, unique: true
|
2011-01-31 13:21:03 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2018-04-17 22:21:34 +00:00
|
|
|
assert_not index(:index_delete_me_on_name)
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
new_name_index = index(:new_name_index)
|
|
|
|
assert new_name_index.unique
|
|
|
|
end
|
2011-01-31 14:10:51 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_changing_columns
|
|
|
|
with_bulk_change_table do |t|
|
|
|
|
t.string :name
|
|
|
|
t.date :birthdate
|
2011-01-31 14:10:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2018-04-17 22:21:34 +00:00
|
|
|
assert_not column(:name).default
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal :date, column(:birthdate).type
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2017-12-06 10:09:24 +00:00
|
|
|
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
|
|
|
|
expected_query_count = {
|
|
|
|
"Mysql2Adapter" => 3, # one query for columns, one query for primary key, one query to do the bulk change
|
2018-01-24 03:25:22 +00:00
|
|
|
"PostgreSQLAdapter" => 3, # one query for columns, one for bulk change, one for comment
|
2017-12-06 10:09:24 +00:00
|
|
|
}.fetch(classname) {
|
|
|
|
raise "need an expected query count for #{classname}"
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_queries(expected_query_count, ignore_none: true) do
|
2012-01-10 22:48:51 +00:00
|
|
|
with_bulk_change_table do |t|
|
2016-08-06 17:37:57 +00:00
|
|
|
t.change :name, :string, default: "NONAME"
|
2018-01-24 03:25:22 +00:00
|
|
|
t.change :birthdate, :datetime, comment: "This is a comment"
|
2011-01-31 14:10:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
end
|
|
|
|
|
2016-08-06 16:26:20 +00:00
|
|
|
assert_equal "NONAME", column(:name).default
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal :datetime, column(:birthdate).type
|
2018-01-24 03:25:22 +00:00
|
|
|
assert_equal "This is a comment", column(:birthdate).comment
|
2012-01-10 22:48:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2019-09-09 23:26:14 +00:00
|
|
|
def test_changing_index
|
|
|
|
with_bulk_change_table do |t|
|
|
|
|
t.string :username
|
|
|
|
t.index :username, name: :username_index
|
|
|
|
end
|
|
|
|
|
|
|
|
assert index(:username_index)
|
|
|
|
assert_not index(:username_index).unique
|
|
|
|
|
|
|
|
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
|
|
|
|
expected_query_count = {
|
|
|
|
"Mysql2Adapter" => 1, # mysql2 supports dropping and creating two indexes using one statement
|
|
|
|
"PostgreSQLAdapter" => 2,
|
|
|
|
}.fetch(classname) {
|
|
|
|
raise "need an expected query count for #{classname}"
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_queries(expected_query_count) do
|
|
|
|
with_bulk_change_table do |t|
|
|
|
|
t.remove_index name: :username_index
|
|
|
|
t.index :username, name: :username_index, unique: true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
assert index(:username_index)
|
|
|
|
assert index(:username_index).unique
|
|
|
|
end
|
|
|
|
|
2017-01-05 09:50:18 +00:00
|
|
|
private
|
2016-08-06 17:55:02 +00:00
|
|
|
def with_bulk_change_table
|
|
|
|
# Reset columns/indexes cache as we're changing the table
|
|
|
|
@columns = @indexes = nil
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2016-08-06 17:55:02 +00:00
|
|
|
Person.connection.change_table(:delete_me, bulk: true) do |t|
|
|
|
|
yield t
|
|
|
|
end
|
2011-01-31 14:10:51 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2016-08-06 17:55:02 +00:00
|
|
|
def column(name)
|
2016-08-16 07:30:11 +00:00
|
|
|
columns.detect { |c| c.name == name.to_s }
|
2016-08-06 17:55:02 +00:00
|
|
|
end
|
2011-01-31 13:21:03 +00:00
|
|
|
|
2016-08-06 17:55:02 +00:00
|
|
|
def columns
|
|
|
|
@columns ||= Person.connection.columns("delete_me")
|
|
|
|
end
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2016-08-06 17:55:02 +00:00
|
|
|
def index(name)
|
2016-08-16 07:30:11 +00:00
|
|
|
indexes.detect { |i| i.name == name.to_s }
|
2016-08-06 17:55:02 +00:00
|
|
|
end
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2016-08-06 17:55:02 +00:00
|
|
|
def indexes
|
|
|
|
@indexes ||= Person.connection.indexes("delete_me")
|
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
end # AlterTableMigrationsTest
|
|
|
|
end
|
|
|
|
|
|
|
|
class CopyMigrationsTest < ActiveRecord::TestCase
|
2015-01-15 06:42:33 +00:00
|
|
|
include ActiveSupport::Testing::Stream
|
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def setup
|
|
|
|
end
|
|
|
|
|
|
|
|
def clear
|
|
|
|
ActiveRecord::Base.timestamped_migrations = true
|
|
|
|
to_delete = Dir[@migrations_path + "/*.rb"] - @existing_migrations
|
|
|
|
File.delete(*to_delete)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_copying_migrations_without_timestamps
|
|
|
|
ActiveRecord::Base.timestamped_migrations = false
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/valid"
|
|
|
|
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
|
|
|
|
2016-08-06 17:44:11 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy")
|
2013-11-01 08:49:57 +00:00
|
|
|
assert File.exist?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied.map(&:filename)
|
|
|
|
|
|
|
|
expected = "# This migration comes from bukkits (originally 1)"
|
2017-07-09 17:41:28 +00:00
|
|
|
assert_equal expected, IO.readlines(@migrations_path + "/4_people_have_hobbies.bukkits.rb")[1].chomp
|
2012-01-10 22:48:51 +00:00
|
|
|
|
|
|
|
files_count = Dir[@migrations_path + "/*.rb"].length
|
2016-08-06 17:44:11 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy")
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
2018-01-25 23:16:57 +00:00
|
|
|
assert_empty copied
|
2012-01-10 22:48:51 +00:00
|
|
|
ensure
|
|
|
|
clear
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_copying_migrations_without_timestamps_from_2_sources
|
|
|
|
ActiveRecord::Base.timestamped_migrations = false
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/valid"
|
|
|
|
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
|
|
|
|
2012-02-09 12:56:52 +00:00
|
|
|
sources = {}
|
2012-01-10 22:48:51 +00:00
|
|
|
sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
|
|
|
|
sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
|
|
|
|
ActiveRecord::Migration.copy(@migrations_path, sources)
|
2013-11-01 08:49:57 +00:00
|
|
|
assert File.exist?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/6_create_articles.omg.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/7_create_comments.omg.rb")
|
2012-01-10 22:48:51 +00:00
|
|
|
|
|
|
|
files_count = Dir[@migrations_path + "/*.rb"].length
|
|
|
|
ActiveRecord::Migration.copy(@migrations_path, sources)
|
|
|
|
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
|
|
|
ensure
|
|
|
|
clear
|
|
|
|
end
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_copying_migrations_with_timestamps
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
|
|
|
|
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2013-12-12 10:57:06 +00:00
|
|
|
travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
|
2016-08-06 17:44:11 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
|
2013-11-01 08:49:57 +00:00
|
|
|
assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
|
2012-01-10 22:48:51 +00:00
|
|
|
expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
|
|
|
|
@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"]
|
|
|
|
assert_equal expected, copied.map(&:filename)
|
2011-12-08 23:50:39 +00:00
|
|
|
|
2010-07-26 12:06:14 +00:00
|
|
|
files_count = Dir[@migrations_path + "/*.rb"].length
|
2016-08-06 17:44:11 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
|
2010-07-26 12:06:14 +00:00
|
|
|
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
2018-01-25 23:16:57 +00:00
|
|
|
assert_empty copied
|
2010-07-26 12:06:14 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
ensure
|
|
|
|
clear
|
|
|
|
end
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_copying_migrations_with_timestamps_from_2_sources
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
|
|
|
|
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-02-09 12:56:52 +00:00
|
|
|
sources = {}
|
2012-01-10 22:48:51 +00:00
|
|
|
sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
|
|
|
|
sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"
|
|
|
|
|
2013-12-12 10:57:06 +00:00
|
|
|
travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
|
2012-01-10 22:48:51 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, sources)
|
2013-11-01 08:49:57 +00:00
|
|
|
assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/20100726101012_create_articles.omg.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/20100726101013_create_comments.omg.rb")
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal 4, copied.length
|
2010-07-26 12:06:14 +00:00
|
|
|
|
|
|
|
files_count = Dir[@migrations_path + "/*.rb"].length
|
|
|
|
ActiveRecord::Migration.copy(@migrations_path, sources)
|
|
|
|
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
ensure
|
|
|
|
clear
|
|
|
|
end
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_copying_migrations_with_timestamps_to_destination_with_timestamps_in_future
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
|
|
|
|
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2013-12-12 10:57:06 +00:00
|
|
|
travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
|
2016-08-06 17:44:11 +00:00
|
|
|
ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
|
2013-11-01 08:49:57 +00:00
|
|
|
assert File.exist?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
files_count = Dir[@migrations_path + "/*.rb"].length
|
2016-08-06 17:44:11 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
2018-01-25 23:16:57 +00:00
|
|
|
assert_empty copied
|
2010-07-26 12:06:14 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
ensure
|
|
|
|
clear
|
|
|
|
end
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2013-03-09 06:37:26 +00:00
|
|
|
def test_copying_migrations_preserving_magic_comments
|
|
|
|
ActiveRecord::Base.timestamped_migrations = false
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/valid"
|
|
|
|
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
|
|
|
|
2016-08-06 17:44:11 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/magic")
|
2013-11-01 08:49:57 +00:00
|
|
|
assert File.exist?(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")
|
2013-03-09 06:37:26 +00:00
|
|
|
assert_equal [@migrations_path + "/4_currencies_have_symbols.bukkits.rb"], copied.map(&:filename)
|
|
|
|
|
2017-07-01 16:23:28 +00:00
|
|
|
expected = "# frozen_string_literal: true\n# coding: ISO-8859-15\n# This migration comes from bukkits (originally 1)"
|
|
|
|
assert_equal expected, IO.readlines(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")[0..2].join.chomp
|
2013-03-09 06:37:26 +00:00
|
|
|
|
|
|
|
files_count = Dir[@migrations_path + "/*.rb"].length
|
2016-08-06 17:44:11 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/magic")
|
2013-03-09 06:37:26 +00:00
|
|
|
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
2018-01-25 23:16:57 +00:00
|
|
|
assert_empty copied
|
2013-03-09 06:37:26 +00:00
|
|
|
ensure
|
|
|
|
clear
|
|
|
|
end
|
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_skipping_migrations
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
|
|
|
|
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
2012-05-11 19:45:26 +00:00
|
|
|
|
|
|
|
sources = {}
|
2012-01-10 22:48:51 +00:00
|
|
|
sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
|
|
|
|
sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_name_collision"
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
skipped = []
|
|
|
|
on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
|
2016-08-06 17:37:57 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, sources, on_skip: on_skip)
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal 2, copied.length
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal 1, skipped.length
|
|
|
|
assert_equal ["omg PeopleHaveHobbies"], skipped
|
|
|
|
ensure
|
|
|
|
clear
|
|
|
|
end
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_skip_is_not_called_if_migrations_are_from_the_same_plugin
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
|
|
|
|
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-02-09 12:56:52 +00:00
|
|
|
sources = {}
|
2012-01-10 22:48:51 +00:00
|
|
|
sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
|
2010-07-26 12:06:14 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
skipped = []
|
|
|
|
on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
|
2016-08-06 17:37:57 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, sources, on_skip: on_skip)
|
|
|
|
ActiveRecord::Migration.copy(@migrations_path, sources, on_skip: on_skip)
|
2010-07-31 12:43:41 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
assert_equal 2, copied.length
|
|
|
|
assert_equal 0, skipped.length
|
|
|
|
ensure
|
|
|
|
clear
|
|
|
|
end
|
2010-10-09 08:28:46 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_copying_migrations_to_non_existing_directory
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/non_existing"
|
|
|
|
@existing_migrations = []
|
2010-10-09 08:28:46 +00:00
|
|
|
|
2013-12-12 10:57:06 +00:00
|
|
|
travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
|
2016-08-06 17:44:11 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
|
2013-11-01 08:49:57 +00:00
|
|
|
assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
|
2010-10-09 08:28:46 +00:00
|
|
|
assert_equal 2, copied.length
|
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
ensure
|
|
|
|
clear
|
|
|
|
Dir.delete(@migrations_path)
|
|
|
|
end
|
2010-10-09 08:28:46 +00:00
|
|
|
|
2012-01-10 22:48:51 +00:00
|
|
|
def test_copying_migrations_to_empty_directory
|
|
|
|
@migrations_path = MIGRATIONS_ROOT + "/empty"
|
|
|
|
@existing_migrations = []
|
2011-12-08 23:50:39 +00:00
|
|
|
|
2013-12-12 10:57:06 +00:00
|
|
|
travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
|
2016-08-06 17:44:11 +00:00
|
|
|
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
|
2013-11-01 08:49:57 +00:00
|
|
|
assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
|
|
|
|
assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
|
2011-12-08 23:50:39 +00:00
|
|
|
assert_equal 2, copied.length
|
2010-07-31 12:43:41 +00:00
|
|
|
end
|
2012-01-10 22:48:51 +00:00
|
|
|
ensure
|
|
|
|
clear
|
2010-07-26 12:06:14 +00:00
|
|
|
end
|
2013-06-04 11:26:00 +00:00
|
|
|
|
|
|
|
def test_check_pending_with_stdlib_logger
|
|
|
|
old, ActiveRecord::Base.logger = ActiveRecord::Base.logger, ::Logger.new($stdout)
|
|
|
|
quietly do
|
2018-09-25 17:18:20 +00:00
|
|
|
assert_nothing_raised { ActiveRecord::Migration::CheckPending.new(Proc.new { }).call({}) }
|
2013-06-04 11:26:00 +00:00
|
|
|
end
|
|
|
|
ensure
|
|
|
|
ActiveRecord::Base.logger = old
|
|
|
|
end
|
2014-07-15 20:56:27 +00:00
|
|
|
|
2016-03-24 11:03:43 +00:00
|
|
|
def test_unknown_migration_version_should_raise_an_argument_error
|
|
|
|
assert_raise(ArgumentError) { ActiveRecord::Migration[1.0] }
|
|
|
|
end
|
2005-10-29 18:40:49 +00:00
|
|
|
end
|