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

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

require MIGRATIONS_ROOT + "/valid/1_people_have_last_names"
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 17 18 19 20 21 22 23 24 25
  class ActiveRecord::Migration
    class <<self
      attr_accessor :message_count
      def puts(text="")
        self.message_count ||= 0
        self.message_count += 1
      end
    end
  end

26
  class MigrationTest < ActiveRecord::TestCase
J
Jamis Buck 已提交
27
    self.use_transactional_fixtures = false
J
Jeremy Kemper 已提交
28

29
    fixtures :people
J
Jamis Buck 已提交
30

31
    def setup
J
Jamis Buck 已提交
32 33
      ActiveRecord::Migration.verbose = true
      PeopleHaveLastNames.message_count = 0
34 35 36 37
    end

    def teardown
      ActiveRecord::Base.connection.initialize_schema_information
38
      ActiveRecord::Base.connection.update "UPDATE #{ActiveRecord::Migrator.schema_info_table_name} SET version = 0"
39

40 41 42
      %w(reminders people_reminders prefix_reminders_suffix).each do |table|
        Reminder.connection.drop_table(table) rescue nil
      end
43 44
      Reminder.reset_column_information

45
      %w(last_name key bio age height wealth birthday favorite_day
46
         moment_of_truth male administrator funny).each do |column|
47 48
        Person.connection.remove_column('people', column) rescue nil
      end
49
      Person.connection.remove_column("people", "first_name") rescue nil
50
      Person.connection.remove_column("people", "middle_name") rescue nil
51
      Person.connection.add_column("people", "first_name", :string, :limit => 40)
52 53
      Person.reset_column_information
    end
54

55
    def test_add_index
56 57 58
      # 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
59
      Person.connection.add_column "people", "administrator", :boolean
60

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

64
      # Orcl nds shrt indx nms.  Sybs 2.
65 66
      # OpenBase does not have named indexes.  You must specify a single column name
      unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter)
67 68 69 70 71 72 73 74 75
        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"]) }
        assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
        assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
        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") }
        assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
        assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
      end
76

77
      # quoting
78
      # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
79 80 81 82 83
      # OpenBase does not have named indexes.  You must specify a single column name
      unless current_adapter?(:OpenBaseAdapter)
        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 已提交
84

85
      # Sybase adapter does not support indexes on :boolean columns
86 87
      # OpenBase does not have named indexes.  You must specify a single column
      unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
88 89 90
        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
91
    end
92 93 94 95 96 97 98 99 100 101 102

    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
103 104

    def test_create_table_with_not_null_column
105 106 107 108
      assert_nothing_raised do
        Person.connection.create_table :testings do |t|
          t.column :foo, :string, :null => false
        end
109 110 111 112 113 114 115 116
      end

      assert_raises(ActiveRecord::StatementInvalid) do
        Person.connection.execute "insert into testings (foo) values (NULL)"
      end
    ensure
      Person.connection.drop_table :testings rescue nil
    end
117 118

    def test_create_table_with_defaults
119 120 121
      # MySQL doesn't allow defaults on TEXT or BLOB columns.
      mysql = current_adapter?(:MysqlAdapter)

122 123 124 125 126
      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
127
        t.column :five, :text, :default => "hello" unless mysql
128 129 130 131 132 133 134
      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" }
135
      five = columns.detect { |c| c.name == "five" } unless mysql
136 137

      assert_equal "hello", one.default
138 139
      assert_equal true, two.default
      assert_equal false, three.default
140
      assert_equal 1, four.default
141
      assert_equal "hello", five.default unless mysql
142 143 144 145

    ensure
      Person.connection.drop_table :testings rescue nil
    end
146 147

    def test_create_table_with_limits
148 149 150
      assert_nothing_raised do
        Person.connection.create_table :testings do |t|
          t.column :foo, :string, :limit => 255
151

152
          t.column :default_int, :integer
153

154 155 156 157
          t.column :one_int,   :integer, :limit => 1
          t.column :four_int,  :integer, :limit => 4
          t.column :eight_int, :integer, :limit => 8
        end
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
      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"   }

      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
174 175 176 177 178
      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
179 180 181 182
      end
    ensure
      Person.connection.drop_table :testings rescue nil
    end
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
    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

      assert_equal %w(foo testings_id), Person.connection.columns(:testings).map { |c| c.name }.sort
    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

      assert_equal %w(foo testingsid), Person.connection.columns(:testings).map { |c| c.name }.sort
    ensure
      Person.connection.drop_table :testings rescue nil
      ActiveRecord::Base.primary_key_prefix_type = nil
    end


211 212 213
    # SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL
    # column to a table without a default value.
    unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :SQLiteAdapter)
214 215 216 217 218
      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
219

220 221 222 223 224 225 226
        assert_raises(ActiveRecord::StatementInvalid) do
          Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
        end
      ensure
        Person.connection.drop_table :testings rescue nil
      end
    end
227

228
    def test_add_column_not_null_with_default
229
      Person.connection.create_table :testings do |t|
230 231
        t.column :foo, :string
      end
J
Jeremy Kemper 已提交
232 233

      con = Person.connection
234
      Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
235
      Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
236
      Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
237
      assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
238 239

      assert_raises(ActiveRecord::StatementInvalid) do
240 241 242 243 244 245
        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
246 247 248 249
      end
    ensure
      Person.connection.drop_table :testings rescue nil
    end
250

251 252 253 254
    # 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
255
      correct_value = '0012345678901234567890.0123456789'.to_d
256 257 258 259 260 261

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

      # Do a manual insertion
262 263
      if current_adapter?(:OracleAdapter)
        Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
264 265
      elsif current_adapter?(:OpenBaseAdapter)
        Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
266 267 268
      else
        Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
      end
269 270 271 272 273 274

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

      # If this assert fails, that means the SELECT is broken!
275 276 277
      unless current_adapter?(:SQLite3Adapter)
        assert_equal correct_value, row.wealth
      end
278 279 280 281 282 283 284 285 286 287 288 289

      # 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!
290 291 292
      unless current_adapter?(:SQLite3Adapter)
        assert_equal correct_value, row.wealth
      end
293 294 295 296 297 298

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

299 300 301 302 303 304 305 306 307
    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
    
308 309 310 311 312 313
    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
314
      Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
315 316
      Person.connection.add_column "people", "birthday", :datetime
      Person.connection.add_column "people", "favorite_day", :date
317
      Person.connection.add_column "people", "moment_of_truth", :datetime
318
      Person.connection.add_column "people", "male", :boolean
319
      Person.reset_column_information
320

321 322 323 324 325
      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,
326
          :moment_of_truth => "1782-10-10 21:40:18", :male => true
327 328 329
      end

      bob = Person.find(:first)
330 331 332 333 334 335 336
      assert_equal 'bob', bob.first_name
      assert_equal 'bobsen', bob.last_name
      assert_equal "I was born ....", bob.bio
      assert_equal 18, bob.age

      # Test for 30 significent digits (beyond the 16 of float), 10 of them
      # after the decimal place.
J
Jeremy Kemper 已提交
337

338 339 340
      unless current_adapter?(:SQLite3Adapter)
        assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
      end
J
Jeremy Kemper 已提交
341

342
      assert_equal true, bob.male?
343

344 345 346 347 348
      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
349

350
      if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
351
        # Sybase, and Oracle don't differentiate between date/time
352 353 354 355 356
        assert_equal Time, bob.favorite_day.class
      else
        assert_equal Date, bob.favorite_day.class
      end

357
      # Test DateTime column and defaults, including timezone.
J
Jeremy Kemper 已提交
358
      # FIXME: moment of truth may be Time on 64-bit platforms.
359
      if bob.moment_of_truth.is_a?(DateTime)
360
        assert_equal DateTime.local_offset, bob.moment_of_truth.offset
361 362
        assert_not_equal 0, bob.moment_of_truth.offset
        assert_not_equal "Z", bob.moment_of_truth.zone
363
        assert_equal DateTime::ITALY, bob.moment_of_truth.start
364
      end
365

366
      assert_equal TrueClass, bob.male?.class
367
      assert_kind_of BigDecimal, bob.wealth
368 369
    end

370 371 372 373 374
    if current_adapter?(:MysqlAdapter)
      def test_unabstracted_database_dependent_types
        Person.delete_all

        ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
375
        Person.reset_column_information
376
        Person.create :intelligence_quotient => 300
J
Jeremy Kemper 已提交
377
        jonnyg = Person.find(:first)
378 379 380
        assert_equal 127, jonnyg.intelligence_quotient
        jonnyg.destroy
      ensure
381
        ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
382 383 384
      end
    end

385
    def test_add_remove_single_field_using_string_arguments
386 387
      assert !Person.column_methods_hash.include?(:last_name)

388
      ActiveRecord::Migration.add_column 'people', 'last_name', :string
389 390 391

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

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
      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
408 409 410 411

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

413 414
    def test_add_rename
      Person.delete_all
415

416
      begin
417
        Person.connection.add_column "people", "girlfriend", :string
418
        Person.reset_column_information
419 420
        Person.create :girlfriend => 'bobette'

421
        Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
422 423

        Person.reset_column_information
424
        bob = Person.find(:first)
425

426 427 428 429 430
        assert_equal "bobette", bob.exgirlfriend
      ensure
        Person.connection.remove_column("people", "girlfriend") rescue nil
        Person.connection.remove_column("people", "exgirlfriend") rescue nil
      end
431

432
    end
433

434 435
    def test_rename_column_using_symbol_arguments
      begin
436
        names_before = Person.find(:all).map(&:first_name)
437 438 439
        Person.connection.rename_column :people, :first_name, :nick_name
        Person.reset_column_information
        assert Person.column_names.include?("nick_name")
440
        assert_equal names_before, Person.find(:all).map(&:nick_name)
441 442 443 444 445
      ensure
        Person.connection.remove_column("people","nick_name")
        Person.connection.add_column("people","first_name", :string)
      end
    end
446

447 448
    def test_rename_column
      begin
449
        names_before = Person.find(:all).map(&:first_name)
450 451 452
        Person.connection.rename_column "people", "first_name", "nick_name"
        Person.reset_column_information
        assert Person.column_names.include?("nick_name")
453
        assert_equal names_before, Person.find(:all).map(&:nick_name)
454 455 456 457 458
      ensure
        Person.connection.remove_column("people","nick_name")
        Person.connection.add_column("people","first_name", :string)
      end
    end
459

460 461 462 463 464 465 466 467 468 469
    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 已提交
470

471 472 473 474 475
    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
476
      Person.connection.add_index :hats, :hat_name
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
      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
      ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true

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

509 510 511 512
    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 已提交
513

514 515 516 517
        Topic.connection.change_column "topics", "written_on", :datetime, :null => false
        Topic.reset_column_information
      end
    end
518

519 520 521 522
    def test_rename_table
      begin
        ActiveRecord::Base.connection.create_table :octopuses do |t|
          t.column :url, :string
523
        end
524
        ActiveRecord::Base.connection.rename_table :octopuses, :octopi
525

526
        # Using explicit id in insert for compatibility across all databases
J
Jeremy Kemper 已提交
527
        con = ActiveRecord::Base.connection
528
        con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
529
        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')" }
530
        con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
531 532

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

534 535 536 537 538
      ensure
        ActiveRecord::Base.connection.drop_table :octopuses rescue nil
        ActiveRecord::Base.connection.drop_table :octopi rescue nil
      end
    end
J
Jeremy Kemper 已提交
539

540
    def test_change_column_nullability
J
Jeremy Kemper 已提交
541
      Person.delete_all
542 543 544 545 546 547 548 549 550 551
      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
552

553 554 555 556 557 558
    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 已提交
559

560 561 562
        ActiveRecord::Base.connection.rename_table :octopuses, :octopi

        # Using explicit id in insert for compatibility across all databases
J
Jeremy Kemper 已提交
563
        con = ActiveRecord::Base.connection
564
        con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
565
        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')" }
566
        con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
567 568 569 570 571 572 573 574 575

        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

576
    def test_change_column
577 578 579 580 581
      Person.connection.add_column 'people', 'age', :integer
      old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
      assert old_columns.find { |c| c.name == 'age' and c.type == :integer }

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

583 584 585
      new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
      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 }
586 587 588 589

      old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
      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 }
590
      new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
591 592
      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 }
593
      assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
594
    end
J
Jeremy Kemper 已提交
595

596 597 598 599
    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 已提交
600

601 602 603 604
      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
605 606
    ensure
      Person.connection.remove_column("people", "contributor") rescue nil
607
    end
608 609

    def test_change_column_with_new_default
610
      Person.connection.add_column "people", "administrator", :boolean, :default => true
611
      Person.reset_column_information
612
      assert Person.new.administrator?
613

614
      assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
615
      Person.reset_column_information
616
      assert !Person.new.administrator?
617 618
    ensure
      Person.connection.remove_column("people", "administrator") rescue nil
619
    end
J
Jeremy Kemper 已提交
620

621 622 623 624 625
    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
626 627 628 629 630 631 632 633 634 635 636 637 638

    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 }

      assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
    ensure
      Person.connection.drop_table :testings rescue nil
    end

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

    def test_add_table
646
      assert !Reminder.table_exists?
647

648
      WeNeedReminders.up
J
Jeremy Kemper 已提交
649 650

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

653 654 655 656
      WeNeedReminders.down
      assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
    end

657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
    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
692
      # precision/scale explicitly left out.  By the SQL standard, numbers
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
      # assigned to this field should be truncated but that's seldom respected.
      if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
        # - PostgreSQL changes the SQL spec on columns declared simply as
        # "decimal" to something more useful: instead of being given a scale
        # of 0, they take on the compile-time limit for precision and scale,
        # so the following should succeed unless you have used really wacky
        # compilation options
        # - SQLite2 has the default behavior of preserving all data sent in,
        # so this happens there too
        assert_kind_of BigDecimal, b.value_of_e
        assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
      elsif current_adapter?(:SQLiteAdapter)
        # - SQLite3 stores a float, in violation of SQL
        assert_kind_of BigDecimal, b.value_of_e
        assert_equal BigDecimal("2.71828182845905"), b.value_of_e
      elsif current_adapter?(:SQLServer)
        # - SQL Server rounds instead of truncating
        assert_kind_of Fixnum, b.value_of_e
        assert_equal 3, b.value_of_e
      else
        # - SQL standard is an integer
        assert_kind_of Fixnum, b.value_of_e
        assert_equal 2, b.value_of_e
      end

      GiveMeBigNumbers.down
      assert_raises(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
    end

722 723
    def test_migrator
      assert !Person.column_methods_hash.include?(:last_name)
724
      assert !Reminder.table_exists?
725

726
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
727

728
      assert_equal 3, ActiveRecord::Migrator.current_version
729 730 731 732 733
      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

734
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
735 736 737 738 739 740 741 742 743

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

    def test_migrator_one_up
      assert !Person.column_methods_hash.include?(:last_name)
744
      assert !Reminder.table_exists?
745

746
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
747 748 749

      Person.reset_column_information
      assert Person.column_methods_hash.include?(:last_name)
750
      assert !Reminder.table_exists?
751

752
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
753 754 755 756

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

758
    def test_migrator_one_down
759
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
760

761
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
762 763 764

      Person.reset_column_information
      assert Person.column_methods_hash.include?(:last_name)
765
      assert !Reminder.table_exists?
766
    end
767

768
    def test_migrator_one_up_one_down
769 770
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
771 772

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

J
Jamis Buck 已提交
776
    def test_migrator_verbosity
777
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
J
Jamis Buck 已提交
778 779 780
      assert PeopleHaveLastNames.message_count > 0
      PeopleHaveLastNames.message_count = 0

781
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
J
Jamis Buck 已提交
782 783 784
      assert PeopleHaveLastNames.message_count > 0
      PeopleHaveLastNames.message_count = 0
    end
785

J
Jamis Buck 已提交
786 787
    def test_migrator_verbosity_off
      PeopleHaveLastNames.verbose = false
788
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
J
Jamis Buck 已提交
789
      assert PeopleHaveLastNames.message_count.zero?
790
      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
J
Jamis Buck 已提交
791 792
      assert PeopleHaveLastNames.message_count.zero?
    end
793

794
    def test_migrator_going_down_due_to_version_target
795 796
      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
797 798

      assert !Person.column_methods_hash.include?(:last_name)
799
      assert !Reminder.table_exists?
800

801
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
802 803 804 805 806 807

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

    def test_schema_info_table_name
      ActiveRecord::Base.table_name_prefix = "prefix_"
      ActiveRecord::Base.table_name_suffix = "_suffix"
812
      Reminder.reset_table_name
813 814 815
      assert_equal "prefix_schema_info_suffix", ActiveRecord::Migrator.schema_info_table_name
      ActiveRecord::Base.table_name_prefix = ""
      ActiveRecord::Base.table_name_suffix = ""
816
      Reminder.reset_table_name
817
      assert_equal "schema_info", ActiveRecord::Migrator.schema_info_table_name
818 819 820
    ensure
      ActiveRecord::Base.table_name_prefix = ""
      ActiveRecord::Base.table_name_suffix = ""
821
    end
822

823 824 825 826
    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)
827
      Reminder.reset_table_name
828
      assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
829

830 831 832 833 834
      # 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'
835
      Reminder.reset_table_name
836 837 838
      assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
      Reminder.table_name_prefix = ''
      Reminder.table_name_suffix = ''
839
      Reminder.reset_table_name
840 841

      # Use AR::Base's prefix/suffix if string or symbol is given
842 843
      ActiveRecord::Base.table_name_prefix = "prefix_"
      ActiveRecord::Base.table_name_suffix = "_suffix"
844
      Reminder.reset_table_name
845 846 847 848
      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 = ""
849
      Reminder.reset_table_name
850
    end
851 852

    def test_add_drop_table_with_prefix_and_suffix
853
      assert !Reminder.table_exists?
854 855
      ActiveRecord::Base.table_name_prefix = 'prefix_'
      ActiveRecord::Base.table_name_suffix = '_suffix'
856
      Reminder.reset_table_name
857
      Reminder.reset_sequence_name
858 859 860 861 862 863
      WeNeedReminders.up
      assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
      assert_equal "hello world", Reminder.find(:first).content

      WeNeedReminders.down
      assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
864
    ensure
865 866
      ActiveRecord::Base.table_name_prefix = ''
      ActiveRecord::Base.table_name_suffix = ''
867
      Reminder.reset_table_name
868
      Reminder.reset_sequence_name
869
    end
870

871 872
    def test_create_table_with_binary_column
      Person.connection.drop_table :binary_testings rescue nil
873

874 875 876 877 878
      assert_nothing_raised {
        Person.connection.create_table :binary_testings do |t|
          t.column "data", :binary, :null => false
        end
      }
879

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

883
      assert_nil data_column.default
884

885
      Person.connection.drop_table :binary_testings rescue nil
886
    end
887

888 889
    def test_migrator_with_duplicates
      assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
890
        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
891 892
      end
    end
893 894

    def test_migrator_with_missing_version_numbers
895
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
896
      assert !Person.column_methods_hash.include?(:middle_name)
J
Jeremy Kemper 已提交
897
      assert_equal 4, ActiveRecord::Migrator.current_version
J
Jeremy Kemper 已提交
898

899
      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 2)
J
Jeremy Kemper 已提交
900 901
      Person.reset_column_information
      assert !Reminder.table_exists?
J
Jeremy Kemper 已提交
902
      assert Person.column_methods_hash.include?(:last_name)
J
Jeremy Kemper 已提交
903
      assert_equal 2, ActiveRecord::Migrator.current_version
904
    end
905

906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
    def test_create_table_with_custom_sequence_name
      return unless current_adapter? :OracleAdapter

      # table name is 29 chars, the standard sequence name will
      # be 33 chars and fail
      assert_raises(ActiveRecord::StatementInvalid) do
        begin
          Person.connection.create_table :table_with_name_thats_just_ok do |t|
            t.column :foo, :string, :null => false
          end
        ensure
          Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
        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
      assert_raises(ActiveRecord::StatementInvalid) do
        Person.connection.execute("select suitably_short_seq.nextval from dual")
      end
    end
942
  end
943 944

  uses_mocha 'Sexy migration tests' do
945
    class SexyMigrationsTest < ActiveRecord::TestCase
946 947 948 949 950 951
      def test_references_column_type_adds_id
        with_new_table do |t|
          t.expects(:column).with('customer_id', :integer, {})
          t.references :customer
        end
      end
J
Jeremy Kemper 已提交
952

M
Michael Koziarski 已提交
953
      def test_references_column_type_with_polymorphic_adds_type
954 955 956 957 958 959
        with_new_table do |t|
          t.expects(:column).with('taggable_type', :string, {})
          t.expects(:column).with('taggable_id', :integer, {})
          t.references :taggable, :polymorphic => true
        end
      end
J
Jeremy Kemper 已提交
960

961 962 963 964 965 966 967
      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
        end
      end
J
Jeremy Kemper 已提交
968

969 970 971 972 973 974
      def test_belongs_to_works_like_references
        with_new_table do |t|
          t.expects(:column).with('customer_id', :integer, {})
          t.belongs_to :customer
        end
      end
J
Jeremy Kemper 已提交
975

976 977 978 979 980 981 982
      def test_timestamps_creates_updated_at_and_created_at
        with_new_table do |t|
          t.expects(:column).with(:created_at, :datetime)
          t.expects(:column).with(:updated_at, :datetime)
          t.timestamps
        end
      end
J
Jeremy Kemper 已提交
983

984 985 986 987 988 989 990
      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
        end
      end
J
Jeremy Kemper 已提交
991

992 993 994 995 996 997 998
      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
        end
      end
J
Jeremy Kemper 已提交
999

1000 1001 1002 1003 1004 1005 1006 1007
      protected
      def with_new_table
        Person.connection.create_table :delete_me do |t|
          yield t
        end
      ensure
        Person.connection.drop_table :delete_me rescue nil
      end
J
Jeremy Kemper 已提交
1008

1009 1010
    end # SexyMigrationsTest
  end # uses_mocha
1011
end