migration_test.rb 32.3 KB
Newer Older
1
require "cases/helper"
2
require "cases/migration/helper"
3 4
require 'bigdecimal/util'

J
Jeremy Kemper 已提交
5 6
require 'models/person'
require 'models/topic'
7
require 'models/developer'
8 9

require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
10 11
require MIGRATIONS_ROOT + "/rename/1_we_need_things"
require MIGRATIONS_ROOT + "/rename/2_rename_things"
12
require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
13

14
class BigNumber < ActiveRecord::Base; end
15

16
class Reminder < ActiveRecord::Base; end
17

18
class Thing < ActiveRecord::Base; end
19

20 21
class MigrationTest < ActiveRecord::TestCase
  self.use_transactional_fixtures = false
22

23
  fixtures :people
24

25 26
  def setup
    super
A
Akira Matsuda 已提交
27
    %w(reminders people_reminders prefix_reminders_suffix p_things_s).each do |table|
A
Aaron Patterson 已提交
28 29 30
      Reminder.connection.drop_table(table) rescue nil
    end
    Reminder.reset_column_information
31 32
    ActiveRecord::Migration.verbose = true
    ActiveRecord::Migration.message_count = 0
A
Akira Matsuda 已提交
33
    ActiveRecord::Base.connection.schema_cache.clear!
34
  end
35

36
  def teardown
37 38 39
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""

40 41
    ActiveRecord::Base.connection.initialize_schema_migrations_table
    ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
42

43
    %w(things awesome_things prefix_things_suffix p_awesome_things_s ).each do |table|
44
      Thing.connection.drop_table(table) rescue nil
45
    end
46
    Thing.reset_column_information
47

48 49 50
    %w(reminders people_reminders prefix_reminders_suffix).each do |table|
      Reminder.connection.drop_table(table) rescue nil
    end
51
    Reminder.reset_table_name
52
    Reminder.reset_column_information
53

54 55 56 57 58 59 60 61 62
    %w(last_name key bio age height wealth birthday favorite_day
       moment_of_truth male administrator funny).each do |column|
      Person.connection.remove_column('people', column) rescue nil
    end
    Person.connection.remove_column("people", "first_name") rescue nil
    Person.connection.remove_column("people", "middle_name") rescue nil
    Person.connection.add_column("people", "first_name", :string, :limit => 40)
    Person.reset_column_information
  end
63

64 65
  def test_migrator_versions
    migrations_path = MIGRATIONS_ROOT + "/valid"
66
    old_path = ActiveRecord::Migrator.migrations_paths
67 68 69 70 71
    ActiveRecord::Migrator.migrations_paths = migrations_path

    ActiveRecord::Migrator.up(migrations_path)
    assert_equal 3, ActiveRecord::Migrator.current_version
    assert_equal 3, ActiveRecord::Migrator.last_version
72
    assert_equal false, ActiveRecord::Migrator.needs_migration?
73 74 75 76

    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
    assert_equal 0, ActiveRecord::Migrator.current_version
    assert_equal 3, ActiveRecord::Migrator.last_version
77
    assert_equal true, ActiveRecord::Migrator.needs_migration?
78 79
  ensure
    ActiveRecord::Migrator.migrations_paths = old_path
80 81
  end

82 83 84 85
  def test_migration_version
    ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947)
  end

86 87 88
  def test_create_table_with_force_true_does_not_drop_nonexisting_table
    if Person.connection.table_exists?(:testings2)
      Person.connection.drop_table :testings2
89 90
    end

91 92 93
    # 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
94 95 96

    assert_not_equal temp_conn, Person.connection

97 98
    temp_conn.create_table :testings2, :force => true do |t|
      t.column :foo, :string
99
    end
100 101 102
  ensure
    Person.connection.drop_table :testings2 rescue nil
  end
103

104 105 106
  def connection
    ActiveRecord::Base.connection
  end
107

108 109 110 111
  def test_migration_instance_has_connection
    migration = Class.new(ActiveRecord::Migration).new
    assert_equal connection, migration.connection
  end
112

113 114 115 116 117 118 119 120
  def test_method_missing_delegates_to_connection
    migration = Class.new(ActiveRecord::Migration) {
      def connection
        Class.new {
          def create_table; "hi mom!"; end
        }.new
      end
    }.new
121

122
    assert_equal "hi mom!", migration.method_missing(:create_table)
123
  end
124

125 126 127 128 129 130 131 132 133 134 135 136 137 138
  def test_add_table_with_decimals
    Person.connection.drop_table :big_numbers rescue nil

    assert !BigNumber.table_exists?
    GiveMeBigNumbers.up

    assert BigNumber.create(
      :bank_balance => 1586.43,
      :big_bank_balance => BigDecimal("1000234000567.95"),
      :world_population => 6000000000,
      :my_house_population => 3,
      :value_of_e => BigDecimal("2.7182818284590452353602875")
    )

139
    b = BigNumber.first
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    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

    # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
    # is_a?(Bignum)
    assert_kind_of Integer, b.world_population
    assert_equal 6000000000, b.world_population
    assert_kind_of Fixnum, b.my_house_population
    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
      # - SQLite2 has the default behavior of preserving all data sent in,
      # so this happens there too
      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
      assert_kind_of Fixnum, b.value_of_e
      assert_equal 2, b.value_of_e
    end

    GiveMeBigNumbers.down
183
    assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first }
184
  end
185

186
  def test_filtering_migrations
187
    assert_no_column Person, :last_name
188
    assert !Reminder.table_exists?
189

190 191
    name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", &name_filter)
192

193
    assert_column Person, :last_name
194
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
195

196 197
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", &name_filter)

198
    assert_no_column Person, :last_name
199
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
200 201 202 203 204 205 206
  end

  class MockMigration < ActiveRecord::Migration
    attr_reader :went_up, :went_down
    def initialize
      @went_up   = false
      @went_down = false
207 208
    end

209 210 211 212
    def up
      @went_up = true
      super
    end
213

214 215 216
    def down
      @went_down = true
      super
217
    end
218
  end
219

220 221 222 223
  def test_instance_based_migration_up
    migration = MockMigration.new
    assert !migration.went_up, 'have not gone up'
    assert !migration.went_down, 'have not gone down'
224

225 226 227 228
    migration.migrate :up
    assert migration.went_up, 'have gone up'
    assert !migration.went_down, 'have not gone down'
  end
229

230 231 232 233
  def test_instance_based_migration_down
    migration = MockMigration.new
    assert !migration.went_up, 'have not gone up'
    assert !migration.went_down, 'have not gone down'
234

235 236 237 238
    migration.migrate :down
    assert !migration.went_up, 'have gone up'
    assert migration.went_down, 'have not gone down'
  end
239

240 241 242 243 244 245 246 247 248 249 250
  if ActiveRecord::Base.connection.supports_ddl_transactions?
    def test_migrator_one_up_with_exception_and_rollback
      assert_no_column Person, :last_name

      migration = Class.new(ActiveRecord::Migration) {
        def version; 100 end
        def migrate(x)
          add_column "people", "last_name", :string
          raise 'Something broke'
        end
      }.new
251

252
      migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
253

254
      e = assert_raise(StandardError) { migrator.migrate }
255

256
      assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
257

258 259
      assert_no_column Person, :last_name,
        "On error, the Migrator should revert schema changes but it did not."
B
bondarev 已提交
260 261
    end

262 263
    def test_migrator_one_up_with_exception_and_rollback_using_run
      assert_no_column Person, :last_name
B
bondarev 已提交
264

265 266 267 268 269 270 271
      migration = Class.new(ActiveRecord::Migration) {
        def version; 100 end
        def migrate(x)
          add_column "people", "last_name", :string
          raise 'Something broke'
        end
      }.new
B
bondarev 已提交
272

273
      migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
B
bondarev 已提交
274

275
      e = assert_raise(StandardError) { migrator.run }
B
bondarev 已提交
276

277
      assert_equal "An error has occurred, this migration was canceled:\n\nSomething broke", e.message
B
bondarev 已提交
278

279 280
      assert_no_column Person, :last_name,
        "On error, the Migrator should revert schema changes but it did not."
281 282
    end

283 284
    def test_migration_without_transaction
      assert_no_column Person, :last_name
285

286 287
      migration = Class.new(ActiveRecord::Migration) {
        self.disable_ddl_transaction!
288

289 290 291 292 293 294
        def version; 101 end
        def migrate(x)
          add_column "people", "last_name", :string
          raise 'Something broke'
        end
      }.new
295

296 297 298
      migrator = ActiveRecord::Migrator.new(:up, [migration], 101)
      e = assert_raise(StandardError) { migrator.migrate }
      assert_equal "An error has occurred, all later migrations canceled:\n\nSomething broke", e.message
299

300 301 302 303 304 305 306
      assert_column Person, :last_name,
        "without ddl transactions, the Migrator should not rollback on error but it did."
    ensure
      Person.reset_column_information
      if Person.column_names.include?('last_name')
        Person.connection.remove_column('people', 'last_name')
      end
307
    end
308
  end
309

310
  def test_schema_migrations_table_name
311 312 313
    original_schema_migrations_table_name = ActiveRecord::Migrator.schema_migrations_table_name

    assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
314 315 316 317
    ActiveRecord::Base.table_name_prefix = "prefix_"
    ActiveRecord::Base.table_name_suffix = "_suffix"
    Reminder.reset_table_name
    assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
318 319 320
    ActiveRecord::Base.schema_migrations_table_name = "changed"
    Reminder.reset_table_name
    assert_equal "prefix_changed_suffix", ActiveRecord::Migrator.schema_migrations_table_name
321 322 323
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""
    Reminder.reset_table_name
324 325 326 327
    assert_equal "changed", ActiveRecord::Migrator.schema_migrations_table_name
  ensure
    ActiveRecord::Base.schema_migrations_table_name = original_schema_migrations_table_name
    Reminder.reset_table_name
328 329
  end

330
  def test_proper_table_name_on_migrator
331
    reminder_class = new_isolated_reminder_class
332 333 334 335 336 337 338
    assert_deprecated do
      assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
    end
    assert_deprecated do
      assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
    end
    assert_deprecated do
339
      assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(reminder_class)
340
    end
341
    reminder_class.reset_table_name
342
    assert_deprecated do
343
      assert_equal reminder_class.table_name, ActiveRecord::Migrator.proper_table_name(reminder_class)
344
    end
345 346 347 348

    # Use the model's own prefix/suffix if a model is given
    ActiveRecord::Base.table_name_prefix = "ARprefix_"
    ActiveRecord::Base.table_name_suffix = "_ARsuffix"
349 350 351
    reminder_class.table_name_prefix = 'prefix_'
    reminder_class.table_name_suffix = '_suffix'
    reminder_class.reset_table_name
352
    assert_deprecated do
353
      assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(reminder_class)
354
    end
355 356 357
    reminder_class.table_name_prefix = ''
    reminder_class.table_name_suffix = ''
    reminder_class.reset_table_name
358 359 360 361

    # Use AR::Base's prefix/suffix if string or symbol is given
    ActiveRecord::Base.table_name_prefix = "prefix_"
    ActiveRecord::Base.table_name_suffix = "_suffix"
362
    reminder_class.reset_table_name
363 364 365 366 367 368
    assert_deprecated do
      assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
    end
    assert_deprecated do
      assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
    end
369 370
  end

371
  def test_proper_table_name_on_migration
372
    reminder_class = new_isolated_reminder_class
373 374 375
    migration = ActiveRecord::Migration.new
    assert_equal "table", migration.proper_table_name('table')
    assert_equal "table", migration.proper_table_name(:table)
376 377 378
    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)
379 380 381 382

    # Use the model's own prefix/suffix if a model is given
    ActiveRecord::Base.table_name_prefix = "ARprefix_"
    ActiveRecord::Base.table_name_suffix = "_ARsuffix"
383 384 385 386 387 388 389
    reminder_class.table_name_prefix = 'prefix_'
    reminder_class.table_name_suffix = '_suffix'
    reminder_class.reset_table_name
    assert_equal "prefix_reminders_suffix", migration.proper_table_name(reminder_class)
    reminder_class.table_name_prefix = ''
    reminder_class.table_name_suffix = ''
    reminder_class.reset_table_name
390 391 392 393

    # Use AR::Base's prefix/suffix if string or symbol is given
    ActiveRecord::Base.table_name_prefix = "prefix_"
    ActiveRecord::Base.table_name_suffix = "_suffix"
394
    reminder_class.reset_table_name
395 396 397 398
    assert_equal "prefix_table_suffix", migration.proper_table_name('table', migration.table_name_options)
    assert_equal "prefix_table_suffix", migration.proper_table_name(:table, migration.table_name_options)
  end

399 400
  def test_rename_table_with_prefix_and_suffix
    assert !Thing.table_exists?
401 402
    ActiveRecord::Base.table_name_prefix = 'p_'
    ActiveRecord::Base.table_name_suffix = '_s'
403 404 405 406 407
    Thing.reset_table_name
    Thing.reset_sequence_name
    WeNeedThings.up

    assert Thing.create("content" => "hello world")
408
    assert_equal "hello world", Thing.first.content
409 410

    RenameThings.up
411
    Thing.table_name = "p_awesome_things_s"
412

413
    assert_equal "hello world", Thing.first.content
414 415 416 417 418 419 420 421 422 423 424 425 426
  ensure
    Thing.reset_table_name
    Thing.reset_sequence_name
  end

  def test_add_drop_table_with_prefix_and_suffix
    assert !Reminder.table_exists?
    ActiveRecord::Base.table_name_prefix = 'prefix_'
    ActiveRecord::Base.table_name_suffix = '_suffix'
    Reminder.reset_table_name
    Reminder.reset_sequence_name
    WeNeedReminders.up
    assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
427
    assert_equal "hello world", Reminder.first.content
428 429

    WeNeedReminders.down
430
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
431 432 433
  ensure
    Reminder.reset_sequence_name
  end
434

435 436
  def test_create_table_with_binary_column
    Person.connection.drop_table :binary_testings rescue nil
437

438 439
    assert_nothing_raised {
      Person.connection.create_table :binary_testings do |t|
440
        t.column "data", :binary, :null => false
441
      end
442 443 444 445
    }

    columns = Person.connection.columns(:binary_testings)
    data_column = columns.detect { |c| c.name == "data" }
446

J
Jon Leighton 已提交
447
    assert_nil data_column.default
448

449 450 451
    Person.connection.drop_table :binary_testings rescue nil
  end

452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
  def test_create_table_with_query
    Person.connection.drop_table :table_from_query_testings rescue nil
    Person.connection.create_table(:person, force: true)

    Person.connection.create_table :table_from_query_testings, as: "SELECT id FROM person"

    columns = Person.connection.columns(:table_from_query_testings)
    assert_equal 1, columns.length
    assert_equal "id", columns.first.name

    Person.connection.drop_table :table_from_query_testings rescue nil
  end

  def test_create_table_with_query_from_relation
    Person.connection.drop_table :table_from_query_testings rescue nil
    Person.connection.create_table(:person, force: true)

    Person.connection.create_table :table_from_query_testings, as: Person.select(:id)

    columns = Person.connection.columns(:table_from_query_testings)
    assert_equal 1, columns.length
    assert_equal "id", columns.first.name

    Person.connection.drop_table :table_from_query_testings rescue nil
  end

478 479 480 481 482 483 484 485 486 487 488
  if current_adapter? :OracleAdapter
    def test_create_table_with_custom_sequence_name
      # table name is 29 chars, the standard sequence name will
      # be 33 chars and should be shortened
      assert_nothing_raised do
        begin
          Person.connection.create_table :table_with_name_thats_just_ok do |t|
            t.column :foo, :string, :null => false
          end
        ensure
          Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
489 490 491
        end
      end

492 493 494 495 496 497 498
      # should be all good w/ a custom sequence name
      assert_nothing_raised do
        begin
          Person.connection.create_table :table_with_name_thats_just_ok,
            :sequence_name => 'suitably_short_seq' do |t|
            t.column :foo, :string, :null => false
          end
499

500
          Person.connection.execute("select suitably_short_seq.nextval from dual")
501

502 503 504 505
        ensure
          Person.connection.drop_table :table_with_name_thats_just_ok,
            :sequence_name => 'suitably_short_seq' rescue nil
        end
506 507
      end

508 509 510 511
      # confirm the custom sequence got dropped
      assert_raise(ActiveRecord::StatementInvalid) do
        Person.connection.execute("select suitably_short_seq.nextval from dual")
      end
512
    end
513
  end
514

515 516 517 518 519 520 521
  if current_adapter?(:MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter)
    def test_out_of_range_limit_should_raise
      Person.connection.drop_table :test_limits rescue nil
      assert_raise(ActiveRecord::ActiveRecordError, "integer limit didn't raise") do
        Person.connection.create_table :test_integer_limits, :force => true do |t|
          t.column :bigone, :integer, :limit => 10
        end
522 523
      end

524 525 526 527 528
      unless current_adapter?(:PostgreSQLAdapter)
        assert_raise(ActiveRecord::ActiveRecordError, "text limit didn't raise") do
          Person.connection.create_table :test_text_limits, :force => true do |t|
            t.column :bigtext, :text, :limit => 0xfffffffff
          end
529 530 531
        end
      end

532 533
      Person.connection.drop_table :test_limits rescue nil
    end
534 535
  end

536
  protected
537 538 539 540 541 542 543 544
    # 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
545
end
546

547 548 549 550 551
class ReservedWordsMigrationTest < ActiveRecord::TestCase
  def test_drop_index_from_table_named_values
    connection = Person.connection
    connection.create_table :values, :force => true do |t|
      t.integer :value
552
    end
553

554 555 556
    assert_nothing_raised do
      connection.add_index :values, :value
      connection.remove_index :values, :column => :value
557
    end
558

559 560 561 562
    connection.drop_table :values rescue nil
  end
end

563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
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

    assert_nothing_raised ArgumentError do
      connection.add_index :values, :value, name: 'a_different_name'
      connection.remove_index :values, column: :value, name: 'a_different_name'
    end

    connection.drop_table :values rescue nil
  end
end

579 580 581 582 583
if ActiveRecord::Base.connection.supports_bulk_alter?
  class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
    def setup
      @connection = Person.connection
      @connection.create_table(:delete_me, :force => true) {|t| }
A
Akira Matsuda 已提交
584 585
      Person.reset_column_information
      Person.reset_sequence_name
586
    end
587

588 589 590
    def teardown
      Person.connection.drop_table(:delete_me) rescue nil
    end
591

592 593
    def test_adding_multiple_columns
      assert_queries(1) do
594
        with_bulk_change_table do |t|
595
          t.column :name, :string
596
          t.string :qualification, :experience
597 598 599
          t.integer :age, :default => 0
          t.date :birthdate
          t.timestamps
600
        end
601
      end
602

603 604 605 606
      assert_equal 8, columns.size
      [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
      assert_equal 0, column(:age).default
    end
607

608 609 610
    def test_removing_columns
      with_bulk_change_table do |t|
        t.string :qualification, :experience
611 612
      end

613
      [:qualification, :experience].each {|c| assert column(c) }
614

615 616 617 618
      assert_queries(1) do
        with_bulk_change_table do |t|
          t.remove :qualification, :experience
          t.string :qualification_experience
619
        end
620
      end
621

622 623 624
      [:qualification, :experience].each {|c| assert ! column(c) }
      assert column(:qualification_experience)
    end
625

626 627 628 629 630
    def test_adding_indexes
      with_bulk_change_table do |t|
        t.string :username
        t.string :name
        t.integer :age
631 632
      end

633 634
      # Adding an index fires a query every time to check if an index already exists or not
      assert_queries(3) do
635
        with_bulk_change_table do |t|
636 637
          t.index :username, :unique => true, :name => :awesome_username_index
          t.index [:name, :age]
638
        end
639
      end
640

641
      assert_equal 2, indexes.size
642

643 644 645
      name_age_index = index(:index_delete_me_on_name_and_age)
      assert_equal ['name', 'age'].sort, name_age_index.columns.sort
      assert ! name_age_index.unique
646

647 648
      assert index(:awesome_username_index).unique
    end
649

650 651 652 653
    def test_removing_index
      with_bulk_change_table do |t|
        t.string :name
        t.index :name
654
      end
655

656 657 658
      assert index(:index_delete_me_on_name)

      assert_queries(3) do
659
        with_bulk_change_table do |t|
660 661
          t.remove_index :name
          t.index :name, :name => :new_name_index, :unique => true
662
        end
663
      end
664

665
      assert ! index(:index_delete_me_on_name)
666

667 668 669
      new_name_index = index(:new_name_index)
      assert new_name_index.unique
    end
670

671 672 673 674
    def test_changing_columns
      with_bulk_change_table do |t|
        t.string :name
        t.date :birthdate
675
      end
676

677 678
      assert ! column(:name).default
      assert_equal :date, column(:birthdate).type
679

680 681 682
      # One query for columns (delete_me table)
      # One query for primary key (delete_me table)
      # One query to do the bulk change
683
      assert_queries(3, :ignore_none => true) do
684 685 686
        with_bulk_change_table do |t|
          t.change :name, :string, :default => 'NONAME'
          t.change :birthdate, :datetime
687
        end
688 689
      end

690 691 692
      assert_equal 'NONAME', column(:name).default
      assert_equal :datetime, column(:birthdate).type
    end
693

694
    protected
695

696 697 698
    def with_bulk_change_table
      # Reset columns/indexes cache as we're changing the table
      @columns = @indexes = nil
699

700 701
      Person.connection.change_table(:delete_me, :bulk => true) do |t|
        yield t
702
      end
703
    end
704

705 706 707
    def column(name)
      columns.detect {|c| c.name == name.to_s }
    end
708

709 710
    def columns
      @columns ||= Person.connection.columns('delete_me')
711 712
    end

713 714
    def index(name)
      indexes.detect {|i| i.name == name.to_s }
715 716
    end

717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
    def indexes
      @indexes ||= Person.connection.indexes('delete_me')
    end
  end # AlterTableMigrationsTest

end

class CopyMigrationsTest < ActiveRecord::TestCase
  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"]

    copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
A
Arun Agrawal 已提交
740 741
    assert File.exist?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
    assert File.exist?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
    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)"
    assert_equal expected, IO.readlines(@migrations_path + "/4_people_have_hobbies.bukkits.rb")[0].chomp

    files_count = Dir[@migrations_path + "/*.rb"].length
    copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
    assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
    assert copied.empty?
  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"]

760
    sources = {}
761 762 763
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
    sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
    ActiveRecord::Migration.copy(@migrations_path, sources)
A
Arun Agrawal 已提交
764 765 766 767
    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")
768 769 770 771 772 773 774

    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
775

776 777 778
  def test_copying_migrations_with_timestamps
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
779

780
    travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
781
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
A
Arun Agrawal 已提交
782 783
      assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
784 785 786
      expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
                  @migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"]
      assert_equal expected, copied.map(&:filename)
787

788
      files_count = Dir[@migrations_path + "/*.rb"].length
789
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
790 791 792
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
      assert copied.empty?
    end
793 794 795
  ensure
    clear
  end
796

797 798 799
  def test_copying_migrations_with_timestamps_from_2_sources
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
800

801
    sources = {}
802 803 804
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
    sources[:omg]     = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"

805
    travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
806
      copied = ActiveRecord::Migration.copy(@migrations_path, sources)
A
Arun Agrawal 已提交
807 808 809 810
      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")
811
      assert_equal 4, copied.length
812 813 814 815 816

      files_count = Dir[@migrations_path + "/*.rb"].length
      ActiveRecord::Migration.copy(@migrations_path, sources)
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
    end
817 818 819
  ensure
    clear
  end
820

821 822 823
  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"]
824

825
    travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
826
      ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
A
Arun Agrawal 已提交
827 828
      assert File.exist?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
829

830 831 832 833
      files_count = Dir[@migrations_path + "/*.rb"].length
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
      assert copied.empty?
834
    end
835 836 837
  ensure
    clear
  end
838

839 840 841 842 843 844
  def test_copying_migrations_preserving_magic_comments
    ActiveRecord::Base.timestamped_migrations = false
    @migrations_path = MIGRATIONS_ROOT + "/valid"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]

    copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/magic"})
A
Arun Agrawal 已提交
845
    assert File.exist?(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")
846 847 848 849 850 851 852 853 854 855 856 857 858
    assert_equal [@migrations_path + "/4_currencies_have_symbols.bukkits.rb"], copied.map(&:filename)

    expected = "# 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..1].join.chomp

    files_count = Dir[@migrations_path + "/*.rb"].length
    copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/magic"})
    assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
    assert copied.empty?
  ensure
    clear
  end

859 860 861
  def test_skipping_migrations
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
862 863

    sources = {}
864 865
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
    sources[:omg]     = MIGRATIONS_ROOT + "/to_copy_with_name_collision"
866

867 868 869 870
    skipped = []
    on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
    copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
    assert_equal 2, copied.length
871

872 873 874 875 876
    assert_equal 1, skipped.length
    assert_equal ["omg PeopleHaveHobbies"], skipped
  ensure
    clear
  end
877

878 879 880
  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"]
881

882
    sources = {}
883
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
884

885 886 887 888
    skipped = []
    on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
    copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
    ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
889

890 891 892 893 894
    assert_equal 2, copied.length
    assert_equal 0, skipped.length
  ensure
    clear
  end
895

896 897 898
  def test_copying_migrations_to_non_existing_directory
    @migrations_path = MIGRATIONS_ROOT + "/non_existing"
    @existing_migrations = []
899

900
    travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
901
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
A
Arun Agrawal 已提交
902 903
      assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
904 905
      assert_equal 2, copied.length
    end
906 907 908 909
  ensure
    clear
    Dir.delete(@migrations_path)
  end
910

911 912 913
  def test_copying_migrations_to_empty_directory
    @migrations_path = MIGRATIONS_ROOT + "/empty"
    @existing_migrations = []
914

915
    travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
916
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
A
Arun Agrawal 已提交
917 918
      assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
919
      assert_equal 2, copied.length
920
    end
921 922
  ensure
    clear
923
  end
924 925 926 927 928 929 930 931 932

  def test_check_pending_with_stdlib_logger
    old, ActiveRecord::Base.logger = ActiveRecord::Base.logger, ::Logger.new($stdout)
    quietly do
      assert_nothing_raised { ActiveRecord::Migration::CheckPending.new(Proc.new {}).call({}) }
    end
  ensure
    ActiveRecord::Base.logger = old
  end
933
end