migration_test.rb 37.6 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 48 49 50
  def setup
    super
    ActiveRecord::Migration.verbose = true
    ActiveRecord::Migration.message_count = 0
  end
51

52 53 54
  def teardown
    ActiveRecord::Base.connection.initialize_schema_migrations_table
    ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
55

56 57
    %w(things awesome_things prefix_things_suffix prefix_awesome_things_suffix).each do |table|
      Thing.connection.drop_table(table) rescue nil
58
    end
59
    Thing.reset_column_information
60

61 62 63 64
    %w(reminders people_reminders prefix_reminders_suffix).each do |table|
      Reminder.connection.drop_table(table) rescue nil
    end
    Reminder.reset_column_information
65

66 67 68 69 70 71 72 73 74
    %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
75

76 77 78
  def test_create_table_with_force_true_does_not_drop_nonexisting_table
    if Person.connection.table_exists?(:testings2)
      Person.connection.drop_table :testings2
79 80
    end

81 82 83
    # 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
84 85 86

    assert_not_equal temp_conn, Person.connection

87 88
    temp_conn.create_table :testings2, :force => true do |t|
      t.column :foo, :string
89
    end
90 91 92
  ensure
    Person.connection.drop_table :testings2 rescue nil
  end
93

94 95
  def test_add_table
    assert !Reminder.table_exists?
96

97
    WeNeedReminders.up
98

99 100
    assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
    assert_equal "hello world", Reminder.find(:first).content
101

102 103 104
    WeNeedReminders.down
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
  end
105

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

167 168 169
  def test_migrator
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
170

171
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
172

173 174 175 176 177
    assert_equal 3, ActiveRecord::Migrator.current_version
    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
178

179
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
180

181 182 183 184 185
    assert_equal 0, ActiveRecord::Migrator.current_version
    Person.reset_column_information
    assert !Person.column_methods_hash.include?(:last_name)
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
  end
186

187 188 189
  def test_filtering_migrations
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
190

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

194 195 196
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
197

198 199 200 201 202 203 204 205 206 207 208 209
    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
210 211
    end

212 213 214 215
    def up
      @went_up = true
      super
    end
216

217 218 219
    def down
      @went_down = true
      super
220
    end
221
  end
222

223 224 225 226
  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'
227

228 229 230 231
    migration.migrate :up
    assert migration.went_up, 'have gone up'
    assert !migration.went_down, 'have not gone down'
  end
232

233 234 235 236
  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'
237

238 239 240 241
    migration.migrate :down
    assert !migration.went_up, 'have gone up'
    assert migration.went_down, 'have not gone down'
  end
242

243 244 245
  def test_migrator_one_up
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
246

247
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
248

249 250 251
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
252

253
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
254

255 256 257
    assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
    assert_equal "hello world", Reminder.find(:first).content
  end
258

259 260
  def test_migrator_one_down
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
261

262
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
263

264 265 266 267
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
  end
268

269 270 271
  def test_migrator_one_up_one_down
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
272

273 274 275
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
  end
276

277 278 279 280 281 282
  def test_migrator_double_up
    assert_equal(0, ActiveRecord::Migrator.current_version)
    ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
    assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) }
    assert_equal(1, ActiveRecord::Migrator.current_version)
  end
283

284 285 286 287 288 289 290
  def test_migrator_double_down
    assert_equal(0, ActiveRecord::Migrator.current_version)
    ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
    ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1)
    assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) }
    assert_equal(0, ActiveRecord::Migrator.current_version)
  end
291

292 293 294 295
  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
296

297
    refute Person.column_methods_hash.include?(:last_name)
298

299 300 301 302 303 304 305
    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 }
306 307 308 309

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

    Person.reset_column_information
310
    refute Person.column_methods_hash.include?(:last_name)
311
  end
312

313 314
  def test_only_loads_pending_migrations
    # migrate up to 1
315
    ActiveRecord::SchemaMigration.create!(:version => '1')
316

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

319 320 321 322 323
    names = proxies.map(&:name)
    assert !names.include?('ValidPeopleHaveLastNames')
    assert names.include?('WeNeedReminders')
    assert names.include?('InnocentJointable')
  end
324

325 326 327
  def test_target_version_zero_should_run_only_once
    # migrate up to 1
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
J
Jamis Buck 已提交
328

329 330 331 332 333 334 335
    # 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
336

337 338 339 340 341 342 343 344 345
  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 已提交
346
    end
347
  end
348

349 350 351 352
  def test_migrator_verbosity
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
    assert_not_equal 0, ActiveRecord::Migration.message_count
    ActiveRecord::Migration.message_count = 0
353

354 355 356 357
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
    assert_not_equal 0, ActiveRecord::Migration.message_count
    ActiveRecord::Migration.message_count = 0
  end
358

359 360 361 362 363 364 365
  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
366

367 368 369
  def test_migrator_going_down_due_to_version_target
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
370

371 372
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
373

374
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
375

376 377 378 379 380
    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
381

382 383 384
  def test_migrator_rollback
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
    assert_equal(3, ActiveRecord::Migrator.current_version)
385

386 387
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(2, ActiveRecord::Migrator.current_version)
388

389 390
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(1, ActiveRecord::Migrator.current_version)
391

392 393
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(0, ActiveRecord::Migrator.current_version)
394

395 396 397
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(0, ActiveRecord::Migrator.current_version)
  end
398

399 400 401
  def test_migrator_forward
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
    assert_equal(1, ActiveRecord::Migrator.current_version)
402

403 404
    ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid", 2)
    assert_equal(3, ActiveRecord::Migrator.current_version)
405

406 407 408
    ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid")
    assert_equal(3, ActiveRecord::Migrator.current_version)
  end
409

410 411 412
  def test_get_all_versions
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
    assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
413

414 415
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
416

417 418
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal([1], ActiveRecord::Migrator.get_all_versions)
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 451 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 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
    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
507

508 509
  def test_create_table_with_binary_column
    Person.connection.drop_table :binary_testings rescue nil
510

511 512 513
    assert_nothing_raised {
      Person.connection.create_table :binary_testings do |t|
        t.column "data", :binary, :null => false
514
      end
515 516 517 518
    }

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

520 521 522 523
    if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
      assert_equal '', data_column.default
    else
      assert_nil data_column.default
524
    end
525

526 527 528 529
    Person.connection.drop_table :binary_testings rescue nil
  end

  def test_create_table_with_custom_sequence_name
530
    skip "not supported" unless current_adapter? :OracleAdapter
531

532 533 534 535 536 537
    # 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
538
        end
539 540
      ensure
        Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
541
      end
542
    end
543

544 545 546 547 548 549
    # 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
550 551 552
        end

        Person.connection.execute("select suitably_short_seq.nextval from dual")
553 554

      ensure
555 556
        Person.connection.drop_table :table_with_name_thats_just_ok,
                                     :sequence_name => 'suitably_short_seq' rescue nil
557
      end
558
    end
559

560 561 562 563
    # confirm the custom sequence got dropped
    assert_raise(ActiveRecord::StatementInvalid) do
      Person.connection.execute("select suitably_short_seq.nextval from dual")
    end
564
  end
565

566 567 568 569
  protected
    def with_env_tz(new_tz = 'US/Eastern')
      old_tz, ENV['TZ'] = ENV['TZ'], new_tz
      yield
570
    ensure
571
      old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
572
    end
573
end
574

575 576 577 578 579
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
580
    end
581

582 583 584
    assert_nothing_raised do
      connection.add_index :values, :value
      connection.remove_index :values, :column => :value
585
    end
586

587 588 589 590 591 592 593 594 595
    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|
596
    end
597
  end
598

599 600 601 602 603 604 605 606
  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
607
    end
608
  end
609

610 611 612 613
  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
614
    end
615
  end
616

617 618 619 620
  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
621
    end
622
  end
623

624 625 626 627
  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
628
    end
629
  end
630

631 632 633 634 635
  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
636
    end
637
  end
638

639 640 641 642 643
  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
644
    end
645
  end
646

647 648 649 650 651
  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
652
    end
653
  end
654

655 656 657 658 659
  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
660
    end
661
  end
662

663 664 665 666
  def test_timestamps_creates_updated_at_and_created_at
    with_change_table do |t|
      @connection.expects(:add_timestamps).with(:delete_me)
      t.timestamps
667
    end
668
  end
669

670 671 672 673
  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
674
    end
675
  end
676

677 678 679 680 681 682 683
  def string_column
    if current_adapter?(:PostgreSQLAdapter)
      "character varying(255)"
    elsif current_adapter?(:OracleAdapter)
      'VARCHAR2(255)'
    else
      'varchar(255)'
684
    end
685
  end
686

687 688 689 690 691 692 693
  def integer_column
    if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
      'int(11)'
    elsif current_adapter?(:OracleAdapter)
      'NUMBER(38)'
    else
      'integer'
694
    end
695
  end
696

697 698 699 700 701
  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
702
    end
703
  end
704

705 706 707 708 709
  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
710
    end
711
  end
712

713 714 715 716
  def test_column_creates_column
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
      t.column :bar, :integer
717
    end
718
  end
719

720 721 722 723
  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
724
    end
725
  end
726

727 728 729 730
  def test_index_creates_index
    with_change_table do |t|
      @connection.expects(:add_index).with(:delete_me, :bar, {})
      t.index :bar
731
    end
732
  end
733

734 735 736 737
  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
738
    end
739
  end
740

741 742 743 744
  def test_index_exists
    with_change_table do |t|
      @connection.expects(:index_exists?).with(:delete_me, :bar, {})
      t.index_exists?(:bar)
745
    end
746
  end
747

748 749 750 751
  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)
752
    end
753
  end
754

755 756 757 758
  def test_change_changes_column
    with_change_table do |t|
      @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
      t.change :bar, :string
759
    end
760
  end
761

762 763 764 765
  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
766
    end
767
  end
768

769 770 771 772
  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
773
    end
774
  end
775

776 777 778 779
  def test_remove_drops_single_column
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, [:bar])
      t.remove :bar
780
    end
781
  end
782

783 784 785 786
  def test_remove_drops_multiple_columns
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
      t.remove :bar, :baz
787
    end
788
  end
789

790 791 792 793
  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
794 795
    end
  end
796

797 798 799 800 801 802
  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
803

804 805 806 807 808 809 810
  protected
  def with_change_table
    Person.connection.change_table :delete_me do |t|
      yield t
    end
  end
end
811

812 813 814 815 816 817
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
818

819 820 821
    def teardown
      Person.connection.drop_table(:delete_me) rescue nil
    end
822

823 824
    def test_adding_multiple_columns
      assert_queries(1) do
825
        with_bulk_change_table do |t|
826
          t.column :name, :string
827
          t.string :qualification, :experience
828 829 830
          t.integer :age, :default => 0
          t.date :birthdate
          t.timestamps
831
        end
832
      end
833

834 835 836 837
      assert_equal 8, columns.size
      [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
      assert_equal 0, column(:age).default
    end
838

839 840 841
    def test_removing_columns
      with_bulk_change_table do |t|
        t.string :qualification, :experience
842 843
      end

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

846 847 848 849
      assert_queries(1) do
        with_bulk_change_table do |t|
          t.remove :qualification, :experience
          t.string :qualification_experience
850
        end
851
      end
852

853 854 855
      [:qualification, :experience].each {|c| assert ! column(c) }
      assert column(:qualification_experience)
    end
856

857 858 859 860 861
    def test_adding_indexes
      with_bulk_change_table do |t|
        t.string :username
        t.string :name
        t.integer :age
862 863
      end

864 865
      # Adding an index fires a query every time to check if an index already exists or not
      assert_queries(3) do
866
        with_bulk_change_table do |t|
867 868
          t.index :username, :unique => true, :name => :awesome_username_index
          t.index [:name, :age]
869
        end
870
      end
871

872
      assert_equal 2, indexes.size
873

874 875 876
      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
877

878 879
      assert index(:awesome_username_index).unique
    end
880

881 882 883 884
    def test_removing_index
      with_bulk_change_table do |t|
        t.string :name
        t.index :name
885
      end
886

887 888 889
      assert index(:index_delete_me_on_name)

      assert_queries(3) do
890
        with_bulk_change_table do |t|
891 892
          t.remove_index :name
          t.index :name, :name => :new_name_index, :unique => true
893
        end
894
      end
895

896
      assert ! index(:index_delete_me_on_name)
897

898 899 900
      new_name_index = index(:new_name_index)
      assert new_name_index.unique
    end
901

902 903 904 905
    def test_changing_columns
      with_bulk_change_table do |t|
        t.string :name
        t.date :birthdate
906
      end
907

908 909
      assert ! column(:name).default
      assert_equal :date, column(:birthdate).type
910

911 912 913 914 915 916 917
      # 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
918
        end
919 920
      end

921 922 923
      assert_equal 'NONAME', column(:name).default
      assert_equal :datetime, column(:birthdate).type
    end
924

925
    protected
926

927 928 929
    def with_bulk_change_table
      # Reset columns/indexes cache as we're changing the table
      @columns = @indexes = nil
930

931 932
      Person.connection.change_table(:delete_me, :bulk => true) do |t|
        yield t
933
      end
934
    end
935

936 937 938
    def column(name)
      columns.detect {|c| c.name == name.to_s }
    end
939

940 941
    def columns
      @columns ||= Person.connection.columns('delete_me')
942 943
    end

944 945
    def index(name)
      indexes.detect {|i| i.name == name.to_s }
946 947
    end

948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
    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
1006

1007 1008 1009
  def test_copying_migrations_with_timestamps
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1010

1011 1012 1013 1014 1015 1016 1017
    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)
1018

1019
      files_count = Dir[@migrations_path + "/*.rb"].length
1020
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
1021 1022 1023
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
      assert copied.empty?
    end
1024 1025 1026
  ensure
    clear
  end
1027

1028 1029 1030
  def test_copying_migrations_with_timestamps_from_2_sources
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1031

1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
    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
1043 1044 1045 1046 1047

      files_count = Dir[@migrations_path + "/*.rb"].length
      ActiveRecord::Migration.copy(@migrations_path, sources)
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
    end
1048 1049 1050
  ensure
    clear
  end
1051

1052 1053 1054
  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"]
1055

1056 1057 1058 1059
    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")
1060

1061 1062 1063 1064
      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?
1065
    end
1066 1067 1068
  ensure
    clear
  end
1069

1070 1071 1072
  def test_skipping_migrations
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1073

1074 1075 1076
    sources = ActiveSupport::OrderedHash.new
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
    sources[:omg]     = MIGRATIONS_ROOT + "/to_copy_with_name_collision"
1077

1078 1079 1080 1081
    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
1082

1083 1084 1085 1086 1087
    assert_equal 1, skipped.length
    assert_equal ["omg PeopleHaveHobbies"], skipped
  ensure
    clear
  end
1088

1089 1090 1091
  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"]
1092

1093 1094
    sources = ActiveSupport::OrderedHash.new
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
1095

1096 1097 1098 1099
    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)
1100

1101 1102 1103 1104 1105
    assert_equal 2, copied.length
    assert_equal 0, skipped.length
  ensure
    clear
  end
1106

1107 1108 1109
  def test_copying_migrations_to_non_existing_directory
    @migrations_path = MIGRATIONS_ROOT + "/non_existing"
    @existing_migrations = []
1110

1111 1112 1113 1114
    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")
1115 1116
      assert_equal 2, copied.length
    end
1117 1118 1119 1120
  ensure
    clear
    Dir.delete(@migrations_path)
  end
1121

1122 1123 1124
  def test_copying_migrations_to_empty_directory
    @migrations_path = MIGRATIONS_ROOT + "/empty"
    @existing_migrations = []
1125

1126 1127 1128 1129
    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")
1130
      assert_equal 2, copied.length
1131
    end
1132 1133
  ensure
    clear
1134
  end
1135
end