Merged updates from trunk into stable branch
[feedcatcher.git] / vendor / rails / activerecord / test / cases / migration_test.rb
1 require "cases/helper"
2 require 'bigdecimal/util'
3
4 require 'models/person'
5 require 'models/topic'
6 require 'models/developer'
7
8 require MIGRATIONS_ROOT + "/valid/1_people_have_last_names"
9 require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
10 require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
11 require MIGRATIONS_ROOT + "/interleaved/pass_3/2_i_raise_on_down"
12
13 if ActiveRecord::Base.connection.supports_migrations?
14 class BigNumber < ActiveRecord::Base; end
15
16 class Reminder < ActiveRecord::Base; end
17
18 class ActiveRecord::Migration
19 class <<self
20 attr_accessor :message_count
21 def puts(text="")
22 self.message_count ||= 0
23 self.message_count += 1
24 end
25 end
26 end
27
28 class MigrationTest < ActiveRecord::TestCase
29 self.use_transactional_fixtures = false
30
31 fixtures :people
32
33 def setup
34 ActiveRecord::Migration.verbose = true
35 PeopleHaveLastNames.message_count = 0
36 end
37
38 def teardown
39 ActiveRecord::Base.connection.initialize_schema_migrations_table
40 ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
41
42 %w(reminders people_reminders prefix_reminders_suffix).each do |table|
43 Reminder.connection.drop_table(table) rescue nil
44 end
45 Reminder.reset_column_information
46
47 %w(last_name key bio age height wealth birthday favorite_day
48 moment_of_truth male administrator funny).each do |column|
49 Person.connection.remove_column('people', column) rescue nil
50 end
51 Person.connection.remove_column("people", "first_name") rescue nil
52 Person.connection.remove_column("people", "middle_name") rescue nil
53 Person.connection.add_column("people", "first_name", :string, :limit => 40)
54 Person.reset_column_information
55 end
56
57 def test_add_index
58 # Limit size of last_name and key columns to support Firebird index limitations
59 Person.connection.add_column "people", "last_name", :string, :limit => 100
60 Person.connection.add_column "people", "key", :string, :limit => 100
61 Person.connection.add_column "people", "administrator", :boolean
62
63 assert_nothing_raised { Person.connection.add_index("people", "last_name") }
64 assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
65
66 # Orcl nds shrt indx nms. Sybs 2.
67 # OpenBase does not have named indexes. You must specify a single column name
68 unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter)
69 assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
70 assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
71 assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
72 assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
73 assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
74 assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
75 assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
76 assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
77 end
78
79 # quoting
80 # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
81 # OpenBase does not have named indexes. You must specify a single column name
82 unless current_adapter?(:OpenBaseAdapter)
83 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)
84 assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
85 assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
86 end
87
88 # Sybase adapter does not support indexes on :boolean columns
89 # OpenBase does not have named indexes. You must specify a single column
90 unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
91 assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
92 assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
93 end
94 end
95
96 def testing_table_with_only_foo_attribute
97 Person.connection.create_table :testings, :id => false do |t|
98 t.column :foo, :string
99 end
100
101 yield Person.connection
102 ensure
103 Person.connection.drop_table :testings rescue nil
104 end
105 protected :testing_table_with_only_foo_attribute
106
107 def test_create_table_without_id
108 testing_table_with_only_foo_attribute do |connection|
109 assert_equal connection.columns(:testings).size, 1
110 end
111 end
112
113 def test_add_column_with_primary_key_attribute
114 testing_table_with_only_foo_attribute do |connection|
115 assert_nothing_raised { connection.add_column :testings, :id, :primary_key }
116 assert_equal connection.columns(:testings).size, 2
117 end
118 end
119
120 def test_create_table_adds_id
121 Person.connection.create_table :testings do |t|
122 t.column :foo, :string
123 end
124
125 assert_equal %w(foo id),
126 Person.connection.columns(:testings).map { |c| c.name }.sort
127 ensure
128 Person.connection.drop_table :testings rescue nil
129 end
130
131 def test_create_table_with_not_null_column
132 assert_nothing_raised do
133 Person.connection.create_table :testings do |t|
134 t.column :foo, :string, :null => false
135 end
136 end
137
138 assert_raise(ActiveRecord::StatementInvalid) do
139 Person.connection.execute "insert into testings (foo) values (NULL)"
140 end
141 ensure
142 Person.connection.drop_table :testings rescue nil
143 end
144
145 def test_create_table_with_defaults
146 # MySQL doesn't allow defaults on TEXT or BLOB columns.
147 mysql = current_adapter?(:MysqlAdapter)
148
149 Person.connection.create_table :testings do |t|
150 t.column :one, :string, :default => "hello"
151 t.column :two, :boolean, :default => true
152 t.column :three, :boolean, :default => false
153 t.column :four, :integer, :default => 1
154 t.column :five, :text, :default => "hello" unless mysql
155 end
156
157 columns = Person.connection.columns(:testings)
158 one = columns.detect { |c| c.name == "one" }
159 two = columns.detect { |c| c.name == "two" }
160 three = columns.detect { |c| c.name == "three" }
161 four = columns.detect { |c| c.name == "four" }
162 five = columns.detect { |c| c.name == "five" } unless mysql
163
164 assert_equal "hello", one.default
165 assert_equal true, two.default
166 assert_equal false, three.default
167 assert_equal 1, four.default
168 assert_equal "hello", five.default unless mysql
169
170 ensure
171 Person.connection.drop_table :testings rescue nil
172 end
173
174 def test_create_table_with_limits
175 assert_nothing_raised do
176 Person.connection.create_table :testings do |t|
177 t.column :foo, :string, :limit => 255
178
179 t.column :default_int, :integer
180
181 t.column :one_int, :integer, :limit => 1
182 t.column :four_int, :integer, :limit => 4
183 t.column :eight_int, :integer, :limit => 8
184 t.column :eleven_int, :integer, :limit => 11
185 end
186 end
187
188 columns = Person.connection.columns(:testings)
189 foo = columns.detect { |c| c.name == "foo" }
190 assert_equal 255, foo.limit
191
192 default = columns.detect { |c| c.name == "default_int" }
193 one = columns.detect { |c| c.name == "one_int" }
194 four = columns.detect { |c| c.name == "four_int" }
195 eight = columns.detect { |c| c.name == "eight_int" }
196 eleven = columns.detect { |c| c.name == "eleven_int" }
197
198 if current_adapter?(:PostgreSQLAdapter)
199 assert_equal 'integer', default.sql_type
200 assert_equal 'smallint', one.sql_type
201 assert_equal 'integer', four.sql_type
202 assert_equal 'bigint', eight.sql_type
203 assert_equal 'integer', eleven.sql_type
204 elsif current_adapter?(:MysqlAdapter)
205 assert_match 'int(11)', default.sql_type
206 assert_match 'tinyint', one.sql_type
207 assert_match 'int', four.sql_type
208 assert_match 'bigint', eight.sql_type
209 assert_match 'int(11)', eleven.sql_type
210 elsif current_adapter?(:OracleAdapter)
211 assert_equal 'NUMBER(38)', default.sql_type
212 assert_equal 'NUMBER(1)', one.sql_type
213 assert_equal 'NUMBER(4)', four.sql_type
214 assert_equal 'NUMBER(8)', eight.sql_type
215 end
216 ensure
217 Person.connection.drop_table :testings rescue nil
218 end
219
220 def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
221 ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
222
223 Person.connection.create_table :testings do |t|
224 t.column :foo, :string
225 end
226
227 assert_equal %w(foo testings_id), Person.connection.columns(:testings).map { |c| c.name }.sort
228 ensure
229 Person.connection.drop_table :testings rescue nil
230 ActiveRecord::Base.primary_key_prefix_type = nil
231 end
232
233 def test_create_table_with_primary_key_prefix_as_table_name
234 ActiveRecord::Base.primary_key_prefix_type = :table_name
235
236 Person.connection.create_table :testings do |t|
237 t.column :foo, :string
238 end
239
240 assert_equal %w(foo testingsid), Person.connection.columns(:testings).map { |c| c.name }.sort
241 ensure
242 Person.connection.drop_table :testings rescue nil
243 ActiveRecord::Base.primary_key_prefix_type = nil
244 end
245
246 def test_create_table_with_force_true_does_not_drop_nonexisting_table
247 if Person.connection.table_exists?(:testings2)
248 Person.connection.drop_table :testings2
249 end
250
251 # using a copy as we need the drop_table method to
252 # continue to work for the ensure block of the test
253 temp_conn = Person.connection.dup
254 temp_conn.expects(:drop_table).never
255 temp_conn.create_table :testings2, :force => true do |t|
256 t.column :foo, :string
257 end
258 ensure
259 Person.connection.drop_table :testings2 rescue nil
260 end
261
262 def test_create_table_with_timestamps_should_create_datetime_columns
263 table_name = :testings
264
265 Person.connection.create_table table_name do |t|
266 t.timestamps
267 end
268 created_columns = Person.connection.columns(table_name)
269
270 created_at_column = created_columns.detect {|c| c.name == 'created_at' }
271 updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
272
273 assert created_at_column.null
274 assert updated_at_column.null
275 ensure
276 Person.connection.drop_table table_name rescue nil
277 end
278
279 def test_create_table_with_timestamps_should_create_datetime_columns_with_options
280 table_name = :testings
281
282 Person.connection.create_table table_name do |t|
283 t.timestamps :null => false
284 end
285 created_columns = Person.connection.columns(table_name)
286
287 created_at_column = created_columns.detect {|c| c.name == 'created_at' }
288 updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
289
290 assert !created_at_column.null
291 assert !updated_at_column.null
292 ensure
293 Person.connection.drop_table table_name rescue nil
294 end
295
296 # Sybase, and SQLite3 will not allow you to add a NOT NULL
297 # column to a table without a default value.
298 unless current_adapter?(:SybaseAdapter, :SQLiteAdapter)
299 def test_add_column_not_null_without_default
300 Person.connection.create_table :testings do |t|
301 t.column :foo, :string
302 end
303 Person.connection.add_column :testings, :bar, :string, :null => false
304
305 assert_raise(ActiveRecord::StatementInvalid) do
306 Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
307 end
308 ensure
309 Person.connection.drop_table :testings rescue nil
310 end
311 end
312
313 def test_add_column_not_null_with_default
314 Person.connection.create_table :testings do |t|
315 t.column :foo, :string
316 end
317
318 con = Person.connection
319 Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
320 Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
321 Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
322 assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
323
324 assert_raise(ActiveRecord::StatementInvalid) do
325 unless current_adapter?(:OpenBaseAdapter)
326 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)"
327 else
328 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)",
329 "Testing Insert","id",2)
330 end
331 end
332 ensure
333 Person.connection.drop_table :testings rescue nil
334 end
335
336 # We specifically do a manual INSERT here, and then test only the SELECT
337 # functionality. This allows us to more easily catch INSERT being broken,
338 # but SELECT actually working fine.
339 def test_native_decimal_insert_manual_vs_automatic
340 correct_value = '0012345678901234567890.0123456789'.to_d
341
342 Person.delete_all
343 Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
344 Person.reset_column_information
345
346 # Do a manual insertion
347 if current_adapter?(:OracleAdapter)
348 Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
349 elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
350 Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
351 else
352 Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
353 end
354
355 # SELECT
356 row = Person.find(:first)
357 assert_kind_of BigDecimal, row.wealth
358
359 # If this assert fails, that means the SELECT is broken!
360 unless current_adapter?(:SQLite3Adapter)
361 assert_equal correct_value, row.wealth
362 end
363
364 # Reset to old state
365 Person.delete_all
366
367 # Now use the Rails insertion
368 assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
369
370 # SELECT
371 row = Person.find(:first)
372 assert_kind_of BigDecimal, row.wealth
373
374 # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
375 unless current_adapter?(:SQLite3Adapter)
376 assert_equal correct_value, row.wealth
377 end
378
379 # Reset to old state
380 Person.connection.del_column "people", "wealth" rescue nil
381 Person.reset_column_information
382 end
383
384 def test_add_column_with_precision_and_scale
385 Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
386 Person.reset_column_information
387
388 wealth_column = Person.columns_hash['wealth']
389 assert_equal 9, wealth_column.precision
390 assert_equal 7, wealth_column.scale
391 end
392
393 def test_native_types
394 Person.delete_all
395 Person.connection.add_column "people", "last_name", :string
396 Person.connection.add_column "people", "bio", :text
397 Person.connection.add_column "people", "age", :integer
398 Person.connection.add_column "people", "height", :float
399 Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
400 Person.connection.add_column "people", "birthday", :datetime
401 Person.connection.add_column "people", "favorite_day", :date
402 Person.connection.add_column "people", "moment_of_truth", :datetime
403 Person.connection.add_column "people", "male", :boolean
404 Person.reset_column_information
405
406 assert_nothing_raised do
407 Person.create :first_name => 'bob', :last_name => 'bobsen',
408 :bio => "I was born ....", :age => 18, :height => 1.78,
409 :wealth => BigDecimal.new("12345678901234567890.0123456789"),
410 :birthday => 18.years.ago, :favorite_day => 10.days.ago,
411 :moment_of_truth => "1782-10-10 21:40:18", :male => true
412 end
413
414 bob = Person.find(:first)
415 assert_equal 'bob', bob.first_name
416 assert_equal 'bobsen', bob.last_name
417 assert_equal "I was born ....", bob.bio
418 assert_equal 18, bob.age
419
420 # Test for 30 significent digits (beyond the 16 of float), 10 of them
421 # after the decimal place.
422
423 unless current_adapter?(:SQLite3Adapter)
424 assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
425 end
426
427 assert_equal true, bob.male?
428
429 assert_equal String, bob.first_name.class
430 assert_equal String, bob.last_name.class
431 assert_equal String, bob.bio.class
432 assert_equal Fixnum, bob.age.class
433 assert_equal Time, bob.birthday.class
434
435 if current_adapter?(:OracleAdapter, :SybaseAdapter)
436 # Sybase, and Oracle don't differentiate between date/time
437 assert_equal Time, bob.favorite_day.class
438 else
439 assert_equal Date, bob.favorite_day.class
440 end
441
442 # Test DateTime column and defaults, including timezone.
443 # FIXME: moment of truth may be Time on 64-bit platforms.
444 if bob.moment_of_truth.is_a?(DateTime)
445
446 with_env_tz 'US/Eastern' do
447 assert_equal DateTime.local_offset, bob.moment_of_truth.offset
448 assert_not_equal 0, bob.moment_of_truth.offset
449 assert_not_equal "Z", bob.moment_of_truth.zone
450 # US/Eastern is -5 hours from GMT
451 assert_equal Rational(-5, 24), bob.moment_of_truth.offset
452 assert_match /\A-05:?00\Z/, bob.moment_of_truth.zone #ruby 1.8.6 uses HH:MM, prior versions use HHMM
453 assert_equal DateTime::ITALY, bob.moment_of_truth.start
454 end
455 end
456
457 assert_equal TrueClass, bob.male?.class
458 assert_kind_of BigDecimal, bob.wealth
459 end
460
461 if current_adapter?(:MysqlAdapter)
462 def test_unabstracted_database_dependent_types
463 Person.delete_all
464
465 ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
466 Person.reset_column_information
467 assert_match /tinyint/, Person.columns_hash['intelligence_quotient'].sql_type
468 ensure
469 ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
470 end
471 end
472
473 def test_add_remove_single_field_using_string_arguments
474 assert !Person.column_methods_hash.include?(:last_name)
475
476 ActiveRecord::Migration.add_column 'people', 'last_name', :string
477
478 Person.reset_column_information
479 assert Person.column_methods_hash.include?(:last_name)
480
481 ActiveRecord::Migration.remove_column 'people', 'last_name'
482
483 Person.reset_column_information
484 assert !Person.column_methods_hash.include?(:last_name)
485 end
486
487 def test_add_remove_single_field_using_symbol_arguments
488 assert !Person.column_methods_hash.include?(:last_name)
489
490 ActiveRecord::Migration.add_column :people, :last_name, :string
491
492 Person.reset_column_information
493 assert Person.column_methods_hash.include?(:last_name)
494
495 ActiveRecord::Migration.remove_column :people, :last_name
496
497 Person.reset_column_information
498 assert !Person.column_methods_hash.include?(:last_name)
499 end
500
501 def test_add_rename
502 Person.delete_all
503
504 begin
505 Person.connection.add_column "people", "girlfriend", :string
506 Person.reset_column_information
507 Person.create :girlfriend => 'bobette'
508
509 Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
510
511 Person.reset_column_information
512 bob = Person.find(:first)
513
514 assert_equal "bobette", bob.exgirlfriend
515 ensure
516 Person.connection.remove_column("people", "girlfriend") rescue nil
517 Person.connection.remove_column("people", "exgirlfriend") rescue nil
518 end
519
520 end
521
522 def test_rename_column_using_symbol_arguments
523 begin
524 names_before = Person.find(:all).map(&:first_name)
525 Person.connection.rename_column :people, :first_name, :nick_name
526 Person.reset_column_information
527 assert Person.column_names.include?("nick_name")
528 assert_equal names_before, Person.find(:all).map(&:nick_name)
529 ensure
530 Person.connection.remove_column("people","nick_name")
531 Person.connection.add_column("people","first_name", :string)
532 end
533 end
534
535 def test_rename_column
536 begin
537 names_before = Person.find(:all).map(&:first_name)
538 Person.connection.rename_column "people", "first_name", "nick_name"
539 Person.reset_column_information
540 assert Person.column_names.include?("nick_name")
541 assert_equal names_before, Person.find(:all).map(&:nick_name)
542 ensure
543 Person.connection.remove_column("people","nick_name")
544 Person.connection.add_column("people","first_name", :string)
545 end
546 end
547
548 def test_rename_column_preserves_default_value_not_null
549 begin
550 default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default
551 assert_equal 70000, default_before
552 Developer.connection.rename_column "developers", "salary", "anual_salary"
553 Developer.reset_column_information
554 assert Developer.column_names.include?("anual_salary")
555 default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default
556 assert_equal 70000, default_after
557 ensure
558 Developer.connection.rename_column "developers", "anual_salary", "salary"
559 Developer.reset_column_information
560 end
561 end
562
563 def test_rename_nonexistent_column
564 ActiveRecord::Base.connection.create_table(:hats) do |table|
565 table.column :hat_name, :string, :default => nil
566 end
567 exception = if current_adapter?(:PostgreSQLAdapter)
568 ActiveRecord::StatementInvalid
569 else
570 ActiveRecord::ActiveRecordError
571 end
572 assert_raise(exception) do
573 Person.connection.rename_column "hats", "nonexistent", "should_fail"
574 end
575 ensure
576 ActiveRecord::Base.connection.drop_table(:hats)
577 end
578
579 def test_rename_column_with_sql_reserved_word
580 begin
581 assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
582 Person.reset_column_information
583 assert Person.column_names.include?("group")
584 ensure
585 Person.connection.remove_column("people", "group") rescue nil
586 Person.connection.add_column("people", "first_name", :string) rescue nil
587 end
588 end
589
590 def test_rename_column_with_an_index
591 ActiveRecord::Base.connection.create_table(:hats) do |table|
592 table.column :hat_name, :string, :limit => 100
593 table.column :hat_size, :integer
594 end
595 Person.connection.add_index :hats, :hat_name
596 assert_nothing_raised do
597 Person.connection.rename_column "hats", "hat_name", "name"
598 end
599 ensure
600 ActiveRecord::Base.connection.drop_table(:hats)
601 end
602
603 def test_remove_column_with_index
604 ActiveRecord::Base.connection.create_table(:hats) do |table|
605 table.column :hat_name, :string, :limit => 100
606 table.column :hat_size, :integer
607 end
608 ActiveRecord::Base.connection.add_index "hats", "hat_size"
609
610 assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
611 ensure
612 ActiveRecord::Base.connection.drop_table(:hats)
613 end
614
615 def test_remove_column_with_multi_column_index
616 ActiveRecord::Base.connection.create_table(:hats) do |table|
617 table.column :hat_name, :string, :limit => 100
618 table.column :hat_size, :integer
619 table.column :hat_style, :string, :limit => 100
620 end
621 ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true
622
623 assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
624 ensure
625 ActiveRecord::Base.connection.drop_table(:hats)
626 end
627
628 def test_change_type_of_not_null_column
629 assert_nothing_raised do
630 Topic.connection.change_column "topics", "written_on", :datetime, :null => false
631 Topic.reset_column_information
632
633 Topic.connection.change_column "topics", "written_on", :datetime, :null => false
634 Topic.reset_column_information
635 end
636 end
637
638 def test_rename_table
639 begin
640 ActiveRecord::Base.connection.create_table :octopuses do |t|
641 t.column :url, :string
642 end
643 ActiveRecord::Base.connection.rename_table :octopuses, :octopi
644
645 # Using explicit id in insert for compatibility across all databases
646 con = ActiveRecord::Base.connection
647 con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
648 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')" }
649 con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
650
651 assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
652
653 ensure
654 ActiveRecord::Base.connection.drop_table :octopuses rescue nil
655 ActiveRecord::Base.connection.drop_table :octopi rescue nil
656 end
657 end
658
659 def test_change_column_nullability
660 Person.delete_all
661 Person.connection.add_column "people", "funny", :boolean
662 Person.reset_column_information
663 assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
664 Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
665 Person.reset_column_information
666 assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
667 Person.connection.change_column "people", "funny", :boolean, :null => true
668 Person.reset_column_information
669 assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
670 end
671
672 def test_rename_table_with_an_index
673 begin
674 ActiveRecord::Base.connection.create_table :octopuses do |t|
675 t.column :url, :string
676 end
677 ActiveRecord::Base.connection.add_index :octopuses, :url
678
679 ActiveRecord::Base.connection.rename_table :octopuses, :octopi
680
681 # Using explicit id in insert for compatibility across all databases
682 con = ActiveRecord::Base.connection
683 con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
684 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')" }
685 con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
686
687 assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
688 assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
689 ensure
690 ActiveRecord::Base.connection.drop_table :octopuses rescue nil
691 ActiveRecord::Base.connection.drop_table :octopi rescue nil
692 end
693 end
694
695 def test_change_column
696 Person.connection.add_column 'people', 'age', :integer
697 old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
698 assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
699
700 assert_nothing_raised { Person.connection.change_column "people", "age", :string }
701
702 new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
703 assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
704 assert new_columns.find { |c| c.name == 'age' and c.type == :string }
705
706 old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
707 assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
708 assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
709 new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
710 assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
711 assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
712 assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
713 end
714
715 def test_change_column_with_nil_default
716 Person.connection.add_column "people", "contributor", :boolean, :default => true
717 Person.reset_column_information
718 assert Person.new.contributor?
719
720 assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
721 Person.reset_column_information
722 assert !Person.new.contributor?
723 assert_nil Person.new.contributor
724 ensure
725 Person.connection.remove_column("people", "contributor") rescue nil
726 end
727
728 def test_change_column_with_new_default
729 Person.connection.add_column "people", "administrator", :boolean, :default => true
730 Person.reset_column_information
731 assert Person.new.administrator?
732
733 assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
734 Person.reset_column_information
735 assert !Person.new.administrator?
736 ensure
737 Person.connection.remove_column("people", "administrator") rescue nil
738 end
739
740 def test_change_column_default
741 Person.connection.change_column_default "people", "first_name", "Tester"
742 Person.reset_column_information
743 assert_equal "Tester", Person.new.first_name
744 end
745
746 def test_change_column_quotes_column_names
747 Person.connection.create_table :testings do |t|
748 t.column :select, :string
749 end
750
751 assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }
752
753 assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
754 ensure
755 Person.connection.drop_table :testings rescue nil
756 end
757
758 def test_keeping_default_and_notnull_constaint_on_change
759 Person.connection.create_table :testings do |t|
760 t.column :title, :string
761 end
762 person_klass = Class.new(Person)
763 person_klass.set_table_name 'testings'
764
765 person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
766 person_klass.reset_column_information
767 assert_equal 99, person_klass.columns_hash["wealth"].default
768 assert_equal false, person_klass.columns_hash["wealth"].null
769 assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
770
771 # change column default to see that column doesn't lose its not null definition
772 person_klass.connection.change_column_default "testings", "wealth", 100
773 person_klass.reset_column_information
774 assert_equal 100, person_klass.columns_hash["wealth"].default
775 assert_equal false, person_klass.columns_hash["wealth"].null
776
777 # rename column to see that column doesn't lose its not null and/or default definition
778 person_klass.connection.rename_column "testings", "wealth", "money"
779 person_klass.reset_column_information
780 assert_nil person_klass.columns_hash["wealth"]
781 assert_equal 100, person_klass.columns_hash["money"].default
782 assert_equal false, person_klass.columns_hash["money"].null
783
784 # change column
785 person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
786 person_klass.reset_column_information
787 assert_equal 1000, person_klass.columns_hash["money"].default
788 assert_equal false, person_klass.columns_hash["money"].null
789
790 # change column, make it nullable and clear default
791 person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
792 person_klass.reset_column_information
793 assert_nil person_klass.columns_hash["money"].default
794 assert_equal true, person_klass.columns_hash["money"].null
795
796 # change_column_null, make it not nullable and set null values to a default value
797 person_klass.connection.execute('UPDATE testings SET money = NULL')
798 person_klass.connection.change_column_null "testings", "money", false, 2000
799 person_klass.reset_column_information
800 assert_nil person_klass.columns_hash["money"].default
801 assert_equal false, person_klass.columns_hash["money"].null
802 assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
803 ensure
804 Person.connection.drop_table :testings rescue nil
805 end
806
807 def test_change_column_default_to_null
808 Person.connection.change_column_default "people", "first_name", nil
809 Person.reset_column_information
810 assert_nil Person.new.first_name
811 end
812
813 def test_add_table
814 assert !Reminder.table_exists?
815
816 WeNeedReminders.up
817
818 assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
819 assert_equal "hello world", Reminder.find(:first).content
820
821 WeNeedReminders.down
822 assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
823 end
824
825 def test_add_table_with_decimals
826 Person.connection.drop_table :big_numbers rescue nil
827
828 assert !BigNumber.table_exists?
829 GiveMeBigNumbers.up
830
831 assert BigNumber.create(
832 :bank_balance => 1586.43,
833 :big_bank_balance => BigDecimal("1000234000567.95"),
834 :world_population => 6000000000,
835 :my_house_population => 3,
836 :value_of_e => BigDecimal("2.7182818284590452353602875")
837 )
838
839 b = BigNumber.find(:first)
840 assert_not_nil b
841
842 assert_not_nil b.bank_balance
843 assert_not_nil b.big_bank_balance
844 assert_not_nil b.world_population
845 assert_not_nil b.my_house_population
846 assert_not_nil b.value_of_e
847
848 # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
849 # is_a?(Bignum)
850 assert_kind_of Integer, b.world_population
851 assert_equal 6000000000, b.world_population
852 assert_kind_of Fixnum, b.my_house_population
853 assert_equal 3, b.my_house_population
854 assert_kind_of BigDecimal, b.bank_balance
855 assert_equal BigDecimal("1586.43"), b.bank_balance
856 assert_kind_of BigDecimal, b.big_bank_balance
857 assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
858
859 # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
860 # precision/scale explicitly left out. By the SQL standard, numbers
861 # assigned to this field should be truncated but that's seldom respected.
862 if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
863 # - PostgreSQL changes the SQL spec on columns declared simply as
864 # "decimal" to something more useful: instead of being given a scale
865 # of 0, they take on the compile-time limit for precision and scale,
866 # so the following should succeed unless you have used really wacky
867 # compilation options
868 # - SQLite2 has the default behavior of preserving all data sent in,
869 # so this happens there too
870 assert_kind_of BigDecimal, b.value_of_e
871 assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
872 elsif current_adapter?(:SQLiteAdapter)
873 # - SQLite3 stores a float, in violation of SQL
874 assert_kind_of BigDecimal, b.value_of_e
875 assert_equal BigDecimal("2.71828182845905"), b.value_of_e
876 else
877 # - SQL standard is an integer
878 assert_kind_of Fixnum, b.value_of_e
879 assert_equal 2, b.value_of_e
880 end
881
882 GiveMeBigNumbers.down
883 assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
884 end
885
886 def test_migrator
887 assert !Person.column_methods_hash.include?(:last_name)
888 assert !Reminder.table_exists?
889
890 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
891
892 assert_equal 3, ActiveRecord::Migrator.current_version
893 Person.reset_column_information
894 assert Person.column_methods_hash.include?(:last_name)
895 assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
896 assert_equal "hello world", Reminder.find(:first).content
897
898 ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
899
900 assert_equal 0, ActiveRecord::Migrator.current_version
901 Person.reset_column_information
902 assert !Person.column_methods_hash.include?(:last_name)
903 assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
904 end
905
906 def test_migrator_one_up
907 assert !Person.column_methods_hash.include?(:last_name)
908 assert !Reminder.table_exists?
909
910 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
911
912 Person.reset_column_information
913 assert Person.column_methods_hash.include?(:last_name)
914 assert !Reminder.table_exists?
915
916 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
917
918 assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
919 assert_equal "hello world", Reminder.find(:first).content
920 end
921
922 def test_migrator_one_down
923 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
924
925 ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
926
927 Person.reset_column_information
928 assert Person.column_methods_hash.include?(:last_name)
929 assert !Reminder.table_exists?
930 end
931
932 def test_migrator_one_up_one_down
933 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
934 ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
935
936 assert !Person.column_methods_hash.include?(:last_name)
937 assert !Reminder.table_exists?
938 end
939
940 def test_migrator_double_up
941 assert_equal(0, ActiveRecord::Migrator.current_version)
942 ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
943 assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) }
944 assert_equal(1, ActiveRecord::Migrator.current_version)
945 end
946
947 def test_migrator_double_down
948 assert_equal(0, ActiveRecord::Migrator.current_version)
949 ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
950 ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1)
951 assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) }
952 assert_equal(0, ActiveRecord::Migrator.current_version)
953 end
954
955 if ActiveRecord::Base.connection.supports_ddl_transactions?
956 def test_migrator_one_up_with_exception_and_rollback
957 assert !Person.column_methods_hash.include?(:last_name)
958
959 e = assert_raise(StandardError) do
960 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
961 end
962
963 assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
964
965 Person.reset_column_information
966 assert !Person.column_methods_hash.include?(:last_name)
967 end
968 end
969
970 def test_finds_migrations
971 migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
972
973 [[1, 'PeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
974 assert_equal migrations[i].version, pair.first
975 assert_equal migrations[i].name, pair.last
976 end
977 end
978
979 def test_finds_pending_migrations
980 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1)
981 migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2").pending_migrations
982
983 assert_equal 1, migrations.size
984 assert_equal migrations[0].version, 3
985 assert_equal migrations[0].name, 'InnocentJointable'
986 end
987
988 def test_only_loads_pending_migrations
989 # migrate up to 1
990 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
991
992 # now unload the migrations that have been defined
993 PeopleHaveLastNames.unloadable
994 ActiveSupport::Dependencies.remove_unloadable_constants!
995
996 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
997
998 assert !defined? PeopleHaveLastNames
999
1000 %w(WeNeedReminders, InnocentJointable).each do |migration|
1001 assert defined? migration
1002 end
1003
1004 ensure
1005 load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
1006 end
1007
1008 def test_migrator_interleaved_migrations
1009 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
1010
1011 assert_nothing_raised do
1012 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
1013 end
1014
1015 Person.reset_column_information
1016 assert Person.column_methods_hash.include?(:last_name)
1017
1018 assert_nothing_raised do
1019 ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
1020 end
1021 end
1022
1023 def test_migrator_db_has_no_schema_migrations_table
1024 ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations;")
1025 assert_nothing_raised do
1026 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
1027 end
1028 end
1029
1030 def test_migrator_verbosity
1031 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1032 assert PeopleHaveLastNames.message_count > 0
1033 PeopleHaveLastNames.message_count = 0
1034
1035 ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
1036 assert PeopleHaveLastNames.message_count > 0
1037 PeopleHaveLastNames.message_count = 0
1038 end
1039
1040 def test_migrator_verbosity_off
1041 PeopleHaveLastNames.verbose = false
1042 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1043 assert PeopleHaveLastNames.message_count.zero?
1044 ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
1045 assert PeopleHaveLastNames.message_count.zero?
1046 end
1047
1048 def test_migrator_going_down_due_to_version_target
1049 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1050 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
1051
1052 assert !Person.column_methods_hash.include?(:last_name)
1053 assert !Reminder.table_exists?
1054
1055 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1056
1057 Person.reset_column_information
1058 assert Person.column_methods_hash.include?(:last_name)
1059 assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
1060 assert_equal "hello world", Reminder.find(:first).content
1061 end
1062
1063 def test_migrator_rollback
1064 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1065 assert_equal(3, ActiveRecord::Migrator.current_version)
1066
1067 ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1068 assert_equal(2, ActiveRecord::Migrator.current_version)
1069
1070 ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1071 assert_equal(1, ActiveRecord::Migrator.current_version)
1072
1073 ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1074 assert_equal(0, ActiveRecord::Migrator.current_version)
1075
1076 ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1077 assert_equal(0, ActiveRecord::Migrator.current_version)
1078 end
1079
1080 def test_schema_migrations_table_name
1081 ActiveRecord::Base.table_name_prefix = "prefix_"
1082 ActiveRecord::Base.table_name_suffix = "_suffix"
1083 Reminder.reset_table_name
1084 assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
1085 ActiveRecord::Base.table_name_prefix = ""
1086 ActiveRecord::Base.table_name_suffix = ""
1087 Reminder.reset_table_name
1088 assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
1089 ensure
1090 ActiveRecord::Base.table_name_prefix = ""
1091 ActiveRecord::Base.table_name_suffix = ""
1092 end
1093
1094 def test_proper_table_name
1095 assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
1096 assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
1097 assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
1098 Reminder.reset_table_name
1099 assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
1100
1101 # Use the model's own prefix/suffix if a model is given
1102 ActiveRecord::Base.table_name_prefix = "ARprefix_"
1103 ActiveRecord::Base.table_name_suffix = "_ARsuffix"
1104 Reminder.table_name_prefix = 'prefix_'
1105 Reminder.table_name_suffix = '_suffix'
1106 Reminder.reset_table_name
1107 assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
1108 Reminder.table_name_prefix = ''
1109 Reminder.table_name_suffix = ''
1110 Reminder.reset_table_name
1111
1112 # Use AR::Base's prefix/suffix if string or symbol is given
1113 ActiveRecord::Base.table_name_prefix = "prefix_"
1114 ActiveRecord::Base.table_name_suffix = "_suffix"
1115 Reminder.reset_table_name
1116 assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
1117 assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
1118 ActiveRecord::Base.table_name_prefix = ""
1119 ActiveRecord::Base.table_name_suffix = ""
1120 Reminder.reset_table_name
1121 end
1122
1123 def test_add_drop_table_with_prefix_and_suffix
1124 assert !Reminder.table_exists?
1125 ActiveRecord::Base.table_name_prefix = 'prefix_'
1126 ActiveRecord::Base.table_name_suffix = '_suffix'
1127 Reminder.reset_table_name
1128 Reminder.reset_sequence_name
1129 WeNeedReminders.up
1130 assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
1131 assert_equal "hello world", Reminder.find(:first).content
1132
1133 WeNeedReminders.down
1134 assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
1135 ensure
1136 ActiveRecord::Base.table_name_prefix = ''
1137 ActiveRecord::Base.table_name_suffix = ''
1138 Reminder.reset_table_name
1139 Reminder.reset_sequence_name
1140 end
1141
1142 def test_create_table_with_binary_column
1143 Person.connection.drop_table :binary_testings rescue nil
1144
1145 assert_nothing_raised {
1146 Person.connection.create_table :binary_testings do |t|
1147 t.column "data", :binary, :null => false
1148 end
1149 }
1150
1151 columns = Person.connection.columns(:binary_testings)
1152 data_column = columns.detect { |c| c.name == "data" }
1153
1154 if current_adapter?(:MysqlAdapter)
1155 assert_equal '', data_column.default
1156 else
1157 assert_nil data_column.default
1158 end
1159
1160 Person.connection.drop_table :binary_testings rescue nil
1161 end
1162
1163 def test_migrator_with_duplicates
1164 assert_raise(ActiveRecord::DuplicateMigrationVersionError) do
1165 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
1166 end
1167 end
1168
1169 def test_migrator_with_duplicate_names
1170 assert_raise(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
1171 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
1172 end
1173 end
1174
1175 def test_migrator_with_missing_version_numbers
1176 assert_raise(ActiveRecord::UnknownMigrationVersionError) do
1177 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
1178 end
1179 end
1180
1181 def test_create_table_with_custom_sequence_name
1182 return unless current_adapter? :OracleAdapter
1183
1184 # table name is 29 chars, the standard sequence name will
1185 # be 33 chars and fail
1186 assert_raise(ActiveRecord::StatementInvalid) do
1187 begin
1188 Person.connection.create_table :table_with_name_thats_just_ok do |t|
1189 t.column :foo, :string, :null => false
1190 end
1191 ensure
1192 Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
1193 end
1194 end
1195
1196 # should be all good w/ a custom sequence name
1197 assert_nothing_raised do
1198 begin
1199 Person.connection.create_table :table_with_name_thats_just_ok,
1200 :sequence_name => 'suitably_short_seq' do |t|
1201 t.column :foo, :string, :null => false
1202 end
1203
1204 Person.connection.execute("select suitably_short_seq.nextval from dual")
1205
1206 ensure
1207 Person.connection.drop_table :table_with_name_thats_just_ok,
1208 :sequence_name => 'suitably_short_seq' rescue nil
1209 end
1210 end
1211
1212 # confirm the custom sequence got dropped
1213 assert_raise(ActiveRecord::StatementInvalid) do
1214 Person.connection.execute("select suitably_short_seq.nextval from dual")
1215 end
1216 end
1217
1218 protected
1219 def with_env_tz(new_tz = 'US/Eastern')
1220 old_tz, ENV['TZ'] = ENV['TZ'], new_tz
1221 yield
1222 ensure
1223 old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
1224 end
1225
1226 end
1227
1228 class SexyMigrationsTest < ActiveRecord::TestCase
1229 def test_references_column_type_adds_id
1230 with_new_table do |t|
1231 t.expects(:column).with('customer_id', :integer, {})
1232 t.references :customer
1233 end
1234 end
1235
1236 def test_references_column_type_with_polymorphic_adds_type
1237 with_new_table do |t|
1238 t.expects(:column).with('taggable_type', :string, {})
1239 t.expects(:column).with('taggable_id', :integer, {})
1240 t.references :taggable, :polymorphic => true
1241 end
1242 end
1243
1244 def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
1245 with_new_table do |t|
1246 t.expects(:column).with('taggable_type', :string, {:null => false})
1247 t.expects(:column).with('taggable_id', :integer, {:null => false})
1248 t.references :taggable, :polymorphic => true, :null => false
1249 end
1250 end
1251
1252 def test_belongs_to_works_like_references
1253 with_new_table do |t|
1254 t.expects(:column).with('customer_id', :integer, {})
1255 t.belongs_to :customer
1256 end
1257 end
1258
1259 def test_timestamps_creates_updated_at_and_created_at
1260 with_new_table do |t|
1261 t.expects(:column).with(:created_at, :datetime, kind_of(Hash))
1262 t.expects(:column).with(:updated_at, :datetime, kind_of(Hash))
1263 t.timestamps
1264 end
1265 end
1266
1267 def test_integer_creates_integer_column
1268 with_new_table do |t|
1269 t.expects(:column).with(:foo, 'integer', {})
1270 t.expects(:column).with(:bar, 'integer', {})
1271 t.integer :foo, :bar
1272 end
1273 end
1274
1275 def test_string_creates_string_column
1276 with_new_table do |t|
1277 t.expects(:column).with(:foo, 'string', {})
1278 t.expects(:column).with(:bar, 'string', {})
1279 t.string :foo, :bar
1280 end
1281 end
1282
1283 protected
1284 def with_new_table
1285 Person.connection.create_table :delete_me, :force => true do |t|
1286 yield t
1287 end
1288 ensure
1289 Person.connection.drop_table :delete_me rescue nil
1290 end
1291
1292 end # SexyMigrationsTest
1293
1294 class ChangeTableMigrationsTest < ActiveRecord::TestCase
1295 def setup
1296 @connection = Person.connection
1297 @connection.create_table :delete_me, :force => true do |t|
1298 end
1299 end
1300
1301 def teardown
1302 Person.connection.drop_table :delete_me rescue nil
1303 end
1304
1305 def test_references_column_type_adds_id
1306 with_change_table do |t|
1307 @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
1308 t.references :customer
1309 end
1310 end
1311
1312 def test_remove_references_column_type_removes_id
1313 with_change_table do |t|
1314 @connection.expects(:remove_column).with(:delete_me, 'customer_id')
1315 t.remove_references :customer
1316 end
1317 end
1318
1319 def test_add_belongs_to_works_like_add_references
1320 with_change_table do |t|
1321 @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
1322 t.belongs_to :customer
1323 end
1324 end
1325
1326 def test_remove_belongs_to_works_like_remove_references
1327 with_change_table do |t|
1328 @connection.expects(:remove_column).with(:delete_me, 'customer_id')
1329 t.remove_belongs_to :customer
1330 end
1331 end
1332
1333 def test_references_column_type_with_polymorphic_adds_type
1334 with_change_table do |t|
1335 @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {})
1336 @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {})
1337 t.references :taggable, :polymorphic => true
1338 end
1339 end
1340
1341 def test_remove_references_column_type_with_polymorphic_removes_type
1342 with_change_table do |t|
1343 @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
1344 @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
1345 t.remove_references :taggable, :polymorphic => true
1346 end
1347 end
1348
1349 def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
1350 with_change_table do |t|
1351 @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {:null => false})
1352 @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {:null => false})
1353 t.references :taggable, :polymorphic => true, :null => false
1354 end
1355 end
1356
1357 def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
1358 with_change_table do |t|
1359 @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
1360 @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
1361 t.remove_references :taggable, :polymorphic => true, :null => false
1362 end
1363 end
1364
1365 def test_timestamps_creates_updated_at_and_created_at
1366 with_change_table do |t|
1367 @connection.expects(:add_timestamps).with(:delete_me)
1368 t.timestamps
1369 end
1370 end
1371
1372 def test_remove_timestamps_creates_updated_at_and_created_at
1373 with_change_table do |t|
1374 @connection.expects(:remove_timestamps).with(:delete_me)
1375 t.remove_timestamps
1376 end
1377 end
1378
1379 def string_column
1380 if current_adapter?(:PostgreSQLAdapter)
1381 "character varying(255)"
1382 else
1383 'varchar(255)'
1384 end
1385 end
1386
1387 def integer_column
1388 if current_adapter?(:MysqlAdapter)
1389 'int(11)'
1390 else
1391 'integer'
1392 end
1393 end
1394
1395 def test_integer_creates_integer_column
1396 with_change_table do |t|
1397 @connection.expects(:add_column).with(:delete_me, :foo, integer_column, {})
1398 @connection.expects(:add_column).with(:delete_me, :bar, integer_column, {})
1399 t.integer :foo, :bar
1400 end
1401 end
1402
1403 def test_string_creates_string_column
1404 with_change_table do |t|
1405 @connection.expects(:add_column).with(:delete_me, :foo, string_column, {})
1406 @connection.expects(:add_column).with(:delete_me, :bar, string_column, {})
1407 t.string :foo, :bar
1408 end
1409 end
1410
1411 def test_column_creates_column
1412 with_change_table do |t|
1413 @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
1414 t.column :bar, :integer
1415 end
1416 end
1417
1418 def test_column_creates_column_with_options
1419 with_change_table do |t|
1420 @connection.expects(:add_column).with(:delete_me, :bar, :integer, {:null => false})
1421 t.column :bar, :integer, :null => false
1422 end
1423 end
1424
1425 def test_index_creates_index
1426 with_change_table do |t|
1427 @connection.expects(:add_index).with(:delete_me, :bar, {})
1428 t.index :bar
1429 end
1430 end
1431
1432 def test_index_creates_index_with_options
1433 with_change_table do |t|
1434 @connection.expects(:add_index).with(:delete_me, :bar, {:unique => true})
1435 t.index :bar, :unique => true
1436 end
1437 end
1438
1439 def test_change_changes_column
1440 with_change_table do |t|
1441 @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
1442 t.change :bar, :string
1443 end
1444 end
1445
1446 def test_change_changes_column_with_options
1447 with_change_table do |t|
1448 @connection.expects(:change_column).with(:delete_me, :bar, :string, {:null => true})
1449 t.change :bar, :string, :null => true
1450 end
1451 end
1452
1453 def test_change_default_changes_column
1454 with_change_table do |t|
1455 @connection.expects(:change_column_default).with(:delete_me, :bar, :string)
1456 t.change_default :bar, :string
1457 end
1458 end
1459
1460 def test_remove_drops_single_column
1461 with_change_table do |t|
1462 @connection.expects(:remove_column).with(:delete_me, [:bar])
1463 t.remove :bar
1464 end
1465 end
1466
1467 def test_remove_drops_multiple_columns
1468 with_change_table do |t|
1469 @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
1470 t.remove :bar, :baz
1471 end
1472 end
1473
1474 def test_remove_index_removes_index_with_options
1475 with_change_table do |t|
1476 @connection.expects(:remove_index).with(:delete_me, {:unique => true})
1477 t.remove_index :unique => true
1478 end
1479 end
1480
1481 def test_rename_renames_column
1482 with_change_table do |t|
1483 @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
1484 t.rename :bar, :baz
1485 end
1486 end
1487
1488 protected
1489 def with_change_table
1490 Person.connection.change_table :delete_me do |t|
1491 yield t
1492 end
1493 end
1494 end
1495 end