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

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

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

13
class BigNumber < ActiveRecord::Base; end
14

15
class Reminder < ActiveRecord::Base; end
16

17
class Thing < ActiveRecord::Base; end
18

19 20 21 22
class ActiveRecord::Migration
  class << self
    attr_accessor :message_count
  end
23

24 25 26
  def puts(text="")
    ActiveRecord::Migration.message_count ||= 0
    ActiveRecord::Migration.message_count += 1
J
Jamis Buck 已提交
27
  end
28
end
J
Jamis Buck 已提交
29

30 31 32 33 34 35 36 37 38 39 40
module ActiveRecord
  class MigrationTest < ActiveRecord::TestCase
    attr_reader :connection

    def setup
      super
      @connection = Base.connection
    end
  end
end

41 42
class MigrationTest < ActiveRecord::TestCase
  self.use_transactional_fixtures = false
43

44
  fixtures :people
45

46 47
  def setup
    super
A
Aaron Patterson 已提交
48 49 50 51
    %w(reminders people_reminders prefix_reminders_suffix).each do |table|
      Reminder.connection.drop_table(table) rescue nil
    end
    Reminder.reset_column_information
52 53 54
    ActiveRecord::Migration.verbose = true
    ActiveRecord::Migration.message_count = 0
  end
55

56 57 58
  def teardown
    ActiveRecord::Base.connection.initialize_schema_migrations_table
    ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
59

60 61
    %w(things awesome_things prefix_things_suffix prefix_awesome_things_suffix).each do |table|
      Thing.connection.drop_table(table) rescue nil
62
    end
63
    Thing.reset_column_information
64

65 66 67 68
    %w(reminders people_reminders prefix_reminders_suffix).each do |table|
      Reminder.connection.drop_table(table) rescue nil
    end
    Reminder.reset_column_information
69

70 71 72 73 74 75 76 77 78
    %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
79

80 81 82
  def test_create_table_with_force_true_does_not_drop_nonexisting_table
    if Person.connection.table_exists?(:testings2)
      Person.connection.drop_table :testings2
83 84
    end

85 86 87
    # 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
88 89 90

    assert_not_equal temp_conn, Person.connection

91 92
    temp_conn.create_table :testings2, :force => true do |t|
      t.column :foo, :string
93
    end
94 95 96
  ensure
    Person.connection.drop_table :testings2 rescue nil
  end
97

98 99 100
  def connection
    ActiveRecord::Base.connection
  end
101

102 103 104 105
  def test_migration_instance_has_connection
    migration = Class.new(ActiveRecord::Migration).new
    assert_equal connection, migration.connection
  end
106

107 108 109 110 111 112 113 114
  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
115

116
    assert_equal "hi mom!", migration.method_missing(:create_table)
117
  end
118

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 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
  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")
    )

    b = BigNumber.find(:first)
    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
    assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
  end
179

180 181 182
  def test_filtering_migrations
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
183

184 185
    name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", &name_filter)
186

187 188 189
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
190

191 192 193 194 195 196 197 198 199 200 201 202
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", &name_filter)

    Person.reset_column_information
    assert !Person.column_methods_hash.include?(:last_name)
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
  end

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

205 206 207 208
    def up
      @went_up = true
      super
    end
209

210 211 212
    def down
      @went_down = true
      super
213
    end
214
  end
215

216 217 218 219
  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'
220

221 222 223 224
    migration.migrate :up
    assert migration.went_up, 'have gone up'
    assert !migration.went_down, 'have not gone down'
  end
225

226 227 228 229
  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'
230

231 232 233 234
    migration.migrate :down
    assert !migration.went_up, 'have gone up'
    assert migration.went_down, 'have not gone down'
  end
235

236 237 238 239
  def test_migrator_one_up_with_exception_and_rollback
    unless ActiveRecord::Base.connection.supports_ddl_transactions?
      skip "not supported on #{ActiveRecord::Base.connection.class}"
    end
240

241
    refute Person.column_methods_hash.include?(:last_name)
242

243 244 245 246 247 248 249
    migration = Struct.new(:name, :version) {
      def migrate(x); raise 'Something broke'; end
    }.new('zomg', 100)

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

    e = assert_raise(StandardError) { migrator.migrate }
250 251 252 253

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

    Person.reset_column_information
254
    refute Person.column_methods_hash.include?(:last_name)
255
  end
256

257 258
  def test_only_loads_pending_migrations
    # migrate up to 1
259
    ActiveRecord::SchemaMigration.create!(:version => '1')
260

261
    proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
262

263 264 265 266 267
    names = proxies.map(&:name)
    assert !names.include?('ValidPeopleHaveLastNames')
    assert names.include?('WeNeedReminders')
    assert names.include?('InnocentJointable')
  end
268

269 270 271
  def test_target_version_zero_should_run_only_once
    # migrate up to 1
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
J
Jamis Buck 已提交
272

273 274 275 276 277 278 279
    # migrate down to 0
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)

    # migrate down to 0 again
    proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
    assert_equal [], proxies
  end
280

281 282 283 284 285 286 287 288 289
  def test_migrator_db_has_no_schema_migrations_table
    # Oracle adapter raises error if semicolon is present as last character
    if current_adapter?(:OracleAdapter)
      ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations")
    else
      ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations;")
    end
    assert_nothing_raised do
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
J
Jamis Buck 已提交
290
    end
291
  end
292

293 294 295 296
  def test_migrator_verbosity
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
    assert_not_equal 0, ActiveRecord::Migration.message_count
    ActiveRecord::Migration.message_count = 0
297

298 299 300 301
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
    assert_not_equal 0, ActiveRecord::Migration.message_count
    ActiveRecord::Migration.message_count = 0
  end
302

303 304 305 306 307 308 309
  def test_migrator_verbosity_off
    ActiveRecord::Migration.verbose = false
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
    assert_equal 0, ActiveRecord::Migration.message_count
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
    assert_equal 0, ActiveRecord::Migration.message_count
  end
310

311 312 313
  def test_migrator_going_down_due_to_version_target
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
314

315 316
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
317

318
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
319

320 321 322 323 324
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
    assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
    assert_equal "hello world", Reminder.find(:first).content
  end
325

326 327 328
  def test_migrator_rollback
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
    assert_equal(3, ActiveRecord::Migrator.current_version)
329

330 331
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(2, ActiveRecord::Migrator.current_version)
332

333 334
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(1, ActiveRecord::Migrator.current_version)
335

336 337
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(0, ActiveRecord::Migrator.current_version)
338

339 340 341
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(0, ActiveRecord::Migrator.current_version)
  end
342

343 344 345
  def test_migrator_forward
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
    assert_equal(1, ActiveRecord::Migrator.current_version)
346

347 348
    ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid", 2)
    assert_equal(3, ActiveRecord::Migrator.current_version)
349

350 351 352
    ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid")
    assert_equal(3, ActiveRecord::Migrator.current_version)
  end
353

354 355 356
  def test_get_all_versions
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
    assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
357

358 359
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
360

361 362
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal([1], ActiveRecord::Migrator.get_all_versions)
363

364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal([], ActiveRecord::Migrator.get_all_versions)
  end

  def test_schema_migrations_table_name
    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
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""
    Reminder.reset_table_name
    assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
  ensure
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""
  end

  def test_proper_table_name
    assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
    assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
    assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
    Reminder.reset_table_name
    assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)

    # Use the model's own prefix/suffix if a model is given
    ActiveRecord::Base.table_name_prefix = "ARprefix_"
    ActiveRecord::Base.table_name_suffix = "_ARsuffix"
    Reminder.table_name_prefix = 'prefix_'
    Reminder.table_name_suffix = '_suffix'
    Reminder.reset_table_name
    assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
    Reminder.table_name_prefix = ''
    Reminder.table_name_suffix = ''
    Reminder.reset_table_name

    # Use AR::Base's prefix/suffix if string or symbol is given
    ActiveRecord::Base.table_name_prefix = "prefix_"
    ActiveRecord::Base.table_name_suffix = "_suffix"
    Reminder.reset_table_name
    assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
    assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""
    Reminder.reset_table_name
  end

  def test_rename_table_with_prefix_and_suffix
    assert !Thing.table_exists?
    ActiveRecord::Base.table_name_prefix = 'prefix_'
    ActiveRecord::Base.table_name_suffix = '_suffix'
    Thing.reset_table_name
    Thing.reset_sequence_name
    WeNeedThings.up

    assert Thing.create("content" => "hello world")
    assert_equal "hello world", Thing.find(:first).content

    RenameThings.up
    Thing.table_name = "prefix_awesome_things_suffix"

    assert_equal "hello world", Thing.find(:first).content
  ensure
    ActiveRecord::Base.table_name_prefix = ''
    ActiveRecord::Base.table_name_suffix = ''
    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)
    assert_equal "hello world", Reminder.find(:first).content

    WeNeedReminders.down
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
  ensure
    ActiveRecord::Base.table_name_prefix = ''
    ActiveRecord::Base.table_name_suffix = ''
    Reminder.reset_table_name
    Reminder.reset_sequence_name
  end
451

452 453
  def test_create_table_with_binary_column
    Person.connection.drop_table :binary_testings rescue nil
454

455 456 457
    assert_nothing_raised {
      Person.connection.create_table :binary_testings do |t|
        t.column "data", :binary, :null => false
458
      end
459 460 461 462
    }

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

464 465 466 467
    if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
      assert_equal '', data_column.default
    else
      assert_nil data_column.default
468
    end
469

470 471 472 473
    Person.connection.drop_table :binary_testings rescue nil
  end

  def test_create_table_with_custom_sequence_name
474
    skip "not supported" unless current_adapter? :OracleAdapter
475

476 477 478 479 480 481
    # 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
482
        end
483 484
      ensure
        Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
485
      end
486
    end
487

488 489 490 491 492 493
    # 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
494 495 496
        end

        Person.connection.execute("select suitably_short_seq.nextval from dual")
497 498

      ensure
499 500
        Person.connection.drop_table :table_with_name_thats_just_ok,
                                     :sequence_name => 'suitably_short_seq' rescue nil
501
      end
502
    end
503

504 505 506 507
    # confirm the custom sequence got dropped
    assert_raise(ActiveRecord::StatementInvalid) do
      Person.connection.execute("select suitably_short_seq.nextval from dual")
    end
508
  end
509

510 511 512 513
  protected
    def with_env_tz(new_tz = 'US/Eastern')
      old_tz, ENV['TZ'] = ENV['TZ'], new_tz
      yield
514
    ensure
515
      old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
516
    end
517
end
518

519 520 521 522 523
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
524
    end
525

526 527 528
    assert_nothing_raised do
      connection.add_index :values, :value
      connection.remove_index :values, :column => :value
529
    end
530

531 532 533 534 535 536 537 538 539
    connection.drop_table :values rescue nil
  end
end


class ChangeTableMigrationsTest < ActiveRecord::TestCase
  def setup
    @connection = Person.connection
    @connection.create_table :delete_me, :force => true do |t|
540
    end
541
  end
542

543 544 545 546 547 548 549 550
  def teardown
    Person.connection.drop_table :delete_me rescue nil
  end

  def test_references_column_type_adds_id
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
      t.references :customer
551
    end
552
  end
553

554 555 556 557
  def test_remove_references_column_type_removes_id
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, 'customer_id')
      t.remove_references :customer
558
    end
559
  end
560

561 562 563 564
  def test_add_belongs_to_works_like_add_references
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
      t.belongs_to :customer
565
    end
566
  end
567

568 569 570 571
  def test_remove_belongs_to_works_like_remove_references
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, 'customer_id')
      t.remove_belongs_to :customer
572
    end
573
  end
574

575 576 577 578 579
  def test_references_column_type_with_polymorphic_adds_type
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {})
      @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {})
      t.references :taggable, :polymorphic => true
580
    end
581
  end
582

583 584 585 586 587
  def test_remove_references_column_type_with_polymorphic_removes_type
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
      @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
      t.remove_references :taggable, :polymorphic => true
588
    end
589
  end
590

591 592 593 594 595
  def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {:null => false})
      @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {:null => false})
      t.references :taggable, :polymorphic => true, :null => false
596
    end
597
  end
598

599 600 601 602 603
  def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
      @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
      t.remove_references :taggable, :polymorphic => true, :null => false
604
    end
605
  end
606

607 608 609 610
  def test_timestamps_creates_updated_at_and_created_at
    with_change_table do |t|
      @connection.expects(:add_timestamps).with(:delete_me)
      t.timestamps
611
    end
612
  end
613

614 615 616 617
  def test_remove_timestamps_creates_updated_at_and_created_at
    with_change_table do |t|
      @connection.expects(:remove_timestamps).with(:delete_me)
      t.remove_timestamps
618
    end
619
  end
620

621 622 623 624 625 626 627
  def string_column
    if current_adapter?(:PostgreSQLAdapter)
      "character varying(255)"
    elsif current_adapter?(:OracleAdapter)
      'VARCHAR2(255)'
    else
      'varchar(255)'
628
    end
629
  end
630

631 632 633 634 635 636 637
  def integer_column
    if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
      'int(11)'
    elsif current_adapter?(:OracleAdapter)
      'NUMBER(38)'
    else
      'integer'
638
    end
639
  end
640

641 642 643 644 645
  def test_integer_creates_integer_column
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, :foo, integer_column, {})
      @connection.expects(:add_column).with(:delete_me, :bar, integer_column, {})
      t.integer :foo, :bar
646
    end
647
  end
648

649 650 651 652 653
  def test_string_creates_string_column
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, :foo, string_column, {})
      @connection.expects(:add_column).with(:delete_me, :bar, string_column, {})
      t.string :foo, :bar
654
    end
655
  end
656

657 658 659 660
  def test_column_creates_column
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
      t.column :bar, :integer
661
    end
662
  end
663

664 665 666 667
  def test_column_creates_column_with_options
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, :bar, :integer, {:null => false})
      t.column :bar, :integer, :null => false
668
    end
669
  end
670

671 672 673 674
  def test_index_creates_index
    with_change_table do |t|
      @connection.expects(:add_index).with(:delete_me, :bar, {})
      t.index :bar
675
    end
676
  end
677

678 679 680 681
  def test_index_creates_index_with_options
    with_change_table do |t|
      @connection.expects(:add_index).with(:delete_me, :bar, {:unique => true})
      t.index :bar, :unique => true
682
    end
683
  end
684

685 686 687 688
  def test_index_exists
    with_change_table do |t|
      @connection.expects(:index_exists?).with(:delete_me, :bar, {})
      t.index_exists?(:bar)
689
    end
690
  end
691

692 693 694 695
  def test_index_exists_with_options
    with_change_table do |t|
      @connection.expects(:index_exists?).with(:delete_me, :bar, {:unique => true})
      t.index_exists?(:bar, :unique => true)
696
    end
697
  end
698

699 700 701 702
  def test_change_changes_column
    with_change_table do |t|
      @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
      t.change :bar, :string
703
    end
704
  end
705

706 707 708 709
  def test_change_changes_column_with_options
    with_change_table do |t|
      @connection.expects(:change_column).with(:delete_me, :bar, :string, {:null => true})
      t.change :bar, :string, :null => true
710
    end
711
  end
712

713 714 715 716
  def test_change_default_changes_column
    with_change_table do |t|
      @connection.expects(:change_column_default).with(:delete_me, :bar, :string)
      t.change_default :bar, :string
717
    end
718
  end
719

720 721 722 723
  def test_remove_drops_single_column
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, [:bar])
      t.remove :bar
724
    end
725
  end
726

727 728 729 730
  def test_remove_drops_multiple_columns
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
      t.remove :bar, :baz
731
    end
732
  end
733

734 735 736 737
  def test_remove_index_removes_index_with_options
    with_change_table do |t|
      @connection.expects(:remove_index).with(:delete_me, {:unique => true})
      t.remove_index :unique => true
738 739
    end
  end
740

741 742 743 744 745 746
  def test_rename_renames_column
    with_change_table do |t|
      @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
      t.rename :bar, :baz
    end
  end
747

748 749 750 751 752 753 754
  protected
  def with_change_table
    Person.connection.change_table :delete_me do |t|
      yield t
    end
  end
end
755

756 757 758 759 760 761
if ActiveRecord::Base.connection.supports_bulk_alter?
  class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
    def setup
      @connection = Person.connection
      @connection.create_table(:delete_me, :force => true) {|t| }
    end
762

763 764 765
    def teardown
      Person.connection.drop_table(:delete_me) rescue nil
    end
766

767 768
    def test_adding_multiple_columns
      assert_queries(1) do
769
        with_bulk_change_table do |t|
770
          t.column :name, :string
771
          t.string :qualification, :experience
772 773 774
          t.integer :age, :default => 0
          t.date :birthdate
          t.timestamps
775
        end
776
      end
777

778 779 780 781
      assert_equal 8, columns.size
      [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
      assert_equal 0, column(:age).default
    end
782

783 784 785
    def test_removing_columns
      with_bulk_change_table do |t|
        t.string :qualification, :experience
786 787
      end

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

790 791 792 793
      assert_queries(1) do
        with_bulk_change_table do |t|
          t.remove :qualification, :experience
          t.string :qualification_experience
794
        end
795
      end
796

797 798 799
      [:qualification, :experience].each {|c| assert ! column(c) }
      assert column(:qualification_experience)
    end
800

801 802 803 804 805
    def test_adding_indexes
      with_bulk_change_table do |t|
        t.string :username
        t.string :name
        t.integer :age
806 807
      end

808 809
      # Adding an index fires a query every time to check if an index already exists or not
      assert_queries(3) do
810
        with_bulk_change_table do |t|
811 812
          t.index :username, :unique => true, :name => :awesome_username_index
          t.index [:name, :age]
813
        end
814
      end
815

816
      assert_equal 2, indexes.size
817

818 819 820
      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
821

822 823
      assert index(:awesome_username_index).unique
    end
824

825 826 827 828
    def test_removing_index
      with_bulk_change_table do |t|
        t.string :name
        t.index :name
829
      end
830

831 832 833
      assert index(:index_delete_me_on_name)

      assert_queries(3) do
834
        with_bulk_change_table do |t|
835 836
          t.remove_index :name
          t.index :name, :name => :new_name_index, :unique => true
837
        end
838
      end
839

840
      assert ! index(:index_delete_me_on_name)
841

842 843 844
      new_name_index = index(:new_name_index)
      assert new_name_index.unique
    end
845

846 847 848 849
    def test_changing_columns
      with_bulk_change_table do |t|
        t.string :name
        t.date :birthdate
850
      end
851

852 853
      assert ! column(:name).default
      assert_equal :date, column(:birthdate).type
854

855 856 857 858 859 860 861
      # One query for columns (delete_me table)
      # One query for primary key (delete_me table)
      # One query to do the bulk change
      assert_queries(3) do
        with_bulk_change_table do |t|
          t.change :name, :string, :default => 'NONAME'
          t.change :birthdate, :datetime
862
        end
863 864
      end

865 866 867
      assert_equal 'NONAME', column(:name).default
      assert_equal :datetime, column(:birthdate).type
    end
868

869
    protected
870

871 872 873
    def with_bulk_change_table
      # Reset columns/indexes cache as we're changing the table
      @columns = @indexes = nil
874

875 876
      Person.connection.change_table(:delete_me, :bulk => true) do |t|
        yield t
877
      end
878
    end
879

880 881 882
    def column(name)
      columns.detect {|c| c.name == name.to_s }
    end
883

884 885
    def columns
      @columns ||= Person.connection.columns('delete_me')
886 887
    end

888 889
    def index(name)
      indexes.detect {|i| i.name == name.to_s }
890 891
    end

892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
    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"})
    assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
    assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
    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"]

    sources = ActiveSupport::OrderedHash.new
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
    sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
    ActiveRecord::Migration.copy(@migrations_path, sources)
    assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
    assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
    assert File.exists?(@migrations_path + "/6_create_articles.omg.rb")
    assert File.exists?(@migrations_path + "/7_create_comments.omg.rb")

    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
950

951 952 953
  def test_copying_migrations_with_timestamps
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
954

955 956 957 958 959 960 961
    Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
      assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
      expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
                  @migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"]
      assert_equal expected, copied.map(&:filename)
962

963
      files_count = Dir[@migrations_path + "/*.rb"].length
964
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
965 966 967
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
      assert copied.empty?
    end
968 969 970
  ensure
    clear
  end
971

972 973 974
  def test_copying_migrations_with_timestamps_from_2_sources
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
975

976 977 978 979 980 981 982 983 984 985 986
    sources = ActiveSupport::OrderedHash.new
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
    sources[:omg]     = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"

    Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
      copied = ActiveRecord::Migration.copy(@migrations_path, sources)
      assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
      assert File.exists?(@migrations_path + "/20100726101012_create_articles.omg.rb")
      assert File.exists?(@migrations_path + "/20100726101013_create_comments.omg.rb")
      assert_equal 4, copied.length
987 988 989 990 991

      files_count = Dir[@migrations_path + "/*.rb"].length
      ActiveRecord::Migration.copy(@migrations_path, sources)
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
    end
992 993 994
  ensure
    clear
  end
995

996 997 998
  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"]
999

1000 1001 1002 1003
    Time.travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
      ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
      assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
      assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
1004

1005 1006 1007 1008
      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?
1009
    end
1010 1011 1012
  ensure
    clear
  end
1013

1014 1015 1016
  def test_skipping_migrations
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1017

1018 1019 1020
    sources = ActiveSupport::OrderedHash.new
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
    sources[:omg]     = MIGRATIONS_ROOT + "/to_copy_with_name_collision"
1021

1022 1023 1024 1025
    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
1026

1027 1028 1029 1030 1031
    assert_equal 1, skipped.length
    assert_equal ["omg PeopleHaveHobbies"], skipped
  ensure
    clear
  end
1032

1033 1034 1035
  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"]
1036

1037 1038
    sources = ActiveSupport::OrderedHash.new
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
1039

1040 1041 1042 1043
    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)
1044

1045 1046 1047 1048 1049
    assert_equal 2, copied.length
    assert_equal 0, skipped.length
  ensure
    clear
  end
1050

1051 1052 1053
  def test_copying_migrations_to_non_existing_directory
    @migrations_path = MIGRATIONS_ROOT + "/non_existing"
    @existing_migrations = []
1054

1055 1056 1057 1058
    Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
      assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
1059 1060
      assert_equal 2, copied.length
    end
1061 1062 1063 1064
  ensure
    clear
    Dir.delete(@migrations_path)
  end
1065

1066 1067 1068
  def test_copying_migrations_to_empty_directory
    @migrations_path = MIGRATIONS_ROOT + "/empty"
    @existing_migrations = []
1069

1070 1071 1072 1073
    Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
      assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
1074
      assert_equal 2, copied.length
1075
    end
1076 1077
  ensure
    clear
1078
  end
1079
end