migration_test.rb 67.8 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
class MigrationTest < ActiveRecord::TestCase
  self.use_transactional_fixtures = false
32

33
  fixtures :people
34

35 36 37 38 39
  def setup
    super
    ActiveRecord::Migration.verbose = true
    ActiveRecord::Migration.message_count = 0
  end
40

41 42 43
  def teardown
    ActiveRecord::Base.connection.initialize_schema_migrations_table
    ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
44

45 46
    %w(things awesome_things prefix_things_suffix prefix_awesome_things_suffix).each do |table|
      Thing.connection.drop_table(table) rescue nil
47
    end
48
    Thing.reset_column_information
49

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

55 56 57 58 59 60 61 62 63
    %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
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
  def test_add_index
    # Limit size of last_name and key columns to support Firebird index limitations
    Person.connection.add_column "people", "last_name", :string, :limit => 100
    Person.connection.add_column "people", "key", :string, :limit => 100
    Person.connection.add_column "people", "administrator", :boolean

    assert_nothing_raised { Person.connection.add_index("people", "last_name") }
    assert_nothing_raised { Person.connection.remove_index("people", "last_name") }

    # Orcl nds shrt indx nms.  Sybs 2.
    # OpenBase does not have named indexes.  You must specify a single column name
    unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
      assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
      assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
      # Oracle adapter cannot have specified index name larger than 30 characters
      # Oracle adapter is shortening index name when just column list is given
      unless current_adapter?(:OracleAdapter)
82
        assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
83
        assert_nothing_raised { Person.connection.remove_index("people", :name => :index_people_on_last_name_and_first_name) }
84
        assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
85
        assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
86
      end
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
      assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
      assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
      assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) }
      assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
      assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) }
      assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) }
      assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) }
      assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
      assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) }
      assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
    end

    # quoting
    # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
    # OpenBase does not have named indexes.  You must specify a single column name
    unless current_adapter?(:OpenBaseAdapter)
      Person.update_all "#{Person.connection.quote_column_name 'key'}=#{Person.connection.quote_column_name 'id'}" #some databases (including sqlite2 won't add a unique index if existing data non unique)
      assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
      assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
    end

    # Sybase adapter does not support indexes on :boolean columns
    # OpenBase does not have named indexes.  You must specify a single column
    unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
      assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
      assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
    end

    # Selected adapters support index sort order
    if current_adapter?(:SQLite3Adapter, :MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter)
      assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :order => {:last_name => :desc}) }
      assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) }
      assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :order => {:last_name => :desc}) }
      assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
      assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :order => {:last_name => :desc, :first_name => :asc}) }
      assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
      assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :order => :desc) }
      assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
    end
  end
127

128 129 130 131 132 133
  def test_index_symbol_names
    assert_nothing_raised { Person.connection.add_index :people, :primary_contact_id, :name => :symbol_index_name }
    assert Person.connection.index_exists?(:people, :primary_contact_id, :name => :symbol_index_name)
    assert_nothing_raised { Person.connection.remove_index :people, :name => :symbol_index_name }
    assert !Person.connection.index_exists?(:people, :primary_contact_id, :name => :symbol_index_name)
  end
J
Jeremy Kemper 已提交
134

135 136 137 138 139 140 141 142 143
  def test_add_index_length_limit
    good_index_name = 'x' * Person.connection.index_name_length
    too_long_index_name = good_index_name + 'x'
    assert_raise(ArgumentError)  { Person.connection.add_index("people", "first_name", :name => too_long_index_name) }
    assert !Person.connection.index_name_exists?("people", too_long_index_name, false)
    assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) }
    assert Person.connection.index_name_exists?("people", good_index_name, false)
    Person.connection.remove_index("people", :name => good_index_name)
  end
144

145 146 147 148
  def test_remove_nonexistent_index
    # we do this by name, so OpenBase is a wash as noted above
    unless current_adapter?(:OpenBaseAdapter)
      assert_raise(ArgumentError) { Person.connection.remove_index("people", "no_such_index") }
149
    end
150
  end
151

152 153 154 155 156 157 158 159
  def test_rename_index
    unless current_adapter?(:OpenBaseAdapter)
      # keep the names short to make Oracle and similar behave
      Person.connection.add_index('people', [:first_name], :name => 'old_idx')
      assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_idx') }
      # if the adapter doesn't support the indexes call, pick defaults that let the test pass
      assert !Person.connection.index_name_exists?('people', 'old_idx', false)
      assert Person.connection.index_name_exists?('people', 'new_idx', true)
160
    end
161
  end
162

163 164 165 166
  def test_double_add_index
    unless current_adapter?(:OpenBaseAdapter)
      Person.connection.add_index('people', [:first_name], :name => 'some_idx')
      assert_raise(ArgumentError) { Person.connection.add_index('people', [:first_name], :name => 'some_idx') }
167
    end
168
  end
169

170 171 172
  def test_create_table_with_force_true_does_not_drop_nonexisting_table
    if Person.connection.table_exists?(:testings2)
      Person.connection.drop_table :testings2
173 174
    end

175 176 177 178 179 180
    # 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
    temp_conn.expects(:drop_table).never
    temp_conn.create_table :testings2, :force => true do |t|
      t.column :foo, :string
181
    end
182 183 184
  ensure
    Person.connection.drop_table :testings2 rescue nil
  end
185

186 187 188 189 190
  # We specifically do a manual INSERT here, and then test only the SELECT
  # functionality. This allows us to more easily catch INSERT being broken,
  # but SELECT actually working fine.
  def test_native_decimal_insert_manual_vs_automatic
    correct_value = '0012345678901234567890.0123456789'.to_d
191

192 193 194
    Person.delete_all
    Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
    Person.reset_column_information
195

196 197 198 199 200 201 202 203 204
    # Do a manual insertion
    if current_adapter?(:OracleAdapter)
      Person.connection.execute "insert into people (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, sysdate, sysdate)"
    elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
      Person.connection.execute "insert into people (wealth, created_at, updated_at) values ('12345678901234567890.0123456789', 0, 0)"
    elsif current_adapter?(:PostgreSQLAdapter)
      Person.connection.execute "insert into people (wealth, created_at, updated_at) values (12345678901234567890.0123456789, now(), now())"
    else
      Person.connection.execute "insert into people (wealth, created_at, updated_at) values (12345678901234567890.0123456789, 0, 0)"
205 206
    end

207 208 209
    # SELECT
    row = Person.find(:first)
    assert_kind_of BigDecimal, row.wealth
210

211 212 213 214
    # If this assert fails, that means the SELECT is broken!
    unless current_adapter?(:SQLite3Adapter)
      assert_equal correct_value, row.wealth
    end
215

216 217
    # Reset to old state
    Person.delete_all
218

219 220
    # Now use the Rails insertion
    assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
221

222 223 224
    # SELECT
    row = Person.find(:first)
    assert_kind_of BigDecimal, row.wealth
225

226 227 228 229
    # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
    unless current_adapter?(:SQLite3Adapter)
      assert_equal correct_value, row.wealth
    end
230

231 232 233 234
    # Reset to old state
    Person.connection.del_column "people", "wealth" rescue nil
    Person.reset_column_information
  end
235

236 237 238
  def test_add_column_with_precision_and_scale
    Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
    Person.reset_column_information
239

240 241 242 243 244 245 246 247 248 249 250 251 252
    wealth_column = Person.columns_hash['wealth']
    assert_equal 9, wealth_column.precision
    assert_equal 7, wealth_column.scale
  end

  # Test SQLite adapter specifically for decimal types with precision and scale
  # attributes, since these need to be maintained in schema but aren't actually
  # used in SQLite itself
  if current_adapter?(:SQLite3Adapter)
    def test_change_column_with_new_precision_and_scale
      Person.delete_all
      Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
      Person.reset_column_information
253

254
      Person.connection.change_column 'people', 'wealth', :decimal, :precision => 12, :scale => 8
255
      Person.reset_column_information
256 257 258 259

      wealth_column = Person.columns_hash['wealth']
      assert_equal 12, wealth_column.precision
      assert_equal 8, wealth_column.scale
260 261
    end

262 263 264
    def test_change_column_preserve_other_column_precision_and_scale
      Person.delete_all
      Person.connection.add_column 'people', 'last_name', :string
265 266 267 268 269 270
      Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
      Person.reset_column_information

      wealth_column = Person.columns_hash['wealth']
      assert_equal 9, wealth_column.precision
      assert_equal 7, wealth_column.scale
271

272 273
      Person.connection.change_column 'people', 'last_name', :string, :null => false
      Person.reset_column_information
274

275 276 277 278 279
      wealth_column = Person.columns_hash['wealth']
      assert_equal 9, wealth_column.precision
      assert_equal 7, wealth_column.scale
    end
  end
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
  def test_native_types
    Person.delete_all
    Person.connection.add_column "people", "last_name", :string
    Person.connection.add_column "people", "bio", :text
    Person.connection.add_column "people", "age", :integer
    Person.connection.add_column "people", "height", :float
    Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
    Person.connection.add_column "people", "birthday", :datetime
    Person.connection.add_column "people", "favorite_day", :date
    Person.connection.add_column "people", "moment_of_truth", :datetime
    Person.connection.add_column "people", "male", :boolean
    Person.reset_column_information

    assert_nothing_raised do
      Person.create :first_name => 'bob', :last_name => 'bobsen',
        :bio => "I was born ....", :age => 18, :height => 1.78,
        :wealth => BigDecimal.new("12345678901234567890.0123456789"),
        :birthday => 18.years.ago, :favorite_day => 10.days.ago,
        :moment_of_truth => "1782-10-10 21:40:18", :male => true
    end

    bob = Person.find(:first)
    assert_equal 'bob', bob.first_name
    assert_equal 'bobsen', bob.last_name
    assert_equal "I was born ....", bob.bio
    assert_equal 18, bob.age

    # Test for 30 significant digits (beyond the 16 of float), 10 of them
    # after the decimal place.

    unless current_adapter?(:SQLite3Adapter)
      assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
    end

    assert_equal true, bob.male?

    assert_equal String, bob.first_name.class
    assert_equal String, bob.last_name.class
    assert_equal String, bob.bio.class
    assert_equal Fixnum, bob.age.class
    assert_equal Time, bob.birthday.class

    if current_adapter?(:OracleAdapter, :SybaseAdapter)
      # Sybase, and Oracle don't differentiate between date/time
      assert_equal Time, bob.favorite_day.class
    else
      assert_equal Date, bob.favorite_day.class
    end

    # Oracle adapter stores Time or DateTime with timezone value already in _before_type_cast column
    # therefore no timezone change is done afterwards when default timezone is changed
    unless current_adapter?(:OracleAdapter)
      # Test DateTime column and defaults, including timezone.
      # FIXME: moment of truth may be Time on 64-bit platforms.
      if bob.moment_of_truth.is_a?(DateTime)

        with_env_tz 'US/Eastern' do
          bob.reload
          assert_equal DateTime.local_offset, bob.moment_of_truth.offset
          assert_not_equal 0, bob.moment_of_truth.offset
          assert_not_equal "Z", bob.moment_of_truth.zone
          # US/Eastern is -5 hours from GMT
          assert_equal Rational(-5, 24), bob.moment_of_truth.offset
          assert_match(/\A-05:?00\Z/, bob.moment_of_truth.zone) #ruby 1.8.6 uses HH:MM, prior versions use HHMM
          assert_equal DateTime::ITALY, bob.moment_of_truth.start
        end
347 348 349
      end
    end

350 351 352
    assert_instance_of TrueClass, bob.male?
    assert_kind_of BigDecimal, bob.wealth
  end
353

354 355 356
  if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
    def test_unabstracted_database_dependent_types
      Person.delete_all
357

358 359 360 361 362 363 364
      ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
      Person.reset_column_information
      assert_match(/tinyint/, Person.columns_hash['intelligence_quotient'].sql_type)
    ensure
      ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
    end
  end
365

366 367
  def test_add_remove_single_field_using_string_arguments
    assert !Person.column_methods_hash.include?(:last_name)
J
Jeremy Kemper 已提交
368

369
    ActiveRecord::Migration.add_column 'people', 'last_name', :string
J
Jeremy Kemper 已提交
370

371 372
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
373

374
    ActiveRecord::Migration.remove_column 'people', 'last_name'
375

376 377 378
    Person.reset_column_information
    assert !Person.column_methods_hash.include?(:last_name)
  end
379

380 381
  def test_add_remove_single_field_using_symbol_arguments
    assert !Person.column_methods_hash.include?(:last_name)
382

383
    ActiveRecord::Migration.add_column :people, :last_name, :string
384

385 386
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
387

388
    ActiveRecord::Migration.remove_column :people, :last_name
389

390 391 392
    Person.reset_column_information
    assert !Person.column_methods_hash.include?(:last_name)
  end
393

394 395
  def test_add_rename
    Person.delete_all
396

397 398
    begin
      Person.connection.add_column "people", "girlfriend", :string
399
      Person.reset_column_information
400
      Person.create :girlfriend => 'bobette'
401

402
      Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
403 404

      Person.reset_column_information
405
      bob = Person.find(:first)
406

407 408 409 410 411
      assert_equal "bobette", bob.exgirlfriend
    ensure
      Person.connection.remove_column("people", "girlfriend") rescue nil
      Person.connection.remove_column("people", "exgirlfriend") rescue nil
    end
412

413
  end
414

415 416 417 418
  def test_rename_column_using_symbol_arguments
    begin
      names_before = Person.find(:all).map(&:first_name)
      Person.connection.rename_column :people, :first_name, :nick_name
419
      Person.reset_column_information
420 421 422 423 424 425 426
      assert Person.column_names.include?("nick_name")
      assert_equal names_before, Person.find(:all).map(&:nick_name)
    ensure
      Person.connection.remove_column("people","nick_name")
      Person.connection.add_column("people","first_name", :string)
    end
  end
427

428 429 430 431
  def test_rename_column
    begin
      names_before = Person.find(:all).map(&:first_name)
      Person.connection.rename_column "people", "first_name", "nick_name"
432
      Person.reset_column_information
433 434 435 436 437
      assert Person.column_names.include?("nick_name")
      assert_equal names_before, Person.find(:all).map(&:nick_name)
    ensure
      Person.connection.remove_column("people","nick_name")
      Person.connection.add_column("people","first_name", :string)
438
    end
439
  end
440

441 442 443 444 445 446 447 448 449 450 451 452
  def test_rename_column_preserves_default_value_not_null
    begin
      default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default
      assert_equal 70000, default_before
      Developer.connection.rename_column "developers", "salary", "anual_salary"
      Developer.reset_column_information
      assert Developer.column_names.include?("anual_salary")
      default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default
      assert_equal 70000, default_after
    ensure
      Developer.connection.rename_column "developers", "anual_salary", "salary"
      Developer.reset_column_information
453
    end
454
  end
455

456 457 458
  def test_rename_nonexistent_column
    ActiveRecord::Base.connection.create_table(:hats) do |table|
      table.column :hat_name, :string, :default => nil
459
    end
460 461 462 463
    exception = if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
      ActiveRecord::StatementInvalid
    else
      ActiveRecord::ActiveRecordError
464
    end
465 466
    assert_raise(exception) do
      Person.connection.rename_column "hats", "nonexistent", "should_fail"
467
    end
468 469 470
  ensure
    ActiveRecord::Base.connection.drop_table(:hats)
  end
471

472 473 474 475 476
  def test_rename_column_with_sql_reserved_word
    begin
      assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
      Person.reset_column_information
      assert Person.column_names.include?("group")
477
    ensure
478 479
      Person.connection.remove_column("people", "group") rescue nil
      Person.connection.add_column("people", "first_name", :string) rescue nil
480
    end
481
  end
482

483 484 485 486
  def test_rename_column_with_an_index
    ActiveRecord::Base.connection.create_table(:hats) do |table|
      table.column :hat_name, :string, :limit => 100
      table.column :hat_size, :integer
487
    end
488 489 490
    Person.connection.add_index :hats, :hat_name
    assert_nothing_raised do
      Person.connection.rename_column "hats", "hat_name", "name"
491
    end
492 493 494
  ensure
    ActiveRecord::Base.connection.drop_table(:hats)
  end
495

496 497 498 499
  def test_remove_column_with_index
    ActiveRecord::Base.connection.create_table(:hats) do |table|
      table.column :hat_name, :string, :limit => 100
      table.column :hat_size, :integer
500
    end
501
    ActiveRecord::Base.connection.add_index "hats", "hat_size"
502

503 504 505 506
    assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
  ensure
    ActiveRecord::Base.connection.drop_table(:hats)
  end
507

508 509 510 511 512
  def test_remove_column_with_multi_column_index
    ActiveRecord::Base.connection.create_table(:hats) do |table|
      table.column :hat_name, :string, :limit => 100
      table.column :hat_size, :integer
      table.column :hat_style, :string, :limit => 100
513
    end
514
    ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true
515

516 517 518 519
    assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
  ensure
    ActiveRecord::Base.connection.drop_table(:hats)
  end
520

521 522 523
  def test_remove_column_no_second_parameter_raises_exception
    assert_raise(ArgumentError) { Person.connection.remove_column("funny") }
  end
524

525 526 527 528
  def test_change_type_of_not_null_column
    assert_nothing_raised do
      Topic.connection.change_column "topics", "written_on", :datetime, :null => false
      Topic.reset_column_information
529

530 531
      Topic.connection.change_column "topics", "written_on", :datetime, :null => false
      Topic.reset_column_information
532

533 534
      Topic.connection.change_column "topics", "written_on", :datetime, :null => true
      Topic.reset_column_information
535
    end
536
  end
537

538 539
  if current_adapter?(:SQLite3Adapter)
    def test_rename_table_for_sqlite_should_work_with_reserved_words
540
      begin
541 542 543 544 545
        assert_nothing_raised do
          ActiveRecord::Base.connection.rename_table :references, :old_references
          ActiveRecord::Base.connection.create_table :octopuses do |t|
            t.column :url, :string
          end
546
        end
547 548

        assert_nothing_raised { ActiveRecord::Base.connection.rename_table :octopuses, :references }
549

550
        # Using explicit id in insert for compatibility across all databases
J
Jeremy Kemper 已提交
551
        con = ActiveRecord::Base.connection
552 553 554 555
        assert_nothing_raised do
          con.execute "INSERT INTO 'references' (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://rubyonrails.com')"
        end
        assert_equal 'http://rubyonrails.com', ActiveRecord::Base.connection.select_value("SELECT url FROM 'references' WHERE id=1")
556

557
      ensure
558 559
        ActiveRecord::Base.connection.drop_table :references
        ActiveRecord::Base.connection.rename_table :old_references, :references
560 561
      end
    end
562
  end
J
Jeremy Kemper 已提交
563

564 565 566 567 568 569
  def test_rename_table
    begin
      ActiveRecord::Base.connection.create_table :octopuses do |t|
        t.column :url, :string
      end
      ActiveRecord::Base.connection.rename_table :octopuses, :octopi
J
Jeremy Kemper 已提交
570

571 572 573 574 575
      # Using explicit id in insert for compatibility across all databases
      con = ActiveRecord::Base.connection
      con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
      assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
      con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
576

577
      assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
578

579 580 581
    ensure
      ActiveRecord::Base.connection.drop_table :octopuses rescue nil
      ActiveRecord::Base.connection.drop_table :octopi rescue nil
582
    end
583
  end
584

585 586 587 588 589 590 591 592 593 594 595 596
  def test_change_column_nullability
    Person.delete_all
    Person.connection.add_column "people", "funny", :boolean
    Person.reset_column_information
    assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
    Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
    Person.reset_column_information
    assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
    Person.connection.change_column "people", "funny", :boolean, :null => true
    Person.reset_column_information
    assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
  end
597

598 599 600 601 602 603
  def test_rename_table_with_an_index
    begin
      ActiveRecord::Base.connection.create_table :octopuses do |t|
        t.column :url, :string
      end
      ActiveRecord::Base.connection.add_index :octopuses, :url
604

605
      ActiveRecord::Base.connection.rename_table :octopuses, :octopi
J
Jeremy Kemper 已提交
606

607 608 609 610 611
      # Using explicit id in insert for compatibility across all databases
      con = ActiveRecord::Base.connection
      con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
      assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
      con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
J
Jeremy Kemper 已提交
612

613 614
      assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
      assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
615
    ensure
616 617
      ActiveRecord::Base.connection.drop_table :octopuses rescue nil
      ActiveRecord::Base.connection.drop_table :octopi rescue nil
618
    end
619
  end
620

621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
  def test_change_column
    Person.connection.add_column 'people', 'age', :integer
    label = "test_change_column Columns"
    old_columns = Person.connection.columns(Person.table_name, label)
    assert old_columns.find { |c| c.name == 'age' and c.type == :integer }

    assert_nothing_raised { Person.connection.change_column "people", "age", :string }

    new_columns = Person.connection.columns(Person.table_name, label)
    assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
    assert new_columns.find { |c| c.name == 'age' and c.type == :string }

    old_columns = Topic.connection.columns(Topic.table_name, label)
    assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
    assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
    new_columns = Topic.connection.columns(Topic.table_name, label)
    assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
    assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
    assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
  end
641

642 643 644 645 646 647 648 649 650 651 652 653
  def test_change_column_with_nil_default
    Person.connection.add_column "people", "contributor", :boolean, :default => true
    Person.reset_column_information
    assert Person.new.contributor?

    assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
    Person.reset_column_information
    assert !Person.new.contributor?
    assert_nil Person.new.contributor
  ensure
    Person.connection.remove_column("people", "contributor") rescue nil
  end
J
Jeremy Kemper 已提交
654

655 656 657 658
  def test_change_column_with_new_default
    Person.connection.add_column "people", "administrator", :boolean, :default => true
    Person.reset_column_information
    assert Person.new.administrator?
659

660 661 662 663 664 665
    assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
    Person.reset_column_information
    assert !Person.new.administrator?
  ensure
    Person.connection.remove_column("people", "administrator") rescue nil
  end
666

667 668 669 670 671
  def test_change_column_default
    Person.connection.change_column_default "people", "first_name", "Tester"
    Person.reset_column_information
    assert_equal "Tester", Person.new.first_name
  end
672

673 674 675 676 677
  def test_change_column_default_to_null
    Person.connection.change_column_default "people", "first_name", nil
    Person.reset_column_information
    assert_nil Person.new.first_name
  end
678

679 680
  def test_add_table
    assert !Reminder.table_exists?
681

682
    WeNeedReminders.up
683

684 685
    assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
    assert_equal "hello world", Reminder.find(:first).content
686

687 688 689
    WeNeedReminders.down
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
  end
690

691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
  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
751

752 753 754
  def test_migrator
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
755

756
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
757

758 759 760 761 762
    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
763

764
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
765

766 767 768 769 770
    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
771

772 773 774
  def test_filtering_migrations
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
775

776 777
    name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", &name_filter)
778

779 780 781
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
782

783 784 785 786 787 788 789 790 791 792 793 794
    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
795 796
    end

797 798 799 800
    def up
      @went_up = true
      super
    end
801

802 803 804
    def down
      @went_down = true
      super
805
    end
806
  end
807

808 809 810 811
  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'
812

813 814 815 816
    migration.migrate :up
    assert migration.went_up, 'have gone up'
    assert !migration.went_down, 'have not gone down'
  end
817

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

823 824 825 826
    migration.migrate :down
    assert !migration.went_up, 'have gone up'
    assert migration.went_down, 'have not gone down'
  end
827

828 829 830
  def test_migrator_one_up
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
831

832
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
833

834 835 836
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
837

838
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
839

840 841 842
    assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
    assert_equal "hello world", Reminder.find(:first).content
  end
843

844 845
  def test_migrator_one_down
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
846

847
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
848

849 850 851 852
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
  end
853

854 855 856
  def test_migrator_one_up_one_down
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
857

858 859 860
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
  end
861

862 863 864 865 866 867
  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
868

869 870 871 872 873 874 875
  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
876

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

882
    assert !Person.column_methods_hash.include?(:last_name)
883

884 885
    e = assert_raise(StandardError) do
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
886
    end
887 888 889 890 891

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

    Person.reset_column_information
    assert !Person.column_methods_hash.include?(:last_name)
892
  end
893

894
  def test_finds_migrations
895 896
    list = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid")
    migrations = ActiveRecord::Migrator.new(:up, list).migrations
897

898 899 900
    [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
      assert_equal migrations[i].version, pair.first
      assert_equal migrations[i].name, pair.last
901
    end
902
  end
903

904
  def test_finds_migrations_in_subdirectories
905 906
    list = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid_with_subdirectories")
    migrations = ActiveRecord::Migrator.new(:up, list).migrations
907

908 909 910
    [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
      assert_equal migrations[i].version, pair.first
      assert_equal migrations[i].name, pair.last
911
    end
912
  end
913

914 915
  def test_finds_migrations_from_two_directories
    directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps']
916 917
    list = ActiveRecord::Migrator.migrations directories
    migrations = ActiveRecord::Migrator.new(:up, list).migrations
918

919 920 921 922 923 924 925
    [[20090101010101, "PeopleHaveHobbies"],
     [20090101010202, "PeopleHaveDescriptions"],
     [20100101010101, "ValidWithTimestampsPeopleHaveLastNames"],
     [20100201010101, "ValidWithTimestampsWeNeedReminders"],
     [20100301010101, "ValidWithTimestampsInnocentJointable"]].each_with_index do |pair, i|
      assert_equal pair.first, migrations[i].version
      assert_equal pair.last, migrations[i].name
926
    end
927
  end
928

929 930 931 932
  def test_dump_schema_information_outputs_lexically_ordered_versions
    migration_path = MIGRATIONS_ROOT + '/valid_with_timestamps'
    ActiveRecord::Migrator.run(:up, migration_path, 20100301010101)
    ActiveRecord::Migrator.run(:up, migration_path, 20100201010101)
933

934 935 936
    schema_info = ActiveRecord::Base.connection.dump_schema_information
    assert_match(/20100201010101.*20100301010101/m, schema_info)
  end
937

938 939
  def test_finds_pending_migrations
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1)
940 941
    migration_list = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/interleaved/pass_2")
    migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations
942

943 944 945 946
    assert_equal 1, migrations.size
    assert_equal migrations[0].version, 3
    assert_equal migrations[0].name, 'InterleavedInnocentJointable'
  end
947

948 949 950 951 952 953
  def test_deprecated_constructor
    assert_deprecated do
      ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2")
    end
  end

954 955 956
  def test_relative_migrations
    list = Dir.chdir(MIGRATIONS_ROOT) do
      ActiveRecord::Migrator.up("valid/", 1)
957 958
    end

959 960 961 962 963
    migration_proxy = list.find { |item|
      item.name == 'ValidPeopleHaveLastNames'
    }
    assert migration_proxy, 'should find pending migration'
  end
964

965 966 967
  def test_only_loads_pending_migrations
    # migrate up to 1
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
968

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

971 972 973 974 975
    names = proxies.map(&:name)
    assert !names.include?('ValidPeopleHaveLastNames')
    assert names.include?('WeNeedReminders')
    assert names.include?('InnocentJointable')
  end
976

977 978 979
  def test_target_version_zero_should_run_only_once
    # migrate up to 1
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
J
Jamis Buck 已提交
980

981 982 983 984 985 986 987
    # 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
988

989 990 991 992 993 994 995 996 997
  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 已提交
998
    end
999
  end
1000

1001 1002 1003 1004
  def test_migrator_verbosity
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
    assert_not_equal 0, ActiveRecord::Migration.message_count
    ActiveRecord::Migration.message_count = 0
1005

1006 1007 1008 1009
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
    assert_not_equal 0, ActiveRecord::Migration.message_count
    ActiveRecord::Migration.message_count = 0
  end
1010

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

1019 1020 1021
  def test_migrator_going_down_due_to_version_target
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
1022

1023 1024
    assert !Person.column_methods_hash.include?(:last_name)
    assert !Reminder.table_exists?
1025

1026
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1027

1028 1029 1030 1031 1032
    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
1033

1034 1035 1036
  def test_migrator_rollback
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
    assert_equal(3, ActiveRecord::Migrator.current_version)
1037

1038 1039
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(2, ActiveRecord::Migrator.current_version)
1040

1041 1042
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(1, ActiveRecord::Migrator.current_version)
1043

1044 1045
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(0, ActiveRecord::Migrator.current_version)
1046

1047 1048 1049
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal(0, ActiveRecord::Migrator.current_version)
  end
1050

1051 1052 1053
  def test_migrator_forward
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
    assert_equal(1, ActiveRecord::Migrator.current_version)
1054

1055 1056
    ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid", 2)
    assert_equal(3, ActiveRecord::Migrator.current_version)
1057

1058 1059 1060
    ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid")
    assert_equal(3, ActiveRecord::Migrator.current_version)
  end
1061

1062 1063 1064
  def test_get_all_versions
    ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
    assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
1065

1066 1067
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
1068

1069 1070
    ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
    assert_equal([1], ActiveRecord::Migrator.get_all_versions)
1071

1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
    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
1159

1160 1161
  def test_create_table_with_binary_column
    Person.connection.drop_table :binary_testings rescue nil
1162

1163 1164 1165
    assert_nothing_raised {
      Person.connection.create_table :binary_testings do |t|
        t.column "data", :binary, :null => false
1166
      end
1167 1168 1169 1170
    }

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

1172 1173 1174 1175
    if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
      assert_equal '', data_column.default
    else
      assert_nil data_column.default
1176
    end
1177

1178 1179 1180 1181 1182 1183
    Person.connection.drop_table :binary_testings rescue nil
  end

  def test_migrator_with_duplicates
    assert_raise(ActiveRecord::DuplicateMigrationVersionError) do
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
1184
    end
1185
  end
1186

1187 1188 1189
  def test_migrator_with_duplicate_names
    assert_raise(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
1190
    end
1191
  end
1192

1193 1194 1195
  def test_migrator_with_missing_version_numbers
    assert_raise(ActiveRecord::UnknownMigrationVersionError) do
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
1196
    end
1197
  end
1198

1199 1200
  def test_create_table_with_custom_sequence_name
    return unless current_adapter? :OracleAdapter
1201

1202 1203 1204 1205 1206 1207
    # 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
1208
        end
1209 1210
      ensure
        Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
1211
      end
1212
    end
1213

1214 1215 1216 1217 1218 1219
    # 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
1220 1221 1222
        end

        Person.connection.execute("select suitably_short_seq.nextval from dual")
1223 1224

      ensure
1225 1226
        Person.connection.drop_table :table_with_name_thats_just_ok,
                                     :sequence_name => 'suitably_short_seq' rescue nil
1227
      end
1228
    end
1229

1230 1231 1232 1233
    # confirm the custom sequence got dropped
    assert_raise(ActiveRecord::StatementInvalid) do
      Person.connection.execute("select suitably_short_seq.nextval from dual")
    end
1234
  end
1235

1236 1237 1238 1239
  protected
    def with_env_tz(new_tz = 'US/Eastern')
      old_tz, ENV['TZ'] = ENV['TZ'], new_tz
      yield
1240
    ensure
1241
      old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
1242
    end
1243
end
1244

1245 1246 1247
class InterleavedMigrationsTest < ActiveRecord::TestCase
  def test_migrator_interleaved_migrations
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
1248

1249 1250
    assert_nothing_raised do
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
1251 1252
    end

1253 1254
    Person.reset_column_information
    assert Person.column_methods_hash.include?(:last_name)
1255

1256 1257 1258 1259 1260 1261
    assert_nothing_raised do
      proxies = ActiveRecord::Migrator.down(
        MIGRATIONS_ROOT + "/interleaved/pass_3")
      names = proxies.map(&:name)
      assert names.include?('InterleavedPeopleHaveLastNames')
      assert names.include?('InterleavedInnocentJointable')
1262
    end
1263 1264
  end
end
1265

1266 1267 1268 1269 1270
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
1271
    end
1272

1273 1274 1275
    assert_nothing_raised do
      connection.add_index :values, :value
      connection.remove_index :values, :column => :value
1276
    end
1277

1278 1279 1280 1281 1282 1283 1284 1285 1286
    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|
1287
    end
1288
  end
1289

1290 1291 1292 1293 1294 1295 1296 1297
  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
1298
    end
1299
  end
1300

1301 1302 1303 1304
  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
1305
    end
1306
  end
1307

1308 1309 1310 1311
  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
1312
    end
1313
  end
1314

1315 1316 1317 1318
  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
1319
    end
1320
  end
1321

1322 1323 1324 1325 1326
  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
1327
    end
1328
  end
1329

1330 1331 1332 1333 1334
  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
1335
    end
1336
  end
1337

1338 1339 1340 1341 1342
  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
1343
    end
1344
  end
1345

1346 1347 1348 1349 1350
  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
1351
    end
1352
  end
1353

1354 1355 1356 1357
  def test_timestamps_creates_updated_at_and_created_at
    with_change_table do |t|
      @connection.expects(:add_timestamps).with(:delete_me)
      t.timestamps
1358
    end
1359
  end
1360

1361 1362 1363 1364
  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
1365
    end
1366
  end
1367

1368 1369 1370 1371 1372 1373 1374
  def string_column
    if current_adapter?(:PostgreSQLAdapter)
      "character varying(255)"
    elsif current_adapter?(:OracleAdapter)
      'VARCHAR2(255)'
    else
      'varchar(255)'
1375
    end
1376
  end
1377

1378 1379 1380 1381 1382 1383 1384
  def integer_column
    if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
      'int(11)'
    elsif current_adapter?(:OracleAdapter)
      'NUMBER(38)'
    else
      'integer'
1385
    end
1386
  end
1387

1388 1389 1390 1391 1392
  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
1393
    end
1394
  end
1395

1396 1397 1398 1399 1400
  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
1401
    end
1402
  end
1403

1404 1405 1406 1407
  def test_column_creates_column
    with_change_table do |t|
      @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
      t.column :bar, :integer
1408
    end
1409
  end
1410

1411 1412 1413 1414
  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
1415
    end
1416
  end
1417

1418 1419 1420 1421
  def test_index_creates_index
    with_change_table do |t|
      @connection.expects(:add_index).with(:delete_me, :bar, {})
      t.index :bar
1422
    end
1423
  end
1424

1425 1426 1427 1428
  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
1429
    end
1430
  end
1431

1432 1433 1434 1435
  def test_index_exists
    with_change_table do |t|
      @connection.expects(:index_exists?).with(:delete_me, :bar, {})
      t.index_exists?(:bar)
1436
    end
1437
  end
1438

1439 1440 1441 1442
  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)
1443
    end
1444
  end
1445

1446 1447 1448 1449
  def test_change_changes_column
    with_change_table do |t|
      @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
      t.change :bar, :string
1450
    end
1451
  end
1452

1453 1454 1455 1456
  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
1457
    end
1458
  end
1459

1460 1461 1462 1463
  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
1464
    end
1465
  end
1466

1467 1468 1469 1470
  def test_remove_drops_single_column
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, [:bar])
      t.remove :bar
1471
    end
1472
  end
1473

1474 1475 1476 1477
  def test_remove_drops_multiple_columns
    with_change_table do |t|
      @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
      t.remove :bar, :baz
1478
    end
1479
  end
1480

1481 1482 1483 1484
  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
1485 1486
    end
  end
1487

1488 1489 1490 1491 1492 1493
  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
1494

1495 1496 1497 1498 1499 1500 1501
  protected
  def with_change_table
    Person.connection.change_table :delete_me do |t|
      yield t
    end
  end
end
1502

1503 1504 1505 1506 1507 1508
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
1509

1510 1511 1512
    def teardown
      Person.connection.drop_table(:delete_me) rescue nil
    end
1513

1514 1515
    def test_adding_multiple_columns
      assert_queries(1) do
1516
        with_bulk_change_table do |t|
1517
          t.column :name, :string
1518
          t.string :qualification, :experience
1519 1520 1521
          t.integer :age, :default => 0
          t.date :birthdate
          t.timestamps
1522
        end
1523
      end
1524

1525 1526 1527 1528
      assert_equal 8, columns.size
      [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
      assert_equal 0, column(:age).default
    end
1529

1530 1531 1532
    def test_removing_columns
      with_bulk_change_table do |t|
        t.string :qualification, :experience
1533 1534
      end

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

1537 1538 1539 1540
      assert_queries(1) do
        with_bulk_change_table do |t|
          t.remove :qualification, :experience
          t.string :qualification_experience
1541
        end
1542
      end
1543

1544 1545 1546
      [:qualification, :experience].each {|c| assert ! column(c) }
      assert column(:qualification_experience)
    end
1547

1548 1549 1550 1551 1552
    def test_adding_indexes
      with_bulk_change_table do |t|
        t.string :username
        t.string :name
        t.integer :age
1553 1554
      end

1555 1556
      # Adding an index fires a query every time to check if an index already exists or not
      assert_queries(3) do
1557
        with_bulk_change_table do |t|
1558 1559
          t.index :username, :unique => true, :name => :awesome_username_index
          t.index [:name, :age]
1560
        end
1561
      end
1562

1563
      assert_equal 2, indexes.size
1564

1565 1566 1567
      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
1568

1569 1570
      assert index(:awesome_username_index).unique
    end
1571

1572 1573 1574 1575
    def test_removing_index
      with_bulk_change_table do |t|
        t.string :name
        t.index :name
1576
      end
1577

1578 1579 1580
      assert index(:index_delete_me_on_name)

      assert_queries(3) do
1581
        with_bulk_change_table do |t|
1582 1583
          t.remove_index :name
          t.index :name, :name => :new_name_index, :unique => true
1584
        end
1585
      end
1586

1587
      assert ! index(:index_delete_me_on_name)
1588

1589 1590 1591
      new_name_index = index(:new_name_index)
      assert new_name_index.unique
    end
1592

1593 1594 1595 1596
    def test_changing_columns
      with_bulk_change_table do |t|
        t.string :name
        t.date :birthdate
1597
      end
1598

1599 1600
      assert ! column(:name).default
      assert_equal :date, column(:birthdate).type
1601

1602 1603 1604 1605 1606 1607 1608
      # 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
1609
        end
1610 1611
      end

1612 1613 1614
      assert_equal 'NONAME', column(:name).default
      assert_equal :datetime, column(:birthdate).type
    end
1615

1616
    protected
1617

1618 1619 1620
    def with_bulk_change_table
      # Reset columns/indexes cache as we're changing the table
      @columns = @indexes = nil
1621

1622 1623
      Person.connection.change_table(:delete_me, :bulk => true) do |t|
        yield t
1624
      end
1625
    end
1626

1627 1628 1629
    def column(name)
      columns.detect {|c| c.name == name.to_s }
    end
1630

1631 1632
    def columns
      @columns ||= Person.connection.columns('delete_me')
1633 1634
    end

1635 1636
    def index(name)
      indexes.detect {|i| i.name == name.to_s }
1637 1638
    end

1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696
    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
1697

1698 1699 1700
  def test_copying_migrations_with_timestamps
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1701

1702 1703 1704 1705 1706 1707 1708
    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)
1709

1710
      files_count = Dir[@migrations_path + "/*.rb"].length
1711
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
1712 1713 1714
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
      assert copied.empty?
    end
1715 1716 1717
  ensure
    clear
  end
1718

1719 1720 1721
  def test_copying_migrations_with_timestamps_from_2_sources
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1722

1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733
    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
1734 1735 1736 1737 1738

      files_count = Dir[@migrations_path + "/*.rb"].length
      ActiveRecord::Migration.copy(@migrations_path, sources)
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
    end
1739 1740 1741
  ensure
    clear
  end
1742

1743 1744 1745
  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"]
1746

1747 1748 1749 1750
    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")
1751

1752 1753 1754 1755
      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?
1756
    end
1757 1758 1759
  ensure
    clear
  end
1760

1761 1762 1763
  def test_skipping_migrations
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1764

1765 1766 1767
    sources = ActiveSupport::OrderedHash.new
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
    sources[:omg]     = MIGRATIONS_ROOT + "/to_copy_with_name_collision"
1768

1769 1770 1771 1772
    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
1773

1774 1775 1776 1777 1778
    assert_equal 1, skipped.length
    assert_equal ["omg PeopleHaveHobbies"], skipped
  ensure
    clear
  end
1779

1780 1781 1782
  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"]
1783

1784 1785
    sources = ActiveSupport::OrderedHash.new
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
1786

1787 1788 1789 1790
    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)
1791

1792 1793 1794 1795 1796
    assert_equal 2, copied.length
    assert_equal 0, skipped.length
  ensure
    clear
  end
1797

1798 1799 1800
  def test_copying_migrations_to_non_existing_directory
    @migrations_path = MIGRATIONS_ROOT + "/non_existing"
    @existing_migrations = []
1801

1802 1803 1804 1805
    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")
1806 1807
      assert_equal 2, copied.length
    end
1808 1809 1810 1811
  ensure
    clear
    Dir.delete(@migrations_path)
  end
1812

1813 1814 1815
  def test_copying_migrations_to_empty_directory
    @migrations_path = MIGRATIONS_ROOT + "/empty"
    @existing_migrations = []
1816

1817 1818 1819 1820
    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")
1821
      assert_equal 2, copied.length
1822
    end
1823 1824
  ensure
    clear
1825
  end
1826
end