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