“c59bce8596b980535664d0caa5b650384c7a0e91”上不存在“activerecord/test/cases/migration_test.rb”
migration_test.rb 84.7 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 9

require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
10

11
if ActiveRecord::Base.connection.supports_migrations?
12 13
  class BigNumber < ActiveRecord::Base; end

14 15
  class Reminder < ActiveRecord::Base; end

J
Jamis Buck 已提交
16
  class ActiveRecord::Migration
17
    class << self
J
Jamis Buck 已提交
18
      attr_accessor :message_count
19
    end
20

21
    def puts(text="")
A
Aaron Patterson 已提交
22 23
      ActiveRecord::Migration.message_count ||= 0
      ActiveRecord::Migration.message_count += 1
J
Jamis Buck 已提交
24 25 26
    end
  end

27
  class MigrationTableAndIndexTest < ActiveRecord::TestCase
28 29
    def test_add_schema_info_respects_prefix_and_suffix
      conn = ActiveRecord::Base.connection
30

31
      conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
32 33 34
      # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
      ActiveRecord::Base.table_name_prefix = 'p_'
      ActiveRecord::Base.table_name_suffix = '_s'
35
      conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
36

37
      conn.initialize_schema_migrations_table
38

39
      assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name]
40 41 42 43 44 45
    ensure
      ActiveRecord::Base.table_name_prefix = ""
      ActiveRecord::Base.table_name_suffix = ""
    end
  end

46
  class MigrationTest < ActiveRecord::TestCase
J
Jamis Buck 已提交
47
    self.use_transactional_fixtures = false
J
Jeremy Kemper 已提交
48

49
    fixtures :people
J
Jamis Buck 已提交
50

51
    def setup
J
Jamis Buck 已提交
52
      ActiveRecord::Migration.verbose = true
A
Aaron Patterson 已提交
53
      ActiveRecord::Migration.message_count = 0
54 55 56
    end

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

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

65
      %w(last_name key bio age height wealth birthday favorite_day
66
         moment_of_truth male administrator funny).each do |column|
67 68
        Person.connection.remove_column('people', column) rescue nil
      end
69
      Person.connection.remove_column("people", "first_name") rescue nil
70
      Person.connection.remove_column("people", "middle_name") rescue nil
71
      Person.connection.add_column("people", "first_name", :string, :limit => 40)
72 73
      Person.reset_column_information
    end
74

75
    def test_add_index
76 77 78
      # 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
79
      Person.connection.add_column "people", "administrator", :boolean
80

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

84
      # Orcl nds shrt indx nms.  Sybs 2.
85
      # OpenBase does not have named indexes.  You must specify a single column name
86
      unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
87 88
        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"]) }
89 90 91 92
        # 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)
          assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
93
          assert_nothing_raised { Person.connection.remove_index("people", :name => :index_people_on_last_name_and_first_name) }
94 95 96
          assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
          assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
        end
97 98
        assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
        assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
99 100 101 102 103 104 105 106
        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"]) }
107
      end
108

109
      # quoting
110
      # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
111 112
      # OpenBase does not have named indexes.  You must specify a single column name
      unless current_adapter?(:OpenBaseAdapter)
113
        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)
114 115 116
        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
J
Jeremy Kemper 已提交
117

118
      # Sybase adapter does not support indexes on :boolean columns
119 120
      # OpenBase does not have named indexes.  You must specify a single column
      unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
121 122 123
        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
124
    end
125

126 127 128 129 130 131 132
    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

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

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

    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
156 157
        assert !Person.connection.index_name_exists?('people', 'old_idx', false)
        assert Person.connection.index_name_exists?('people', 'new_idx', true)
158 159 160 161 162 163
      end
    end

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

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
    def test_index_exists
      Person.connection.create_table :testings do |t|
        t.column :foo, :string, :limit => 100
        t.column :bar, :string, :limit => 100
      end
      Person.connection.add_index :testings, :foo

      assert Person.connection.index_exists?(:testings, :foo)
      assert !Person.connection.index_exists?(:testings, :bar)
    ensure
      Person.connection.drop_table :testings rescue nil
    end

    def test_index_exists_on_multiple_columns
      Person.connection.create_table :testings do |t|
        t.column :foo, :string, :limit => 100
        t.column :bar, :string, :limit => 100
      end
      Person.connection.add_index :testings, [:foo, :bar]

      assert Person.connection.index_exists?(:testings, [:foo, :bar])
    ensure
      Person.connection.drop_table :testings rescue nil
    end

    def test_unique_index_exists
      Person.connection.create_table :testings do |t|
        t.column :foo, :string, :limit => 100
      end
      Person.connection.add_index :testings, :foo, :unique => true

      assert Person.connection.index_exists?(:testings, :foo, :unique => true)
    ensure
      Person.connection.drop_table :testings rescue nil
    end

    def test_named_index_exists
      Person.connection.create_table :testings do |t|
        t.column :foo, :string, :limit => 100
      end
      Person.connection.add_index :testings, :foo, :name => "custom_index_name"

      assert Person.connection.index_exists?(:testings, :foo, :name => "custom_index_name")
    ensure
      Person.connection.drop_table :testings rescue nil
    end

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
    def testing_table_with_only_foo_attribute
      Person.connection.create_table :testings, :id => false do |t|
        t.column :foo, :string
      end

      yield Person.connection
    ensure
      Person.connection.drop_table :testings rescue nil
    end
    protected :testing_table_with_only_foo_attribute

    def test_create_table_without_id
      testing_table_with_only_foo_attribute do |connection|
        assert_equal connection.columns(:testings).size, 1
      end
    end

    def test_add_column_with_primary_key_attribute
      testing_table_with_only_foo_attribute do |connection|
        assert_nothing_raised { connection.add_column :testings, :id, :primary_key }
        assert_equal connection.columns(:testings).size, 2
      end
    end

239 240 241 242 243 244 245 246 247 248
    def test_create_table_adds_id
      Person.connection.create_table :testings do |t|
        t.column :foo, :string
      end

      assert_equal %w(foo id),
        Person.connection.columns(:testings).map { |c| c.name }.sort
    ensure
      Person.connection.drop_table :testings rescue nil
    end
249 250

    def test_create_table_with_not_null_column
251 252 253 254
      assert_nothing_raised do
        Person.connection.create_table :testings do |t|
          t.column :foo, :string, :null => false
        end
255 256
      end

257
      assert_raise(ActiveRecord::StatementInvalid) do
258 259 260 261 262
        Person.connection.execute "insert into testings (foo) values (NULL)"
      end
    ensure
      Person.connection.drop_table :testings rescue nil
    end
263 264

    def test_create_table_with_defaults
265
      # MySQL doesn't allow defaults on TEXT or BLOB columns.
B
Brian Lopez 已提交
266
      mysql = current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
267

268 269 270 271 272
      Person.connection.create_table :testings do |t|
        t.column :one, :string, :default => "hello"
        t.column :two, :boolean, :default => true
        t.column :three, :boolean, :default => false
        t.column :four, :integer, :default => 1
273
        t.column :five, :text, :default => "hello" unless mysql
274 275 276 277 278 279 280
      end

      columns = Person.connection.columns(:testings)
      one = columns.detect { |c| c.name == "one" }
      two = columns.detect { |c| c.name == "two" }
      three = columns.detect { |c| c.name == "three" }
      four = columns.detect { |c| c.name == "four" }
281
      five = columns.detect { |c| c.name == "five" } unless mysql
282 283

      assert_equal "hello", one.default
284 285
      assert_equal true, two.default
      assert_equal false, three.default
286
      assert_equal 1, four.default
287
      assert_equal "hello", five.default unless mysql
288 289 290 291

    ensure
      Person.connection.drop_table :testings rescue nil
    end
292 293

    def test_create_table_with_limits
294 295 296
      assert_nothing_raised do
        Person.connection.create_table :testings do |t|
          t.column :foo, :string, :limit => 255
297

298
          t.column :default_int, :integer
299

300 301 302 303
          t.column :one_int,    :integer, :limit => 1
          t.column :four_int,   :integer, :limit => 4
          t.column :eight_int,  :integer, :limit => 8
          t.column :eleven_int, :integer, :limit => 11
304
        end
305 306 307 308 309 310 311 312 313 314
      end

      columns = Person.connection.columns(:testings)
      foo = columns.detect { |c| c.name == "foo" }
      assert_equal 255, foo.limit

      default = columns.detect { |c| c.name == "default_int" }
      one     = columns.detect { |c| c.name == "one_int"     }
      four    = columns.detect { |c| c.name == "four_int"    }
      eight   = columns.detect { |c| c.name == "eight_int"   }
315
      eleven  = columns.detect { |c| c.name == "eleven_int"   }
316 317 318 319 320 321

      if current_adapter?(:PostgreSQLAdapter)
        assert_equal 'integer', default.sql_type
        assert_equal 'smallint', one.sql_type
        assert_equal 'integer', four.sql_type
        assert_equal 'bigint', eight.sql_type
322
        assert_equal 'integer', eleven.sql_type
B
Brian Lopez 已提交
323
      elsif current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
324 325 326 327 328
        assert_match 'int(11)', default.sql_type
        assert_match 'tinyint', one.sql_type
        assert_match 'int', four.sql_type
        assert_match 'bigint', eight.sql_type
        assert_match 'int(11)', eleven.sql_type
329 330 331 332 333
      elsif current_adapter?(:OracleAdapter)
        assert_equal 'NUMBER(38)', default.sql_type
        assert_equal 'NUMBER(1)', one.sql_type
        assert_equal 'NUMBER(4)', four.sql_type
        assert_equal 'NUMBER(8)', eight.sql_type
334 335 336 337
      end
    ensure
      Person.connection.drop_table :testings rescue nil
    end
338

339 340 341 342 343 344 345
    def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
      ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore

      Person.connection.create_table :testings do |t|
          t.column :foo, :string
      end

346
      assert_equal %w(foo testing_id), Person.connection.columns(:testings).map { |c| c.name }.sort
347 348 349 350 351 352 353 354 355 356 357 358
    ensure
      Person.connection.drop_table :testings rescue nil
      ActiveRecord::Base.primary_key_prefix_type = nil
    end

    def test_create_table_with_primary_key_prefix_as_table_name
      ActiveRecord::Base.primary_key_prefix_type = :table_name

      Person.connection.create_table :testings do |t|
          t.column :foo, :string
      end

359
      assert_equal %w(foo testingid), Person.connection.columns(:testings).map { |c| c.name }.sort
360 361 362 363 364
    ensure
      Person.connection.drop_table :testings rescue nil
      ActiveRecord::Base.primary_key_prefix_type = nil
    end

365 366 367 368
    def test_create_table_with_force_true_does_not_drop_nonexisting_table
      if Person.connection.table_exists?(:testings2)
        Person.connection.drop_table :testings2
      end
369

370 371 372 373 374 375
      # 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
376
      end
377 378
    ensure
      Person.connection.drop_table :testings2 rescue nil
379 380
    end

381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
    def test_create_table_with_timestamps_should_create_datetime_columns
      table_name = :testings

      Person.connection.create_table table_name do |t|
        t.timestamps
      end
      created_columns = Person.connection.columns(table_name)

      created_at_column = created_columns.detect {|c| c.name == 'created_at' }
      updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }

      assert created_at_column.null
      assert updated_at_column.null
    ensure
      Person.connection.drop_table table_name rescue nil
    end

    def test_create_table_with_timestamps_should_create_datetime_columns_with_options
      table_name = :testings

      Person.connection.create_table table_name do |t|
        t.timestamps :null => false
      end
      created_columns = Person.connection.columns(table_name)

      created_at_column = created_columns.detect {|c| c.name == 'created_at' }
      updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }

      assert !created_at_column.null
      assert !updated_at_column.null
    ensure
      Person.connection.drop_table table_name rescue nil
    end
414

415 416 417 418 419 420 421
    def test_create_table_without_a_block
      table_name = :testings
      Person.connection.create_table table_name
    ensure
      Person.connection.drop_table table_name rescue nil
    end

422
    # Sybase, and SQLite3 will not allow you to add a NOT NULL
423
    # column to a table without a default value.
424
    unless current_adapter?(:SybaseAdapter, :SQLite3Adapter)
425 426 427 428 429
      def test_add_column_not_null_without_default
        Person.connection.create_table :testings do |t|
          t.column :foo, :string
        end
        Person.connection.add_column :testings, :bar, :string, :null => false
430

431
        assert_raise(ActiveRecord::StatementInvalid) do
432 433 434 435 436 437
          Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
        end
      ensure
        Person.connection.drop_table :testings rescue nil
      end
    end
438

439
    def test_add_column_not_null_with_default
440
      Person.connection.create_table :testings do |t|
441 442
        t.column :foo, :string
      end
J
Jeremy Kemper 已提交
443 444

      con = Person.connection
445
      Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
446
      Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
447
      Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
448
      assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
449

450
      assert_raise(ActiveRecord::StatementInvalid) do
451 452 453 454 455 456
        unless current_adapter?(:OpenBaseAdapter)
          Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
        else
          Person.connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
            "Testing Insert","id",2)
        end
457 458 459 460
      end
    ensure
      Person.connection.drop_table :testings rescue nil
    end
461

462 463 464 465
    # 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
466
      correct_value = '0012345678901234567890.0123456789'.to_d
467 468 469 470 471 472

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

      # Do a manual insertion
473 474
      if current_adapter?(:OracleAdapter)
        Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
475
      elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
476
        Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
477 478 479
      else
        Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
      end
480 481 482 483 484 485

      # SELECT
      row = Person.find(:first)
      assert_kind_of BigDecimal, row.wealth

      # If this assert fails, that means the SELECT is broken!
486 487 488
      unless current_adapter?(:SQLite3Adapter)
        assert_equal correct_value, row.wealth
      end
489 490 491 492 493 494 495 496 497 498 499 500

      # Reset to old state
      Person.delete_all

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

      # SELECT
      row = Person.find(:first)
      assert_kind_of BigDecimal, row.wealth

      # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
501 502 503
      unless current_adapter?(:SQLite3Adapter)
        assert_equal correct_value, row.wealth
      end
504 505 506 507 508 509

      # Reset to old state
      Person.connection.del_column "people", "wealth" rescue nil
      Person.reset_column_information
    end

510 511 512 513 514 515 516 517
    def test_add_column_with_precision_and_scale
      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
    end
518

519 520 521 522 523 524
    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
525
      Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
526 527
      Person.connection.add_column "people", "birthday", :datetime
      Person.connection.add_column "people", "favorite_day", :date
528
      Person.connection.add_column "people", "moment_of_truth", :datetime
529
      Person.connection.add_column "people", "male", :boolean
530
      Person.reset_column_information
531

532 533 534 535 536
      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,
537
          :moment_of_truth => "1782-10-10 21:40:18", :male => true
538 539 540
      end

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

R
R.T. Lechow 已提交
546
      # Test for 30 significant digits (beyond the 16 of float), 10 of them
547
      # after the decimal place.
J
Jeremy Kemper 已提交
548

549 550 551
      unless current_adapter?(:SQLite3Adapter)
        assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
      end
J
Jeremy Kemper 已提交
552

553
      assert_equal true, bob.male?
554

555 556 557 558 559
      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
560

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

568 569 570 571 572 573 574 575
      # 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
576
            bob.reload
577 578 579 580 581
            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
582
            assert_match(/\A-05:?00\Z/, bob.moment_of_truth.zone) #ruby 1.8.6 uses HH:MM, prior versions use HHMM
583 584
            assert_equal DateTime::ITALY, bob.moment_of_truth.start
          end
585
        end
586
      end
587

588
      assert_instance_of TrueClass, bob.male?
589
      assert_kind_of BigDecimal, bob.wealth
590 591
    end

B
Brian Lopez 已提交
592
    if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
593 594 595 596
      def test_unabstracted_database_dependent_types
        Person.delete_all

        ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
597
        Person.reset_column_information
598
        assert_match(/tinyint/, Person.columns_hash['intelligence_quotient'].sql_type)
599
      ensure
600
        ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
601 602 603
      end
    end

604
    def test_add_remove_single_field_using_string_arguments
605 606
      assert !Person.column_methods_hash.include?(:last_name)

607
      ActiveRecord::Migration.add_column 'people', 'last_name', :string
608 609 610

      Person.reset_column_information
      assert Person.column_methods_hash.include?(:last_name)
611

612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
      ActiveRecord::Migration.remove_column 'people', 'last_name'

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

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

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

      Person.reset_column_information
      assert Person.column_methods_hash.include?(:last_name)

      ActiveRecord::Migration.remove_column :people, :last_name
627 628 629 630

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

B
Brian Lopez 已提交
632
    if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
      def testing_table_for_positioning
        Person.connection.create_table :testings, :id => false do |t|
          t.column :first, :integer
          t.column :second, :integer
          t.column :third, :integer
        end

        yield Person.connection
      ensure
        Person.connection.drop_table :testings rescue nil
      end
      protected :testing_table_for_positioning

      def test_column_positioning
        testing_table_for_positioning do |conn|
          assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name }
        end
      end

      def test_add_column_with_positioning
        testing_table_for_positioning do |conn|
          conn.add_column :testings, :new_col, :integer
          assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name }
        end
        testing_table_for_positioning do |conn|
          conn.add_column :testings, :new_col, :integer, :first => true
          assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name }
        end
        testing_table_for_positioning do |conn|
          conn.add_column :testings, :new_col, :integer, :after => :first
          assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name }
        end
      end

      def test_change_column_with_positioning
        testing_table_for_positioning do |conn|
          conn.change_column :testings, :second, :integer, :first => true
          assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name }
        end
        testing_table_for_positioning do |conn|
          conn.change_column :testings, :second, :integer, :after => :third
          assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name }
        end
      end
    end

679 680
    def test_add_rename
      Person.delete_all
681

682
      begin
683
        Person.connection.add_column "people", "girlfriend", :string
684
        Person.reset_column_information
685 686
        Person.create :girlfriend => 'bobette'

687
        Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
688 689

        Person.reset_column_information
690
        bob = Person.find(:first)
691

692 693 694 695 696
        assert_equal "bobette", bob.exgirlfriend
      ensure
        Person.connection.remove_column("people", "girlfriend") rescue nil
        Person.connection.remove_column("people", "exgirlfriend") rescue nil
      end
697

698
    end
699

700 701
    def test_rename_column_using_symbol_arguments
      begin
702
        names_before = Person.find(:all).map(&:first_name)
703 704 705
        Person.connection.rename_column :people, :first_name, :nick_name
        Person.reset_column_information
        assert Person.column_names.include?("nick_name")
706
        assert_equal names_before, Person.find(:all).map(&:nick_name)
707 708 709 710 711
      ensure
        Person.connection.remove_column("people","nick_name")
        Person.connection.add_column("people","first_name", :string)
      end
    end
712

713 714
    def test_rename_column
      begin
715
        names_before = Person.find(:all).map(&:first_name)
716 717 718
        Person.connection.rename_column "people", "first_name", "nick_name"
        Person.reset_column_information
        assert Person.column_names.include?("nick_name")
719
        assert_equal names_before, Person.find(:all).map(&:nick_name)
720 721 722 723 724
      ensure
        Person.connection.remove_column("people","nick_name")
        Person.connection.add_column("people","first_name", :string)
      end
    end
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744

    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
      end
    end

    def test_rename_nonexistent_column
      ActiveRecord::Base.connection.create_table(:hats) do |table|
        table.column :hat_name, :string, :default => nil
      end
745
      exception = if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
746 747 748 749
        ActiveRecord::StatementInvalid
      else
        ActiveRecord::ActiveRecordError
      end
750
      assert_raise(exception) do
751 752 753 754 755
        Person.connection.rename_column "hats", "nonexistent", "should_fail"
      end
    ensure
      ActiveRecord::Base.connection.drop_table(:hats)
    end
756

757 758 759 760 761 762 763 764 765 766
    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")
      ensure
        Person.connection.remove_column("people", "group") rescue nil
        Person.connection.add_column("people", "first_name", :string) rescue nil
      end
    end
J
Jeremy Kemper 已提交
767

768 769 770 771 772
    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
      end
773
      Person.connection.add_index :hats, :hat_name
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
      assert_nothing_raised do
        Person.connection.rename_column "hats", "hat_name", "name"
      end
    ensure
      ActiveRecord::Base.connection.drop_table(:hats)
    end

    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
      end
      ActiveRecord::Base.connection.add_index "hats", "hat_size"

      assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
    ensure
      ActiveRecord::Base.connection.drop_table(:hats)
    end

    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
      end
799
      ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true
800 801 802 803 804 805

      assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
    ensure
      ActiveRecord::Base.connection.drop_table(:hats)
    end

806 807 808 809
    def test_remove_column_no_second_parameter_raises_exception
      assert_raise(ArgumentError) { Person.connection.remove_column("funny") }
    end

810 811 812 813
    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
J
Jeremy Kemper 已提交
814

815 816
        Topic.connection.change_column "topics", "written_on", :datetime, :null => false
        Topic.reset_column_information
817 818 819

        Topic.connection.change_column "topics", "written_on", :datetime, :null => true
        Topic.reset_column_information
820 821
      end
    end
822

823
    if current_adapter?(:SQLite3Adapter)
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
      def test_rename_table_for_sqlite_should_work_with_reserved_words
        begin
          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
          end

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

          # Using explicit id in insert for compatibility across all databases
          con = ActiveRecord::Base.connection
          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")

        ensure
          ActiveRecord::Base.connection.drop_table :references
          ActiveRecord::Base.connection.rename_table :old_references, :references
        end
      end
    end

849 850 851 852
    def test_rename_table
      begin
        ActiveRecord::Base.connection.create_table :octopuses do |t|
          t.column :url, :string
853
        end
854
        ActiveRecord::Base.connection.rename_table :octopuses, :octopi
855

856
        # Using explicit id in insert for compatibility across all databases
J
Jeremy Kemper 已提交
857
        con = ActiveRecord::Base.connection
858
        con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
859
        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')" }
860
        con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
861 862

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

864 865 866 867 868
      ensure
        ActiveRecord::Base.connection.drop_table :octopuses rescue nil
        ActiveRecord::Base.connection.drop_table :octopi rescue nil
      end
    end
J
Jeremy Kemper 已提交
869

870
    def test_change_column_nullability
J
Jeremy Kemper 已提交
871
      Person.delete_all
872 873 874 875 876 877 878 879 880 881
      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
882

883 884 885 886 887 888
    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
J
Jeremy Kemper 已提交
889

890 891 892
        ActiveRecord::Base.connection.rename_table :octopuses, :octopi

        # Using explicit id in insert for compatibility across all databases
J
Jeremy Kemper 已提交
893
        con = ActiveRecord::Base.connection
894
        con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
895
        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')" }
896
        con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
897 898 899 900 901 902 903 904 905

        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")
      ensure
        ActiveRecord::Base.connection.drop_table :octopuses rescue nil
        ActiveRecord::Base.connection.drop_table :octopi rescue nil
      end
    end

906
    def test_change_column
907
      Person.connection.add_column 'people', 'age', :integer
908 909
      label = "test_change_column Columns"
      old_columns = Person.connection.columns(Person.table_name, label)
910 911 912
      assert old_columns.find { |c| c.name == 'age' and c.type == :integer }

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

914
      new_columns = Person.connection.columns(Person.table_name, label)
915 916
      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 }
917

918
      old_columns = Topic.connection.columns(Topic.table_name, label)
919 920
      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 }
921
      new_columns = Topic.connection.columns(Topic.table_name, label)
922 923
      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 }
924
      assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
925
    end
J
Jeremy Kemper 已提交
926

927 928 929 930
    def test_change_column_with_nil_default
      Person.connection.add_column "people", "contributor", :boolean, :default => true
      Person.reset_column_information
      assert Person.new.contributor?
J
Jeremy Kemper 已提交
931

932 933 934 935
      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
936 937
    ensure
      Person.connection.remove_column("people", "contributor") rescue nil
938
    end
939 940

    def test_change_column_with_new_default
941
      Person.connection.add_column "people", "administrator", :boolean, :default => true
942
      Person.reset_column_information
943
      assert Person.new.administrator?
944

945
      assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
946
      Person.reset_column_information
947
      assert !Person.new.administrator?
948 949
    ensure
      Person.connection.remove_column("people", "administrator") rescue nil
950
    end
J
Jeremy Kemper 已提交
951

952 953 954 955 956
    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
957 958 959 960 961 962 963 964

    def test_change_column_quotes_column_names
      Person.connection.create_table :testings do |t|
        t.column :select, :string
      end

      assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }

965 966 967 968 969 970
      # Oracle needs primary key value from sequence
      if current_adapter?(:OracleAdapter)
        assert_nothing_raised { Person.connection.execute "insert into testings (id, #{Person.connection.quote_column_name('select')}) values (testings_seq.nextval, '7 chars')" }
      else
        assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
      end
971 972 973 974
    ensure
      Person.connection.drop_table :testings rescue nil
    end

975 976 977 978 979 980 981 982 983 984 985
    def test_keeping_default_and_notnull_constaint_on_change
      Person.connection.create_table :testings do |t|
        t.column :title, :string
      end
      person_klass = Class.new(Person)
      person_klass.set_table_name 'testings'

      person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
      person_klass.reset_column_information
      assert_equal 99, person_klass.columns_hash["wealth"].default
      assert_equal false, person_klass.columns_hash["wealth"].null
986 987 988 989 990 991
      # Oracle needs primary key value from sequence
      if current_adapter?(:OracleAdapter)
        assert_nothing_raised {person_klass.connection.execute("insert into testings (id, title) values (testings_seq.nextval, 'tester')")}
      else
        assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
      end
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028

      # change column default to see that column doesn't lose its not null definition
      person_klass.connection.change_column_default "testings", "wealth", 100
      person_klass.reset_column_information
      assert_equal 100, person_klass.columns_hash["wealth"].default
      assert_equal false, person_klass.columns_hash["wealth"].null

      # rename column to see that column doesn't lose its not null and/or default definition
      person_klass.connection.rename_column "testings", "wealth", "money"
      person_klass.reset_column_information
      assert_nil person_klass.columns_hash["wealth"]
      assert_equal 100, person_klass.columns_hash["money"].default
      assert_equal false, person_klass.columns_hash["money"].null

      # change column
      person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
      person_klass.reset_column_information
      assert_equal 1000, person_klass.columns_hash["money"].default
      assert_equal false, person_klass.columns_hash["money"].null

      # change column, make it nullable and clear default
      person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
      person_klass.reset_column_information
      assert_nil person_klass.columns_hash["money"].default
      assert_equal true, person_klass.columns_hash["money"].null

      # change_column_null, make it not nullable and set null values to a default value
      person_klass.connection.execute('UPDATE testings SET money = NULL')
      person_klass.connection.change_column_null "testings", "money", false, 2000
      person_klass.reset_column_information
      assert_nil person_klass.columns_hash["money"].default
      assert_equal false, person_klass.columns_hash["money"].null
      assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
    ensure
      Person.connection.drop_table :testings rescue nil
    end

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

1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
    def test_column_exists
      Person.connection.create_table :testings do |t|
        t.column :foo, :string
      end

      assert Person.connection.column_exists?(:testings, :foo)
      assert !Person.connection.column_exists?(:testings, :bar)
    ensure
      Person.connection.drop_table :testings rescue nil
    end

    def test_column_exists_with_type
      Person.connection.create_table :testings do |t|
        t.column :foo, :string
        t.column :bar, :decimal, :precision => 8, :scale => 2
      end

      assert Person.connection.column_exists?(:testings, :foo, :string)
      assert !Person.connection.column_exists?(:testings, :foo, :integer)
      assert Person.connection.column_exists?(:testings, :bar, :decimal)
      assert !Person.connection.column_exists?(:testings, :bar, :integer)
    ensure
      Person.connection.drop_table :testings rescue nil
    end

    def test_column_exists_with_definition
      Person.connection.create_table :testings do |t|
        t.column :foo, :string, :limit => 100
        t.column :bar, :decimal, :precision => 8, :scale => 2
      end

      assert Person.connection.column_exists?(:testings, :foo, :string, :limit => 100)
      assert !Person.connection.column_exists?(:testings, :foo, :string, :limit => 50)
      assert Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 8, :scale => 2)
      assert !Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 10, :scale => 2)
    ensure
      Person.connection.drop_table :testings rescue nil
    end

1074
    def test_add_table
1075
      assert !Reminder.table_exists?
1076

1077
      WeNeedReminders.up
J
Jeremy Kemper 已提交
1078 1079

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

1082
      WeNeedReminders.down
1083
      assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
1084 1085
    end

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
    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
1121
      # precision/scale explicitly left out.  By the SQL standard, numbers
1122
      # assigned to this field should be truncated but that's seldom respected.
1123
      if current_adapter?(:PostgreSQLAdapter)
1124 1125 1126 1127 1128 1129 1130 1131 1132
        # - 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
1133
      elsif current_adapter?(:SQLite3Adapter)
1134 1135
        # - SQLite3 stores a float, in violation of SQL
        assert_kind_of BigDecimal, b.value_of_e
1136
        assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
1137 1138 1139 1140 1141 1142 1143
      else
        # - SQL standard is an integer
        assert_kind_of Fixnum, b.value_of_e
        assert_equal 2, b.value_of_e
      end

      GiveMeBigNumbers.down
1144
      assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
1145 1146
    end

1147 1148
    def test_migrator
      assert !Person.column_methods_hash.include?(:last_name)
1149
      assert !Reminder.table_exists?
1150

1151
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
1152

1153
      assert_equal 3, ActiveRecord::Migrator.current_version
1154 1155 1156 1157 1158
      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

1159
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
1160 1161 1162 1163

      assert_equal 0, ActiveRecord::Migrator.current_version
      Person.reset_column_information
      assert !Person.column_methods_hash.include?(:last_name)
1164
      assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
1165 1166
    end

1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
    class MockMigration < ActiveRecord::Migration
      attr_reader :went_up, :went_down
      def initialize
        @went_up   = false
        @went_down = false
      end

      def up
        @went_up = true
        super
      end

      def down
        @went_down = true
        super
      end
    end

    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'

      migration.migrate :up
      assert migration.went_up, 'have gone up'
      assert !migration.went_down, 'have not gone down'
    end

    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'

      migration.migrate :down
      assert !migration.went_up, 'have gone up'
      assert migration.went_down, 'have not gone down'
    end

1205 1206
    def test_migrator_one_up
      assert !Person.column_methods_hash.include?(:last_name)
1207
      assert !Reminder.table_exists?
1208

1209
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1210 1211 1212

      Person.reset_column_information
      assert Person.column_methods_hash.include?(:last_name)
1213
      assert !Reminder.table_exists?
1214

1215
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
1216 1217 1218 1219

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

1221
    def test_migrator_one_down
1222
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
1223

1224
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
1225

1226 1227
      Person.reset_column_information
      assert Person.column_methods_hash.include?(:last_name)
1228
      assert !Reminder.table_exists?
1229
    end
1230

1231
    def test_migrator_one_up_one_down
1232 1233
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
1234 1235

      assert !Person.column_methods_hash.include?(:last_name)
1236
      assert !Reminder.table_exists?
1237
    end
1238

1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
    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

    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

1254
    if ActiveRecord::Base.connection.supports_ddl_transactions?
1255 1256 1257
      def test_migrator_one_up_with_exception_and_rollback
        assert !Person.column_methods_hash.include?(:last_name)

1258
        e = assert_raise(StandardError) do
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
          ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
        end

        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)
      end
    end

1269 1270
    def test_finds_migrations
      migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
1271

A
Aaron Patterson 已提交
1272
      [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
1273 1274
        assert_equal migrations[i].version, pair.first
        assert_equal migrations[i].name, pair.last
1275 1276 1277
      end
    end

1278 1279 1280 1281
    def test_finds_migrations_from_two_directories
      directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps']
      migrations = ActiveRecord::Migrator.new(:up, directories).migrations

1282 1283 1284 1285 1286 1287 1288
      [[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
1289 1290 1291
      end
    end

1292 1293 1294
    def test_finds_pending_migrations
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1)
      migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2").pending_migrations
1295

1296
      assert_equal 1, migrations.size
1297
      assert_equal migrations[0].version, 3
A
Aaron Patterson 已提交
1298
      assert_equal migrations[0].name, 'InterleavedInnocentJointable'
1299 1300
    end

1301
    def test_relative_migrations
A
Aaron Patterson 已提交
1302
      list = Dir.chdir(MIGRATIONS_ROOT) do
1303 1304 1305
        ActiveRecord::Migrator.up("valid/", 1)
      end

A
Aaron Patterson 已提交
1306 1307 1308 1309
      migration_proxy = list.find { |item|
        item.name == 'ValidPeopleHaveLastNames'
      }
      assert migration_proxy, 'should find pending migration'
1310 1311
    end

1312 1313 1314 1315
    def test_only_loads_pending_migrations
      # migrate up to 1
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)

A
Aaron Patterson 已提交
1316
      proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
1317

A
Aaron Patterson 已提交
1318 1319 1320 1321
      names = proxies.map(&:name)
      assert !names.include?('ValidPeopleHaveLastNames')
      assert names.include?('WeNeedReminders')
      assert names.include?('InnocentJointable')
1322 1323
    end

1324 1325 1326 1327 1328 1329 1330 1331
    def test_target_version_zero_should_run_only_once
      # migrate up to 1
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)

      # migrate down to 0
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)

      # migrate down to 0 again
A
Aaron Patterson 已提交
1332 1333
      proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
      assert_equal [], proxies
1334 1335
    end

1336
    def test_migrator_db_has_no_schema_migrations_table
1337 1338 1339 1340 1341 1342
      # 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
1343 1344 1345 1346 1347
      assert_nothing_raised do
        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
      end
    end

J
Jamis Buck 已提交
1348
    def test_migrator_verbosity
1349
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
A
Aaron Patterson 已提交
1350 1351
      assert_not_equal 0, ActiveRecord::Migration.message_count
      ActiveRecord::Migration.message_count = 0
J
Jamis Buck 已提交
1352

1353
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
A
Aaron Patterson 已提交
1354 1355
      assert_not_equal 0, ActiveRecord::Migration.message_count
      ActiveRecord::Migration.message_count = 0
J
Jamis Buck 已提交
1356
    end
1357

J
Jamis Buck 已提交
1358
    def test_migrator_verbosity_off
A
Aaron Patterson 已提交
1359
      ActiveRecord::Migration.verbose = false
1360
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
A
Aaron Patterson 已提交
1361
      assert_equal 0, ActiveRecord::Migration.message_count
1362
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
A
Aaron Patterson 已提交
1363
      assert_equal 0, ActiveRecord::Migration.message_count
J
Jamis Buck 已提交
1364
    end
1365

1366
    def test_migrator_going_down_due_to_version_target
1367 1368
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
1369 1370

      assert !Person.column_methods_hash.include?(:last_name)
1371
      assert !Reminder.table_exists?
1372

1373
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1374 1375 1376 1377 1378 1379

      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
1380

1381 1382 1383
    def test_migrator_rollback
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
      assert_equal(3, ActiveRecord::Migrator.current_version)
1384

1385
      ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1386
      assert_equal(2, ActiveRecord::Migrator.current_version)
1387

1388
      ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1389
      assert_equal(1, ActiveRecord::Migrator.current_version)
1390

1391
      ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1392
      assert_equal(0, ActiveRecord::Migrator.current_version)
1393

1394
      ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1395
      assert_equal(0, ActiveRecord::Migrator.current_version)
1396
    end
1397

1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
    def test_migrator_forward
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
      assert_equal(1, ActiveRecord::Migrator.current_version)

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

      ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid")
      assert_equal(3, ActiveRecord::Migrator.current_version)
    end
1408

1409 1410
    def test_get_all_versions
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1411
      assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
1412 1413 1414 1415 1416 1417 1418 1419 1420 1421

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

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

      ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
      assert_equal([], ActiveRecord::Migrator.get_all_versions)
    end
1422

1423
    def test_schema_migrations_table_name
1424 1425
      ActiveRecord::Base.table_name_prefix = "prefix_"
      ActiveRecord::Base.table_name_suffix = "_suffix"
1426
      Reminder.reset_table_name
1427
      assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
1428 1429
      ActiveRecord::Base.table_name_prefix = ""
      ActiveRecord::Base.table_name_suffix = ""
1430
      Reminder.reset_table_name
1431
      assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
1432 1433 1434
    ensure
      ActiveRecord::Base.table_name_prefix = ""
      ActiveRecord::Base.table_name_suffix = ""
1435
    end
1436

1437 1438 1439 1440
    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)
1441
      Reminder.reset_table_name
1442
      assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
1443

1444 1445 1446 1447 1448
      # 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'
1449
      Reminder.reset_table_name
1450 1451 1452
      assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
      Reminder.table_name_prefix = ''
      Reminder.table_name_suffix = ''
1453
      Reminder.reset_table_name
1454 1455

      # Use AR::Base's prefix/suffix if string or symbol is given
1456 1457
      ActiveRecord::Base.table_name_prefix = "prefix_"
      ActiveRecord::Base.table_name_suffix = "_suffix"
1458
      Reminder.reset_table_name
1459 1460 1461 1462
      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 = ""
1463
      Reminder.reset_table_name
1464
    end
1465 1466

    def test_add_drop_table_with_prefix_and_suffix
1467
      assert !Reminder.table_exists?
1468 1469
      ActiveRecord::Base.table_name_prefix = 'prefix_'
      ActiveRecord::Base.table_name_suffix = '_suffix'
1470
      Reminder.reset_table_name
1471
      Reminder.reset_sequence_name
1472 1473 1474 1475 1476
      WeNeedReminders.up
      assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
      assert_equal "hello world", Reminder.find(:first).content

      WeNeedReminders.down
1477
      assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
1478
    ensure
1479 1480
      ActiveRecord::Base.table_name_prefix = ''
      ActiveRecord::Base.table_name_suffix = ''
1481
      Reminder.reset_table_name
1482
      Reminder.reset_sequence_name
1483
    end
1484

1485 1486
    def test_create_table_with_binary_column
      Person.connection.drop_table :binary_testings rescue nil
1487

1488 1489 1490 1491 1492
      assert_nothing_raised {
        Person.connection.create_table :binary_testings do |t|
          t.column "data", :binary, :null => false
        end
      }
1493

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

B
Brian Lopez 已提交
1497
      if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
1498 1499 1500 1501
        assert_equal '', data_column.default
      else
        assert_nil data_column.default
      end
1502

1503
      Person.connection.drop_table :binary_testings rescue nil
1504
    end
1505

1506
    def test_migrator_with_duplicates
1507
      assert_raise(ActiveRecord::DuplicateMigrationVersionError) do
1508
        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
1509 1510
      end
    end
1511

1512
    def test_migrator_with_duplicate_names
1513
      assert_raise(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
1514 1515 1516 1517
        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
      end
    end

1518
    def test_migrator_with_missing_version_numbers
1519 1520 1521
      assert_raise(ActiveRecord::UnknownMigrationVersionError) do
        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
      end
1522
    end
1523

1524 1525 1526 1527
    def test_create_table_with_custom_sequence_name
      return unless current_adapter? :OracleAdapter

      # table name is 29 chars, the standard sequence name will
1528 1529
      # be 33 chars and should be shortened
      assert_nothing_raised do
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555
        begin
          Person.connection.create_table :table_with_name_thats_just_ok do |t|
            t.column :foo, :string, :null => false
          end
        ensure
          Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
        end
      end

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

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

        ensure
          Person.connection.drop_table :table_with_name_thats_just_ok,
                                       :sequence_name => 'suitably_short_seq' rescue nil
        end
      end

      # confirm the custom sequence got dropped
1556
      assert_raise(ActiveRecord::StatementInvalid) do
1557 1558 1559
        Person.connection.execute("select suitably_short_seq.nextval from dual")
      end
    end
1560 1561 1562 1563 1564 1565 1566 1567 1568

    protected
      def with_env_tz(new_tz = 'US/Eastern')
        old_tz, ENV['TZ'] = ENV['TZ'], new_tz
        yield
      ensure
        old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
      end

1569
  end
1570

1571 1572 1573 1574 1575
  class SexyMigrationsTest < ActiveRecord::TestCase
    def test_references_column_type_adds_id
      with_new_table do |t|
        t.expects(:column).with('customer_id', :integer, {})
        t.references :customer
1576
      end
1577
    end
J
Jeremy Kemper 已提交
1578

1579 1580 1581 1582 1583
    def test_references_column_type_with_polymorphic_adds_type
      with_new_table do |t|
        t.expects(:column).with('taggable_type', :string, {})
        t.expects(:column).with('taggable_id', :integer, {})
        t.references :taggable, :polymorphic => true
1584
      end
1585
    end
J
Jeremy Kemper 已提交
1586

1587 1588 1589 1590 1591
    def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
      with_new_table do |t|
        t.expects(:column).with('taggable_type', :string, {:null => false})
        t.expects(:column).with('taggable_id', :integer, {:null => false})
        t.references :taggable, :polymorphic => true, :null => false
1592
      end
1593
    end
J
Jeremy Kemper 已提交
1594

1595 1596 1597 1598
    def test_belongs_to_works_like_references
      with_new_table do |t|
        t.expects(:column).with('customer_id', :integer, {})
        t.belongs_to :customer
1599
      end
1600
    end
J
Jeremy Kemper 已提交
1601

1602 1603 1604 1605 1606
    def test_timestamps_creates_updated_at_and_created_at
      with_new_table do |t|
        t.expects(:column).with(:created_at, :datetime, kind_of(Hash))
        t.expects(:column).with(:updated_at, :datetime, kind_of(Hash))
        t.timestamps
1607
      end
1608
    end
J
Jeremy Kemper 已提交
1609

1610 1611 1612 1613 1614
    def test_integer_creates_integer_column
      with_new_table do |t|
        t.expects(:column).with(:foo, 'integer', {})
        t.expects(:column).with(:bar, 'integer', {})
        t.integer :foo, :bar
1615
      end
1616
    end
J
Jeremy Kemper 已提交
1617

1618 1619 1620 1621 1622
    def test_string_creates_string_column
      with_new_table do |t|
        t.expects(:column).with(:foo, 'string', {})
        t.expects(:column).with(:bar, 'string', {})
        t.string :foo, :bar
1623
      end
1624
    end
J
Jeremy Kemper 已提交
1625

1626
    if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLite3Adapter) || current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
1627
      def test_xml_creates_xml_column
1628 1629
        type = current_adapter?(:PostgreSQLAdapter) ? 'xml' : :text

1630
        with_new_table do |t|
1631
          t.expects(:column).with(:data, type, {})
1632 1633 1634
          t.xml :data
        end
      end
1635 1636 1637 1638 1639 1640 1641 1642
    else
      def test_xml_creates_xml_column
        with_new_table do |t|
          assert_raises(NotImplementedError) do
            t.xml :data
          end
        end
      end
1643 1644
    end

1645 1646 1647 1648
    protected
    def with_new_table
      Person.connection.create_table :delete_me, :force => true do |t|
        yield t
1649
      end
1650 1651 1652
    ensure
      Person.connection.drop_table :delete_me rescue nil
    end
J
Jeremy Kemper 已提交
1653

1654
  end # SexyMigrationsTest
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
  class MigrationLoggerTest < ActiveRecord::TestCase
    def test_migration_should_be_run_without_logger
      previous_logger = ActiveRecord::Base.logger
      ActiveRecord::Base.logger = nil
      assert_nothing_raised do
        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
      end
    ensure
      ActiveRecord::Base.logger = previous_logger
    end
  end

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

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

      Person.reset_column_information
      assert Person.column_methods_hash.include?(:last_name)

      assert_nothing_raised do
A
Aaron Patterson 已提交
1680 1681 1682 1683 1684
        proxies = ActiveRecord::Migrator.down(
          MIGRATIONS_ROOT + "/interleaved/pass_3")
        names = proxies.map(&:name)
        assert names.include?('InterleavedPeopleHaveLastNames')
        assert names.include?('InterleavedInnocentJointable')
1685 1686 1687 1688
      end
    end
  end

1689 1690 1691 1692 1693 1694 1695
  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
      end

1696
      assert_nothing_raised do
1697
        connection.add_index :values, :value
1698 1699
        connection.remove_index :values, :column => :value
      end
1700 1701 1702 1703 1704 1705

      connection.drop_table :values rescue nil
    end
  end


1706 1707 1708 1709
  class ChangeTableMigrationsTest < ActiveRecord::TestCase
    def setup
      @connection = Person.connection
      @connection.create_table :delete_me, :force => true do |t|
1710
      end
1711
    end
1712

1713 1714 1715
    def teardown
      Person.connection.drop_table :delete_me rescue nil
    end
1716

1717 1718 1719 1720
    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
1721
      end
1722
    end
1723

1724 1725 1726 1727
    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
1728
      end
1729
    end
1730

1731 1732 1733 1734
    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
1735
      end
1736
    end
1737

1738 1739 1740 1741
    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
1742
      end
1743
    end
1744

1745 1746 1747 1748 1749
    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
1750
      end
1751
    end
1752

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

1761 1762 1763 1764 1765
    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
1766
      end
1767
    end
1768

1769 1770 1771 1772 1773
    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
1774
      end
1775
    end
1776

1777 1778 1779 1780
    def test_timestamps_creates_updated_at_and_created_at
      with_change_table do |t|
        @connection.expects(:add_timestamps).with(:delete_me)
        t.timestamps
1781
      end
1782
    end
1783

1784 1785 1786 1787
    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
1788
      end
1789
    end
1790

1791 1792 1793
    def string_column
      if current_adapter?(:PostgreSQLAdapter)
        "character varying(255)"
1794 1795
      elsif current_adapter?(:OracleAdapter)
        'VARCHAR2(255)'
1796 1797
      else
        'varchar(255)'
1798
      end
1799
    end
1800

1801
    def integer_column
B
Brian Lopez 已提交
1802
      if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
1803
        'int(11)'
1804 1805
      elsif current_adapter?(:OracleAdapter)
        'NUMBER(38)'
1806 1807
      else
        'integer'
1808
      end
1809
    end
1810

1811 1812 1813 1814 1815
    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
1816
      end
1817
    end
1818

1819 1820 1821 1822 1823
    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
1824
      end
1825
    end
1826

1827 1828 1829 1830
    def test_column_creates_column
      with_change_table do |t|
        @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
        t.column :bar, :integer
1831
      end
1832
    end
1833

1834 1835 1836 1837
    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
1838
      end
1839
    end
1840

1841 1842 1843 1844
    def test_index_creates_index
      with_change_table do |t|
        @connection.expects(:add_index).with(:delete_me, :bar, {})
        t.index :bar
1845
      end
1846
    end
1847

1848 1849 1850 1851
    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
1852
      end
1853
    end
1854

1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868
    def test_index_exists
      with_change_table do |t|
        @connection.expects(:index_exists?).with(:delete_me, :bar, {})
        t.index_exists?(:bar)
      end
    end

    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)
      end
    end

1869 1870 1871 1872
    def test_change_changes_column
      with_change_table do |t|
        @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
        t.change :bar, :string
1873
      end
1874
    end
1875

1876 1877 1878 1879
    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
1880
      end
1881
    end
1882

1883 1884 1885 1886
    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
1887
      end
1888
    end
1889

1890 1891 1892 1893
    def test_remove_drops_single_column
      with_change_table do |t|
        @connection.expects(:remove_column).with(:delete_me, [:bar])
        t.remove :bar
1894
      end
1895
    end
1896

1897 1898 1899 1900
    def test_remove_drops_multiple_columns
      with_change_table do |t|
        @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
        t.remove :bar, :baz
1901
      end
1902
    end
1903

1904 1905 1906 1907
    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
1908
      end
1909
    end
1910

1911 1912 1913 1914
    def test_rename_renames_column
      with_change_table do |t|
        @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
        t.rename :bar, :baz
1915
      end
1916
    end
1917

1918 1919 1920 1921
    protected
    def with_change_table
      Person.connection.change_table :delete_me do |t|
        yield t
1922
      end
1923 1924
    end
  end
1925

1926 1927 1928 1929 1930 1931
  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
1932

1933 1934
      def teardown
        Person.connection.drop_table(:delete_me) rescue nil
1935 1936
      end

1937 1938 1939 1940 1941 1942 1943 1944 1945 1946
      def test_adding_multiple_columns
        assert_queries(1) do
          with_bulk_change_table do |t|
            t.column :name, :string
            t.string :qualification, :experience
            t.integer :age, :default => 0
            t.date :birthdate
            t.timestamps
          end
        end
1947

1948 1949 1950
        assert_equal 8, columns.size
        [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
        assert_equal 0, column(:age).default
1951 1952
      end

1953
      def test_removing_columns
1954
        with_bulk_change_table do |t|
1955
          t.string :qualification, :experience
1956 1957
        end

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

1960 1961 1962 1963 1964 1965 1966 1967 1968
        assert_queries(1) do
          with_bulk_change_table do |t|
            t.remove :qualification, :experience
            t.string :qualification_experience
          end
        end

        [:qualification, :experience].each {|c| assert ! column(c) }
        assert column(:qualification_experience)
1969 1970
      end

1971
      def test_adding_indexes
1972
        with_bulk_change_table do |t|
1973 1974 1975
          t.string :username
          t.string :name
          t.integer :age
1976 1977
        end

R
R.T. Lechow 已提交
1978
        # Adding an index fires a query every time to check if an index already exists or not
1979 1980 1981 1982 1983 1984
        assert_queries(3) do
          with_bulk_change_table do |t|
            t.index :username, :unique => true, :name => :awesome_username_index
            t.index [:name, :age]
          end
        end
1985

1986
        assert_equal 2, indexes.size
1987

1988 1989 1990
        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
1991

1992
        assert index(:awesome_username_index).unique
1993 1994
      end

1995
      def test_removing_index
1996
        with_bulk_change_table do |t|
1997 1998
          t.string :name
          t.index :name
1999 2000
        end

2001
        assert index(:index_delete_me_on_name)
2002

2003 2004 2005 2006 2007 2008
        assert_queries(3) do
          with_bulk_change_table do |t|
            t.remove_index :name
            t.index :name, :name => :new_name_index, :unique => true
          end
        end
2009

2010
        assert ! index(:index_delete_me_on_name)
2011

2012 2013 2014
        new_name_index = index(:new_name_index)
        assert new_name_index.unique
      end
2015

2016
      def test_changing_columns
2017
        with_bulk_change_table do |t|
2018 2019
          t.string :name
          t.date :birthdate
2020 2021
        end

2022 2023
        assert ! column(:name).default
        assert_equal :date, column(:birthdate).type
2024

2025 2026 2027 2028
        # 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
2029 2030 2031 2032 2033 2034 2035 2036 2037
          with_bulk_change_table do |t|
            t.change :name, :string, :default => 'NONAME'
            t.change :birthdate, :datetime
          end
        end

        assert_equal 'NONAME', column(:name).default
        assert_equal :datetime, column(:birthdate).type
      end
2038

2039
      protected
2040

2041 2042 2043 2044 2045 2046 2047
      def with_bulk_change_table
        # Reset columns/indexes cache as we're changing the table
        @columns = @indexes = nil

        Person.connection.change_table(:delete_me, :bulk => true) do |t|
          yield t
        end
2048 2049
      end

2050 2051 2052
      def column(name)
        columns.detect {|c| c.name == name.to_s }
      end
2053

2054 2055 2056
      def columns
        @columns ||= Person.connection.columns('delete_me')
      end
2057

2058 2059 2060
      def index(name)
        indexes.detect {|i| i.name == name.to_s }
      end
2061

2062 2063 2064 2065
      def indexes
        @indexes ||= Person.connection.indexes('delete_me')
      end
    end # AlterTableMigrationsTest
2066 2067 2068

  end

2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084
  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"})
2085 2086
      assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb")
      assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb")
2087
      assert_equal [@migrations_path + "/4_people_have_hobbies.rb", @migrations_path + "/5_people_have_descriptions.rb"], copied.map(&:filename)
2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102

      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
2103 2104
      sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
      sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
2105
      ActiveRecord::Migration.copy(@migrations_path, sources)
2106 2107 2108 2109
      assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb")
      assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb")
      assert File.exists?(@migrations_path + "/6_create_articles.rb")
      assert File.exists?(@migrations_path + "/7_create_comments.rb")
2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121

      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

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

A
Aaron Patterson 已提交
2122
      Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
2123
        copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
2124 2125 2126 2127
        assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
        assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
        expected = [@migrations_path + "/20100726101010_people_have_hobbies.rb",
                    @migrations_path + "/20100726101011_people_have_descriptions.rb"]
2128
        assert_equal expected, copied.map(&:filename)
2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143

        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?
      end
    ensure
      clear
    end

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

      sources = ActiveSupport::OrderedHash.new
2144 2145
      sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
      sources[:omg]     = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"
2146

A
Aaron Patterson 已提交
2147
      Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
2148
        copied = ActiveRecord::Migration.copy(@migrations_path, sources)
2149 2150 2151 2152
        assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
        assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
        assert File.exists?(@migrations_path + "/20100726101012_create_articles.rb")
        assert File.exists?(@migrations_path + "/20100726101013_create_comments.rb")
2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166
        assert_equal 4, copied.length

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

    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"]

A
Aaron Patterson 已提交
2167
      Time.travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
2168
        ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
2169 2170
        assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.rb")
        assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.rb")
2171 2172 2173 2174 2175 2176 2177 2178 2179

        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?
      end
    ensure
      clear
    end
2180

2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198
    def test_skipping_migrations
      @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
      @existing_migrations = Dir[@migrations_path + "/*.rb"]

      sources = ActiveSupport::OrderedHash.new
      sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"

      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

      assert_equal 2, skipped.length
      assert_equal ["bukkits PeopleHaveHobbies", "bukkits PeopleHaveDescriptions"], skipped
    ensure
      clear
    end

2199 2200 2201 2202
    def test_copying_migrations_to_non_existing_directory
      @migrations_path = MIGRATIONS_ROOT + "/non_existing"
      @existing_migrations = []

A
Aaron Patterson 已提交
2203
      Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
2204 2205 2206 2207 2208 2209 2210 2211 2212 2213
        copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
        assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
        assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
        assert_equal 2, copied.length
      end
    ensure
      clear
      Dir.delete(@migrations_path)
    end

2214 2215 2216 2217
    def test_copying_migrations_to_empty_directory
      @migrations_path = MIGRATIONS_ROOT + "/empty"
      @existing_migrations = []

A
Aaron Patterson 已提交
2218
      Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
2219
        copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
2220 2221
        assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
        assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
2222 2223 2224 2225 2226
        assert_equal 2, copied.length
      end
    ensure
      clear
    end
2227
  end
2228
end