Merged updates from trunk into stable branch
[feedcatcher.git] / vendor / rails / activerecord / test / cases / base_test.rb
1 require "cases/helper"
2 require 'models/author'
3 require 'models/topic'
4 require 'models/reply'
5 require 'models/category'
6 require 'models/company'
7 require 'models/customer'
8 require 'models/developer'
9 require 'models/project'
10 require 'models/default'
11 require 'models/auto_id'
12 require 'models/column_name'
13 require 'models/subscriber'
14 require 'models/keyboard'
15 require 'models/post'
16 require 'models/comment'
17 require 'models/minimalistic'
18 require 'models/warehouse_thing'
19 require 'models/parrot'
20 require 'rexml/document'
21
22 class Category < ActiveRecord::Base; end
23 class Categorization < ActiveRecord::Base; end
24 class Smarts < ActiveRecord::Base; end
25 class CreditCard < ActiveRecord::Base
26 class PinNumber < ActiveRecord::Base
27 class CvvCode < ActiveRecord::Base; end
28 class SubCvvCode < CvvCode; end
29 end
30 class SubPinNumber < PinNumber; end
31 class Brand < Category; end
32 end
33 class MasterCreditCard < ActiveRecord::Base; end
34 class Post < ActiveRecord::Base; end
35 class Computer < ActiveRecord::Base; end
36 class NonExistentTable < ActiveRecord::Base; end
37 class TestOracleDefault < ActiveRecord::Base; end
38
39 class LoosePerson < ActiveRecord::Base
40 self.table_name = 'people'
41 self.abstract_class = true
42 attr_protected :credit_rating, :administrator
43 end
44
45 class LooseDescendant < LoosePerson
46 attr_protected :phone_number
47 end
48
49 class LooseDescendantSecond< LoosePerson
50 attr_protected :phone_number
51 attr_protected :name
52 end
53
54 class TightPerson < ActiveRecord::Base
55 self.table_name = 'people'
56 attr_accessible :name, :address
57 end
58
59 class TightDescendant < TightPerson
60 attr_accessible :phone_number
61 end
62
63 class ReadonlyTitlePost < Post
64 attr_readonly :title
65 end
66
67 class Booleantest < ActiveRecord::Base; end
68
69 class Task < ActiveRecord::Base
70 attr_protected :starting
71 end
72
73 class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
74 self.table_name = 'topics'
75 attr_accessible :author_name
76 attr_protected :content
77 end
78
79 class BasicsTest < ActiveRecord::TestCase
80 fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
81
82 def test_table_exists
83 assert !NonExistentTable.table_exists?
84 assert Topic.table_exists?
85 end
86
87 def test_set_attributes
88 topic = Topic.find(1)
89 topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
90 topic.save
91 assert_equal("Budget", topic.title)
92 assert_equal("Jason", topic.author_name)
93 assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
94 end
95
96 def test_integers_as_nil
97 test = AutoId.create('value' => '')
98 assert_nil AutoId.find(test.id).value
99 end
100
101 def test_set_attributes_with_block
102 topic = Topic.new do |t|
103 t.title = "Budget"
104 t.author_name = "Jason"
105 end
106
107 assert_equal("Budget", topic.title)
108 assert_equal("Jason", topic.author_name)
109 end
110
111 def test_respond_to?
112 topic = Topic.find(1)
113 assert topic.respond_to?("title")
114 assert topic.respond_to?("title?")
115 assert topic.respond_to?("title=")
116 assert topic.respond_to?(:title)
117 assert topic.respond_to?(:title?)
118 assert topic.respond_to?(:title=)
119 assert topic.respond_to?("author_name")
120 assert topic.respond_to?("attribute_names")
121 assert !topic.respond_to?("nothingness")
122 assert !topic.respond_to?(:nothingness)
123 end
124
125 def test_array_content
126 topic = Topic.new
127 topic.content = %w( one two three )
128 topic.save
129
130 assert_equal(%w( one two three ), Topic.find(topic.id).content)
131 end
132
133 def test_read_attributes_before_type_cast
134 category = Category.new({:name=>"Test categoty", :type => nil})
135 category_attrs = {"name"=>"Test categoty", "type" => nil, "categorizations_count" => nil}
136 assert_equal category_attrs , category.attributes_before_type_cast
137 end
138
139 if current_adapter?(:MysqlAdapter)
140 def test_read_attributes_before_type_cast_on_boolean
141 bool = Booleantest.create({ "value" => false })
142 assert_equal "0", bool.reload.attributes_before_type_cast["value"]
143 end
144 end
145
146 def test_read_attributes_before_type_cast_on_datetime
147 developer = Developer.find(:first)
148 assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
149 end
150
151 def test_hash_content
152 topic = Topic.new
153 topic.content = { "one" => 1, "two" => 2 }
154 topic.save
155
156 assert_equal 2, Topic.find(topic.id).content["two"]
157
158 topic.content_will_change!
159 topic.content["three"] = 3
160 topic.save
161
162 assert_equal 3, Topic.find(topic.id).content["three"]
163 end
164
165 def test_update_array_content
166 topic = Topic.new
167 topic.content = %w( one two three )
168
169 topic.content.push "four"
170 assert_equal(%w( one two three four ), topic.content)
171
172 topic.save
173
174 topic = Topic.find(topic.id)
175 topic.content << "five"
176 assert_equal(%w( one two three four five ), topic.content)
177 end
178
179 def test_case_sensitive_attributes_hash
180 # DB2 is not case-sensitive
181 return true if current_adapter?(:DB2Adapter)
182
183 assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
184 end
185
186 def test_create
187 topic = Topic.new
188 topic.title = "New Topic"
189 topic.save
190 topic_reloaded = Topic.find(topic.id)
191 assert_equal("New Topic", topic_reloaded.title)
192 end
193
194 def test_save!
195 topic = Topic.new(:title => "New Topic")
196 assert topic.save!
197
198 reply = Reply.new
199 assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
200 end
201
202 def test_save_null_string_attributes
203 topic = Topic.find(1)
204 topic.attributes = { "title" => "null", "author_name" => "null" }
205 topic.save!
206 topic.reload
207 assert_equal("null", topic.title)
208 assert_equal("null", topic.author_name)
209 end
210
211 def test_save_nil_string_attributes
212 topic = Topic.find(1)
213 topic.title = nil
214 topic.save!
215 topic.reload
216 assert_nil topic.title
217 end
218
219 def test_save_for_record_with_only_primary_key
220 minimalistic = Minimalistic.new
221 assert_nothing_raised { minimalistic.save }
222 end
223
224 def test_save_for_record_with_only_primary_key_that_is_provided
225 assert_nothing_raised { Minimalistic.create!(:id => 2) }
226 end
227
228 def test_hashes_not_mangled
229 new_topic = { :title => "New Topic" }
230 new_topic_values = { :title => "AnotherTopic" }
231
232 topic = Topic.new(new_topic)
233 assert_equal new_topic[:title], topic.title
234
235 topic.attributes= new_topic_values
236 assert_equal new_topic_values[:title], topic.title
237 end
238
239 def test_create_many
240 topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
241 assert_equal 2, topics.size
242 assert_equal "first", topics.first.title
243 end
244
245 def test_create_columns_not_equal_attributes
246 topic = Topic.new
247 topic.title = 'Another New Topic'
248 topic.send :write_attribute, 'does_not_exist', 'test'
249 assert_nothing_raised { topic.save }
250 end
251
252 def test_create_through_factory
253 topic = Topic.create("title" => "New Topic")
254 topicReloaded = Topic.find(topic.id)
255 assert_equal(topic, topicReloaded)
256 end
257
258 def test_create_through_factory_with_block
259 topic = Topic.create("title" => "New Topic") do |t|
260 t.author_name = "David"
261 end
262 topicReloaded = Topic.find(topic.id)
263 assert_equal("New Topic", topic.title)
264 assert_equal("David", topic.author_name)
265 end
266
267 def test_create_many_through_factory_with_block
268 topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t|
269 t.author_name = "David"
270 end
271 assert_equal 2, topics.size
272 topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id)
273 assert_equal "first", topic1.title
274 assert_equal "David", topic1.author_name
275 assert_equal "second", topic2.title
276 assert_equal "David", topic2.author_name
277 end
278
279 def test_update
280 topic = Topic.new
281 topic.title = "Another New Topic"
282 topic.written_on = "2003-12-12 23:23:00"
283 topic.save
284 topicReloaded = Topic.find(topic.id)
285 assert_equal("Another New Topic", topicReloaded.title)
286
287 topicReloaded.title = "Updated topic"
288 topicReloaded.save
289
290 topicReloadedAgain = Topic.find(topic.id)
291
292 assert_equal("Updated topic", topicReloadedAgain.title)
293 end
294
295 def test_update_columns_not_equal_attributes
296 topic = Topic.new
297 topic.title = "Still another topic"
298 topic.save
299
300 topicReloaded = Topic.find(topic.id)
301 topicReloaded.title = "A New Topic"
302 topicReloaded.send :write_attribute, 'does_not_exist', 'test'
303 assert_nothing_raised { topicReloaded.save }
304 end
305
306 def test_update_for_record_with_only_primary_key
307 minimalistic = minimalistics(:first)
308 assert_nothing_raised { minimalistic.save }
309 end
310
311 def test_write_attribute
312 topic = Topic.new
313 topic.send(:write_attribute, :title, "Still another topic")
314 assert_equal "Still another topic", topic.title
315
316 topic.send(:write_attribute, "title", "Still another topic: part 2")
317 assert_equal "Still another topic: part 2", topic.title
318 end
319
320 def test_read_attribute
321 topic = Topic.new
322 topic.title = "Don't change the topic"
323 assert_equal "Don't change the topic", topic.send(:read_attribute, "title")
324 assert_equal "Don't change the topic", topic["title"]
325
326 assert_equal "Don't change the topic", topic.send(:read_attribute, :title)
327 assert_equal "Don't change the topic", topic[:title]
328 end
329
330 def test_read_attribute_when_false
331 topic = topics(:first)
332 topic.approved = false
333 assert !topic.approved?, "approved should be false"
334 topic.approved = "false"
335 assert !topic.approved?, "approved should be false"
336 end
337
338 def test_read_attribute_when_true
339 topic = topics(:first)
340 topic.approved = true
341 assert topic.approved?, "approved should be true"
342 topic.approved = "true"
343 assert topic.approved?, "approved should be true"
344 end
345
346 def test_read_write_boolean_attribute
347 topic = Topic.new
348 # puts ""
349 # puts "New Topic"
350 # puts topic.inspect
351 topic.approved = "false"
352 # puts "Expecting false"
353 # puts topic.inspect
354 assert !topic.approved?, "approved should be false"
355 topic.approved = "false"
356 # puts "Expecting false"
357 # puts topic.inspect
358 assert !topic.approved?, "approved should be false"
359 topic.approved = "true"
360 # puts "Expecting true"
361 # puts topic.inspect
362 assert topic.approved?, "approved should be true"
363 topic.approved = "true"
364 # puts "Expecting true"
365 # puts topic.inspect
366 assert topic.approved?, "approved should be true"
367 # puts ""
368 end
369
370 def test_query_attribute_string
371 [nil, "", " "].each do |value|
372 assert_equal false, Topic.new(:author_name => value).author_name?
373 end
374
375 assert_equal true, Topic.new(:author_name => "Name").author_name?
376 end
377
378 def test_query_attribute_number
379 [nil, 0, "0"].each do |value|
380 assert_equal false, Developer.new(:salary => value).salary?
381 end
382
383 assert_equal true, Developer.new(:salary => 1).salary?
384 assert_equal true, Developer.new(:salary => "1").salary?
385 end
386
387 def test_query_attribute_boolean
388 [nil, "", false, "false", "f", 0].each do |value|
389 assert_equal false, Topic.new(:approved => value).approved?
390 end
391
392 [true, "true", "1", 1].each do |value|
393 assert_equal true, Topic.new(:approved => value).approved?
394 end
395 end
396
397 def test_query_attribute_with_custom_fields
398 object = Company.find_by_sql(<<-SQL).first
399 SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value
400 FROM companies c1, companies c2
401 WHERE c1.firm_id = c2.id
402 AND c1.id = 2
403 SQL
404
405 assert_equal "Firm", object.string_value
406 assert object.string_value?
407
408 object.string_value = " "
409 assert !object.string_value?
410
411 assert_equal 1, object.int_value.to_i
412 assert object.int_value?
413
414 object.int_value = "0"
415 assert !object.int_value?
416 end
417
418
419 def test_reader_for_invalid_column_names
420 Topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
421 assert !Topic.generated_methods.include?("mumub-jumbo")
422 end
423
424 def test_non_attribute_access_and_assignment
425 topic = Topic.new
426 assert !topic.respond_to?("mumbo")
427 assert_raise(NoMethodError) { topic.mumbo }
428 assert_raise(NoMethodError) { topic.mumbo = 5 }
429 end
430
431 def test_preserving_date_objects
432 if current_adapter?(:SybaseAdapter, :OracleAdapter)
433 # Sybase ctlib does not (yet?) support the date type; use datetime instead.
434 # Oracle treats all dates/times as Time.
435 assert_kind_of(
436 Time, Topic.find(1).last_read,
437 "The last_read attribute should be of the Time class"
438 )
439 else
440 assert_kind_of(
441 Date, Topic.find(1).last_read,
442 "The last_read attribute should be of the Date class"
443 )
444 end
445 end
446
447 def test_preserving_time_objects
448 assert_kind_of(
449 Time, Topic.find(1).bonus_time,
450 "The bonus_time attribute should be of the Time class"
451 )
452
453 assert_kind_of(
454 Time, Topic.find(1).written_on,
455 "The written_on attribute should be of the Time class"
456 )
457
458 # For adapters which support microsecond resolution.
459 if current_adapter?(:PostgreSQLAdapter)
460 assert_equal 11, Topic.find(1).written_on.sec
461 assert_equal 223300, Topic.find(1).written_on.usec
462 assert_equal 9900, Topic.find(2).written_on.usec
463 end
464 end
465
466 def test_custom_mutator
467 topic = Topic.find(1)
468 # This mutator is protected in the class definition
469 topic.send(:approved=, true)
470 assert topic.instance_variable_get("@custom_approved")
471 end
472
473 def test_delete
474 topic = Topic.find(1)
475 assert_equal topic, topic.delete, 'topic.delete did not return self'
476 assert topic.frozen?, 'topic not frozen after delete'
477 assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
478 end
479
480 def test_delete_doesnt_run_callbacks
481 Topic.find(1).delete
482 assert_not_nil Topic.find(2)
483 end
484
485 def test_destroy
486 topic = Topic.find(1)
487 assert_equal topic, topic.destroy, 'topic.destroy did not return self'
488 assert topic.frozen?, 'topic not frozen after destroy'
489 assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
490 end
491
492 def test_record_not_found_exception
493 assert_raise(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
494 end
495
496 def test_initialize_with_attributes
497 topic = Topic.new({
498 "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
499 })
500
501 assert_equal("initialized from attributes", topic.title)
502 end
503
504 def test_initialize_with_invalid_attribute
505 begin
506 topic = Topic.new({ "title" => "test",
507 "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
508 rescue ActiveRecord::MultiparameterAssignmentErrors => ex
509 assert_equal(1, ex.errors.size)
510 assert_equal("last_read", ex.errors[0].attribute)
511 end
512 end
513
514 def test_load
515 topics = Topic.find(:all, :order => 'id')
516 assert_equal(4, topics.size)
517 assert_equal(topics(:first).title, topics.first.title)
518 end
519
520 def test_load_with_condition
521 topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
522
523 assert_equal(1, topics.size)
524 assert_equal(topics(:second).title, topics.first.title)
525 end
526
527 def test_table_name_guesses
528 classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
529
530 assert_equal "topics", Topic.table_name
531
532 assert_equal "categories", Category.table_name
533 assert_equal "smarts", Smarts.table_name
534 assert_equal "credit_cards", CreditCard.table_name
535 assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
536 assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
537 assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
538 assert_equal "categories", CreditCard::Brand.table_name
539 assert_equal "master_credit_cards", MasterCreditCard.table_name
540
541 ActiveRecord::Base.pluralize_table_names = false
542 classes.each(&:reset_table_name)
543
544 assert_equal "category", Category.table_name
545 assert_equal "smarts", Smarts.table_name
546 assert_equal "credit_card", CreditCard.table_name
547 assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
548 assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
549 assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
550 assert_equal "category", CreditCard::Brand.table_name
551 assert_equal "master_credit_card", MasterCreditCard.table_name
552
553 ActiveRecord::Base.pluralize_table_names = true
554 classes.each(&:reset_table_name)
555
556 ActiveRecord::Base.table_name_prefix = "test_"
557 Category.reset_table_name
558 assert_equal "test_categories", Category.table_name
559 ActiveRecord::Base.table_name_suffix = "_test"
560 Category.reset_table_name
561 assert_equal "test_categories_test", Category.table_name
562 ActiveRecord::Base.table_name_prefix = ""
563 Category.reset_table_name
564 assert_equal "categories_test", Category.table_name
565 ActiveRecord::Base.table_name_suffix = ""
566 Category.reset_table_name
567 assert_equal "categories", Category.table_name
568
569 ActiveRecord::Base.pluralize_table_names = false
570 ActiveRecord::Base.table_name_prefix = "test_"
571 Category.reset_table_name
572 assert_equal "test_category", Category.table_name
573 ActiveRecord::Base.table_name_suffix = "_test"
574 Category.reset_table_name
575 assert_equal "test_category_test", Category.table_name
576 ActiveRecord::Base.table_name_prefix = ""
577 Category.reset_table_name
578 assert_equal "category_test", Category.table_name
579 ActiveRecord::Base.table_name_suffix = ""
580 Category.reset_table_name
581 assert_equal "category", Category.table_name
582
583 ActiveRecord::Base.pluralize_table_names = true
584 classes.each(&:reset_table_name)
585 end
586
587 def test_destroy_all
588 original_count = Topic.count
589 topics_by_mary = Topic.count(:conditions => mary = "author_name = 'Mary'")
590
591 Topic.destroy_all mary
592 assert_equal original_count - topics_by_mary, Topic.count
593 end
594
595 def test_destroy_many
596 assert_equal 3, Client.count
597 Client.destroy([2, 3])
598 assert_equal 1, Client.count
599 end
600
601 def test_delete_many
602 original_count = Topic.count
603 Topic.delete(deleting = [1, 2])
604 assert_equal original_count - deleting.size, Topic.count
605 end
606
607 def test_boolean_attributes
608 assert ! Topic.find(1).approved?
609 assert Topic.find(2).approved?
610 end
611
612 def test_increment_counter
613 Topic.increment_counter("replies_count", 1)
614 assert_equal 2, Topic.find(1).replies_count
615
616 Topic.increment_counter("replies_count", 1)
617 assert_equal 3, Topic.find(1).replies_count
618 end
619
620 def test_decrement_counter
621 Topic.decrement_counter("replies_count", 2)
622 assert_equal -1, Topic.find(2).replies_count
623
624 Topic.decrement_counter("replies_count", 2)
625 assert_equal -2, Topic.find(2).replies_count
626 end
627
628 def test_update_counter
629 category = categories(:general)
630 assert_nil category.categorizations_count
631 assert_equal 2, category.categorizations.count
632
633 Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
634 category.reload
635 assert_not_nil category.categorizations_count
636 assert_equal 2, category.categorizations_count
637
638 Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
639 category.reload
640 assert_not_nil category.categorizations_count
641 assert_equal 4, category.categorizations_count
642
643 category_2 = categories(:technology)
644 count_1, count_2 = (category.categorizations_count || 0), (category_2.categorizations_count || 0)
645 Category.update_counters([category.id, category_2.id], "categorizations_count" => 2)
646 category.reload; category_2.reload
647 assert_equal count_1 + 2, category.categorizations_count
648 assert_equal count_2 + 2, category_2.categorizations_count
649 end
650
651 def test_update_all
652 assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'")
653 assert_equal "bulk updated!", Topic.find(1).content
654 assert_equal "bulk updated!", Topic.find(2).content
655
656 assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!'])
657 assert_equal "bulk updated again!", Topic.find(1).content
658 assert_equal "bulk updated again!", Topic.find(2).content
659
660 assert_equal Topic.count, Topic.update_all(['content = ?', nil])
661 assert_nil Topic.find(1).content
662 end
663
664 def test_update_all_with_hash
665 assert_not_nil Topic.find(1).last_read
666 assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
667 assert_equal "bulk updated with hash!", Topic.find(1).content
668 assert_equal "bulk updated with hash!", Topic.find(2).content
669 assert_nil Topic.find(1).last_read
670 assert_nil Topic.find(2).last_read
671 end
672
673 def test_update_all_with_non_standard_table_name
674 assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1])
675 assert_equal 0, WarehouseThing.find(1).value
676 end
677
678 if current_adapter?(:MysqlAdapter)
679 def test_update_all_with_order_and_limit
680 assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
681 end
682 end
683
684 def test_update_all_ignores_order_without_limit_from_association
685 author = authors(:david)
686 assert_nothing_raised do
687 assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ])
688 end
689 end
690
691 def test_update_all_with_order_and_limit_updates_subset_only
692 author = authors(:david)
693 assert_nothing_raised do
694 assert_equal 1, author.posts_sorted_by_id_limited.size
695 assert_equal 2, author.posts_sorted_by_id_limited.find(:all, :limit => 2).size
696 assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ])
697 assert_equal "bulk update!", posts(:welcome).body
698 assert_not_equal "bulk update!", posts(:thinking).body
699 end
700 end
701
702 def test_update_many
703 topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
704 updated = Topic.update(topic_data.keys, topic_data.values)
705
706 assert_equal 2, updated.size
707 assert_equal "1 updated", Topic.find(1).content
708 assert_equal "2 updated", Topic.find(2).content
709 end
710
711 def test_delete_all
712 assert Topic.count > 0
713
714 assert_equal Topic.count, Topic.delete_all
715 end
716
717 def test_update_by_condition
718 Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
719 assert_equal "Have a nice day", Topic.find(1).content
720 assert_equal "bulk updated!", Topic.find(2).content
721 end
722
723 def test_attribute_present
724 t = Topic.new
725 t.title = "hello there!"
726 t.written_on = Time.now
727 assert t.attribute_present?("title")
728 assert t.attribute_present?("written_on")
729 assert !t.attribute_present?("content")
730 end
731
732 def test_attribute_keys_on_new_instance
733 t = Topic.new
734 assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
735 assert_raise(NoMethodError) { t.title2 }
736 end
737
738 def test_class_name
739 assert_equal "Firm", ActiveRecord::Base.class_name("firms")
740 assert_equal "Category", ActiveRecord::Base.class_name("categories")
741 assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder")
742
743 ActiveRecord::Base.pluralize_table_names = false
744 assert_equal "Firms", ActiveRecord::Base.class_name( "firms" )
745 ActiveRecord::Base.pluralize_table_names = true
746
747 ActiveRecord::Base.table_name_prefix = "test_"
748 assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" )
749 ActiveRecord::Base.table_name_suffix = "_tests"
750 assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" )
751 ActiveRecord::Base.table_name_prefix = ""
752 assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" )
753 ActiveRecord::Base.table_name_suffix = ""
754 assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
755 end
756
757 def test_null_fields
758 assert_nil Topic.find(1).parent_id
759 assert_nil Topic.create("title" => "Hey you").parent_id
760 end
761
762 def test_default_values
763 topic = Topic.new
764 assert topic.approved?
765 assert_nil topic.written_on
766 assert_nil topic.bonus_time
767 assert_nil topic.last_read
768
769 topic.save
770
771 topic = Topic.find(topic.id)
772 assert topic.approved?
773 assert_nil topic.last_read
774
775 # Oracle has some funky default handling, so it requires a bit of
776 # extra testing. See ticket #2788.
777 if current_adapter?(:OracleAdapter)
778 test = TestOracleDefault.new
779 assert_equal "X", test.test_char
780 assert_equal "hello", test.test_string
781 assert_equal 3, test.test_int
782 end
783 end
784
785 # Oracle, and Sybase do not have a TIME datatype.
786 unless current_adapter?(:OracleAdapter, :SybaseAdapter)
787 def test_utc_as_time_zone
788 Topic.default_timezone = :utc
789 attributes = { "bonus_time" => "5:42:00AM" }
790 topic = Topic.find(1)
791 topic.attributes = attributes
792 assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
793 Topic.default_timezone = :local
794 end
795
796 def test_utc_as_time_zone_and_new
797 Topic.default_timezone = :utc
798 attributes = { "bonus_time(1i)"=>"2000",
799 "bonus_time(2i)"=>"1",
800 "bonus_time(3i)"=>"1",
801 "bonus_time(4i)"=>"10",
802 "bonus_time(5i)"=>"35",
803 "bonus_time(6i)"=>"50" }
804 topic = Topic.new(attributes)
805 assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
806 Topic.default_timezone = :local
807 end
808 end
809
810 def test_default_values_on_empty_strings
811 topic = Topic.new
812 topic.approved = nil
813 topic.last_read = nil
814
815 topic.save
816
817 topic = Topic.find(topic.id)
818 assert_nil topic.last_read
819
820 # Sybase adapter does not allow nulls in boolean columns
821 if current_adapter?(:SybaseAdapter)
822 assert topic.approved == false
823 else
824 assert_nil topic.approved
825 end
826 end
827
828 def test_equality
829 assert_equal Topic.find(1), Topic.find(2).topic
830 end
831
832 def test_equality_of_new_records
833 assert_not_equal Topic.new, Topic.new
834 end
835
836 def test_hashing
837 assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
838 end
839
840 def test_delete_new_record
841 client = Client.new
842 client.delete
843 assert client.frozen?
844 end
845
846 def test_delete_record_with_associations
847 client = Client.find(3)
848 client.delete
849 assert client.frozen?
850 assert_kind_of Firm, client.firm
851 assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" }
852 end
853
854 def test_destroy_new_record
855 client = Client.new
856 client.destroy
857 assert client.frozen?
858 end
859
860 def test_destroy_record_with_associations
861 client = Client.find(3)
862 client.destroy
863 assert client.frozen?
864 assert_kind_of Firm, client.firm
865 assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" }
866 end
867
868 def test_update_attribute
869 assert !Topic.find(1).approved?
870 Topic.find(1).update_attribute("approved", true)
871 assert Topic.find(1).approved?
872
873 Topic.find(1).update_attribute(:approved, false)
874 assert !Topic.find(1).approved?
875 end
876
877 def test_update_attributes
878 topic = Topic.find(1)
879 assert !topic.approved?
880 assert_equal "The First Topic", topic.title
881
882 topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
883 topic.reload
884 assert topic.approved?
885 assert_equal "The First Topic Updated", topic.title
886
887 topic.update_attributes(:approved => false, :title => "The First Topic")
888 topic.reload
889 assert !topic.approved?
890 assert_equal "The First Topic", topic.title
891 end
892
893 def test_update_attributes!
894 reply = Reply.find(2)
895 assert_equal "The Second Topic of the day", reply.title
896 assert_equal "Have a nice day", reply.content
897
898 reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening")
899 reply.reload
900 assert_equal "The Second Topic of the day updated", reply.title
901 assert_equal "Have a nice evening", reply.content
902
903 reply.update_attributes!(:title => "The Second Topic of the day", :content => "Have a nice day")
904 reply.reload
905 assert_equal "The Second Topic of the day", reply.title
906 assert_equal "Have a nice day", reply.content
907
908 assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
909 end
910
911 def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
912 topic = TopicWithProtectedContentAndAccessibleAuthorName.new
913 assert_raise(RuntimeError) { topic.attributes = { "author_name" => "me" } }
914 assert_raise(RuntimeError) { topic.attributes = { "content" => "stuff" } }
915 end
916
917 def test_mass_assignment_protection
918 firm = Firm.new
919 firm.attributes = { "name" => "Next Angle", "rating" => 5 }
920 assert_equal 1, firm.rating
921 end
922
923 def test_mass_assignment_protection_against_class_attribute_writers
924 [:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
925 :default_timezone, :schema_format, :lock_optimistically, :record_timestamps].each do |method|
926 assert Task.respond_to?(method)
927 assert Task.respond_to?("#{method}=")
928 assert Task.new.respond_to?(method)
929 assert !Task.new.respond_to?("#{method}=")
930 end
931 end
932
933 def test_customized_primary_key_remains_protected
934 subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
935 assert_nil subscriber.id
936
937 keyboard = Keyboard.new(:key_number => 9, :name => 'nice try')
938 assert_nil keyboard.id
939 end
940
941 def test_customized_primary_key_remains_protected_when_referred_to_as_id
942 subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
943 assert_nil subscriber.id
944
945 keyboard = Keyboard.new(:id => 9, :name => 'nice try')
946 assert_nil keyboard.id
947 end
948
949 def test_mass_assigning_invalid_attribute
950 firm = Firm.new
951
952 assert_raise(ActiveRecord::UnknownAttributeError) do
953 firm.attributes = { "id" => 5, "type" => "Client", "i_dont_even_exist" => 20 }
954 end
955 end
956
957 def test_mass_assignment_protection_on_defaults
958 firm = Firm.new
959 firm.attributes = { "id" => 5, "type" => "Client" }
960 assert_nil firm.id
961 assert_equal "Firm", firm[:type]
962 end
963
964 def test_mass_assignment_accessible
965 reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
966 reply.save
967
968 assert reply.approved?
969
970 reply.approved = false
971 reply.save
972
973 assert !reply.approved?
974 end
975
976 def test_mass_assignment_protection_inheritance
977 assert_nil LoosePerson.accessible_attributes
978 assert_equal Set.new([ 'credit_rating', 'administrator' ]), LoosePerson.protected_attributes
979
980 assert_nil LooseDescendant.accessible_attributes
981 assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number' ]), LooseDescendant.protected_attributes
982
983 assert_nil LooseDescendantSecond.accessible_attributes
984 assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number', 'name' ]), LooseDescendantSecond.protected_attributes, 'Running attr_protected twice in one class should merge the protections'
985
986 assert_nil TightPerson.protected_attributes
987 assert_equal Set.new([ 'name', 'address' ]), TightPerson.accessible_attributes
988
989 assert_nil TightDescendant.protected_attributes
990 assert_equal Set.new([ 'name', 'address', 'phone_number' ]), TightDescendant.accessible_attributes
991 end
992
993 def test_readonly_attributes
994 assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
995
996 post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
997 post.reload
998 assert_equal "cannot change this", post.title
999
1000 post.update_attributes(:title => "try to change", :body => "changed")
1001 post.reload
1002 assert_equal "cannot change this", post.title
1003 assert_equal "changed", post.body
1004 end
1005
1006 def test_multiparameter_attributes_on_date
1007 attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
1008 topic = Topic.find(1)
1009 topic.attributes = attributes
1010 # note that extra #to_date call allows test to pass for Oracle, which
1011 # treats dates/times the same
1012 assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
1013 end
1014
1015 def test_multiparameter_attributes_on_date_with_empty_date
1016 attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
1017 topic = Topic.find(1)
1018 topic.attributes = attributes
1019 # note that extra #to_date call allows test to pass for Oracle, which
1020 # treats dates/times the same
1021 assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
1022 end
1023
1024 def test_multiparameter_attributes_on_date_with_all_empty
1025 attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
1026 topic = Topic.find(1)
1027 topic.attributes = attributes
1028 assert_nil topic.last_read
1029 end
1030
1031 def test_multiparameter_attributes_on_time
1032 attributes = {
1033 "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1034 "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1035 }
1036 topic = Topic.find(1)
1037 topic.attributes = attributes
1038 assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1039 end
1040
1041 def test_multiparameter_attributes_on_time_with_old_date
1042 attributes = {
1043 "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
1044 "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1045 }
1046 topic = Topic.find(1)
1047 topic.attributes = attributes
1048 # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
1049 assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
1050 end
1051
1052 def test_multiparameter_attributes_on_time_with_utc
1053 ActiveRecord::Base.default_timezone = :utc
1054 attributes = {
1055 "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1056 "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1057 }
1058 topic = Topic.find(1)
1059 topic.attributes = attributes
1060 assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
1061 ensure
1062 ActiveRecord::Base.default_timezone = :local
1063 end
1064
1065 def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
1066 ActiveRecord::Base.time_zone_aware_attributes = true
1067 ActiveRecord::Base.default_timezone = :utc
1068 Time.zone = ActiveSupport::TimeZone[-28800]
1069 attributes = {
1070 "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1071 "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1072 }
1073 topic = Topic.find(1)
1074 topic.attributes = attributes
1075 assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
1076 assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
1077 assert_equal Time.zone, topic.written_on.time_zone
1078 ensure
1079 ActiveRecord::Base.time_zone_aware_attributes = false
1080 ActiveRecord::Base.default_timezone = :local
1081 Time.zone = nil
1082 end
1083
1084 def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
1085 ActiveRecord::Base.time_zone_aware_attributes = false
1086 Time.zone = ActiveSupport::TimeZone[-28800]
1087 attributes = {
1088 "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1089 "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1090 }
1091 topic = Topic.find(1)
1092 topic.attributes = attributes
1093 assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1094 assert_equal false, topic.written_on.respond_to?(:time_zone)
1095 ensure
1096 Time.zone = nil
1097 end
1098
1099 def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
1100 ActiveRecord::Base.time_zone_aware_attributes = true
1101 ActiveRecord::Base.default_timezone = :utc
1102 Time.zone = ActiveSupport::TimeZone[-28800]
1103 Topic.skip_time_zone_conversion_for_attributes = [:written_on]
1104 attributes = {
1105 "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1106 "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1107 }
1108 topic = Topic.find(1)
1109 topic.attributes = attributes
1110 assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
1111 assert_equal false, topic.written_on.respond_to?(:time_zone)
1112 ensure
1113 ActiveRecord::Base.time_zone_aware_attributes = false
1114 ActiveRecord::Base.default_timezone = :local
1115 Time.zone = nil
1116 Topic.skip_time_zone_conversion_for_attributes = []
1117 end
1118
1119 def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
1120 ActiveRecord::Base.time_zone_aware_attributes = true
1121 ActiveRecord::Base.default_timezone = :utc
1122 Time.zone = ActiveSupport::TimeZone[-28800]
1123 attributes = {
1124 "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
1125 "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
1126 }
1127 topic = Topic.find(1)
1128 topic.attributes = attributes
1129 assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
1130 assert topic.bonus_time.utc?
1131 ensure
1132 ActiveRecord::Base.time_zone_aware_attributes = false
1133 ActiveRecord::Base.default_timezone = :local
1134 Time.zone = nil
1135 end
1136
1137 def test_multiparameter_attributes_on_time_with_empty_seconds
1138 attributes = {
1139 "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1140 "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
1141 }
1142 topic = Topic.find(1)
1143 topic.attributes = attributes
1144 assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1145 end
1146
1147 def test_multiparameter_mass_assignment_protector
1148 task = Task.new
1149 time = Time.mktime(2000, 1, 1, 1)
1150 task.starting = time
1151 attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" }
1152 task.attributes = attributes
1153 assert_equal time, task.starting
1154 end
1155
1156 def test_multiparameter_assignment_of_aggregation
1157 customer = Customer.new
1158 address = Address.new("The Street", "The City", "The Country")
1159 attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
1160 customer.attributes = attributes
1161 assert_equal address, customer.address
1162 end
1163
1164 def test_attributes_on_dummy_time
1165 # Oracle, and Sybase do not have a TIME datatype.
1166 return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
1167
1168 attributes = {
1169 "bonus_time" => "5:42:00AM"
1170 }
1171 topic = Topic.find(1)
1172 topic.attributes = attributes
1173 assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
1174 end
1175
1176 def test_boolean
1177 b_nil = Booleantest.create({ "value" => nil })
1178 nil_id = b_nil.id
1179 b_false = Booleantest.create({ "value" => false })
1180 false_id = b_false.id
1181 b_true = Booleantest.create({ "value" => true })
1182 true_id = b_true.id
1183
1184 b_nil = Booleantest.find(nil_id)
1185 assert_nil b_nil.value
1186 b_false = Booleantest.find(false_id)
1187 assert !b_false.value?
1188 b_true = Booleantest.find(true_id)
1189 assert b_true.value?
1190 end
1191
1192 def test_boolean_cast_from_string
1193 b_blank = Booleantest.create({ "value" => "" })
1194 blank_id = b_blank.id
1195 b_false = Booleantest.create({ "value" => "0" })
1196 false_id = b_false.id
1197 b_true = Booleantest.create({ "value" => "1" })
1198 true_id = b_true.id
1199
1200 b_blank = Booleantest.find(blank_id)
1201 assert_nil b_blank.value
1202 b_false = Booleantest.find(false_id)
1203 assert !b_false.value?
1204 b_true = Booleantest.find(true_id)
1205 assert b_true.value?
1206 end
1207
1208 def test_new_record_returns_boolean
1209 assert_equal Topic.new.new_record?, true
1210 assert_equal Topic.find(1).new_record?, false
1211 end
1212
1213 def test_clone
1214 topic = Topic.find(1)
1215 cloned_topic = nil
1216 assert_nothing_raised { cloned_topic = topic.clone }
1217 assert_equal topic.title, cloned_topic.title
1218 assert cloned_topic.new_record?
1219
1220 # test if the attributes have been cloned
1221 topic.title = "a"
1222 cloned_topic.title = "b"
1223 assert_equal "a", topic.title
1224 assert_equal "b", cloned_topic.title
1225
1226 # test if the attribute values have been cloned
1227 topic.title = {"a" => "b"}
1228 cloned_topic = topic.clone
1229 cloned_topic.title["a"] = "c"
1230 assert_equal "b", topic.title["a"]
1231
1232 #test if attributes set as part of after_initialize are cloned correctly
1233 assert_equal topic.author_email_address, cloned_topic.author_email_address
1234
1235 # test if saved clone object differs from original
1236 cloned_topic.save
1237 assert !cloned_topic.new_record?
1238 assert cloned_topic.id != topic.id
1239 end
1240
1241 def test_clone_with_aggregate_of_same_name_as_attribute
1242 dev = DeveloperWithAggregate.find(1)
1243 assert_kind_of DeveloperSalary, dev.salary
1244
1245 clone = nil
1246 assert_nothing_raised { clone = dev.clone }
1247 assert_kind_of DeveloperSalary, clone.salary
1248 assert_equal dev.salary.amount, clone.salary.amount
1249 assert clone.new_record?
1250
1251 # test if the attributes have been cloned
1252 original_amount = clone.salary.amount
1253 dev.salary.amount = 1
1254 assert_equal original_amount, clone.salary.amount
1255
1256 assert clone.save
1257 assert !clone.new_record?
1258 assert clone.id != dev.id
1259 end
1260
1261 def test_clone_preserves_subtype
1262 clone = nil
1263 assert_nothing_raised { clone = Company.find(3).clone }
1264 assert_kind_of Client, clone
1265 end
1266
1267 def test_bignum
1268 company = Company.find(1)
1269 company.rating = 2147483647
1270 company.save
1271 company = Company.find(1)
1272 assert_equal 2147483647, company.rating
1273 end
1274
1275 # TODO: extend defaults tests to other databases!
1276 if current_adapter?(:PostgreSQLAdapter)
1277 def test_default
1278 default = Default.new
1279
1280 # fixed dates / times
1281 assert_equal Date.new(2004, 1, 1), default.fixed_date
1282 assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
1283
1284 # char types
1285 assert_equal 'Y', default.char1
1286 assert_equal 'a varchar field', default.char2
1287 assert_equal 'a text field', default.char3
1288 end
1289
1290 class Geometric < ActiveRecord::Base; end
1291 def test_geometric_content
1292
1293 # accepted format notes:
1294 # ()'s aren't required
1295 # values can be a mix of float or integer
1296
1297 g = Geometric.new(
1298 :a_point => '(5.0, 6.1)',
1299 #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1300 :a_line_segment => '(2.0, 3), (5.5, 7.0)',
1301 :a_box => '2.0, 3, 5.5, 7.0',
1302 :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
1303 :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
1304 :a_circle => '<(5.3, 10.4), 2>'
1305 )
1306
1307 assert g.save
1308
1309 # Reload and check that we have all the geometric attributes.
1310 h = Geometric.find(g.id)
1311
1312 assert_equal '(5,6.1)', h.a_point
1313 assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1314 assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1315 assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
1316 assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1317 assert_equal '<(5.3,10.4),2>', h.a_circle
1318
1319 # use a geometric function to test for an open path
1320 objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
1321 assert_equal objs[0].isopen, 't'
1322
1323 # test alternate formats when defining the geometric types
1324
1325 g = Geometric.new(
1326 :a_point => '5.0, 6.1',
1327 #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1328 :a_line_segment => '((2.0, 3), (5.5, 7.0))',
1329 :a_box => '(2.0, 3), (5.5, 7.0)',
1330 :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
1331 :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
1332 :a_circle => '((5.3, 10.4), 2)'
1333 )
1334
1335 assert g.save
1336
1337 # Reload and check that we have all the geometric attributes.
1338 h = Geometric.find(g.id)
1339
1340 assert_equal '(5,6.1)', h.a_point
1341 assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1342 assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1343 assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
1344 assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1345 assert_equal '<(5.3,10.4),2>', h.a_circle
1346
1347 # use a geometric function to test for an closed path
1348 objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
1349 assert_equal objs[0].isclosed, 't'
1350 end
1351 end
1352
1353 class NumericData < ActiveRecord::Base
1354 self.table_name = 'numeric_data'
1355 end
1356
1357 def test_numeric_fields
1358 m = NumericData.new(
1359 :bank_balance => 1586.43,
1360 :big_bank_balance => BigDecimal("1000234000567.95"),
1361 :world_population => 6000000000,
1362 :my_house_population => 3
1363 )
1364 assert m.save
1365
1366 m1 = NumericData.find(m.id)
1367 assert_not_nil m1
1368
1369 # As with migration_test.rb, we should make world_population >= 2**62
1370 # to cover 64-bit platforms and test it is a Bignum, but the main thing
1371 # is that it's an Integer.
1372 assert_kind_of Integer, m1.world_population
1373 assert_equal 6000000000, m1.world_population
1374
1375 assert_kind_of Fixnum, m1.my_house_population
1376 assert_equal 3, m1.my_house_population
1377
1378 assert_kind_of BigDecimal, m1.bank_balance
1379 assert_equal BigDecimal("1586.43"), m1.bank_balance
1380
1381 assert_kind_of BigDecimal, m1.big_bank_balance
1382 assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
1383 end
1384
1385 def test_auto_id
1386 auto = AutoId.new
1387 auto.save
1388 assert (auto.id > 0)
1389 end
1390
1391 def quote_column_name(name)
1392 "<#{name}>"
1393 end
1394
1395 def test_quote_keys
1396 ar = AutoId.new
1397 source = {"foo" => "bar", "baz" => "quux"}
1398 actual = ar.send(:quote_columns, self, source)
1399 inverted = actual.invert
1400 assert_equal("<foo>", inverted["bar"])
1401 assert_equal("<baz>", inverted["quux"])
1402 end
1403
1404 def test_sql_injection_via_find
1405 assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1406 Topic.find("123456 OR id > 0")
1407 end
1408 end
1409
1410 def test_column_name_properly_quoted
1411 col_record = ColumnName.new
1412 col_record.references = 40
1413 assert col_record.save
1414 col_record.references = 41
1415 assert col_record.save
1416 assert_not_nil c2 = ColumnName.find(col_record.id)
1417 assert_equal(41, c2.references)
1418 end
1419
1420 def test_quoting_arrays
1421 replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
1422 assert_equal topics(:first).replies.size, replies.size
1423
1424 replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
1425 assert_equal 0, replies.size
1426 end
1427
1428 MyObject = Struct.new :attribute1, :attribute2
1429
1430 def test_serialized_attribute
1431 myobj = MyObject.new('value1', 'value2')
1432 topic = Topic.create("content" => myobj)
1433 Topic.serialize("content", MyObject)
1434 assert_equal(myobj, topic.content)
1435 end
1436
1437 def test_serialized_time_attribute
1438 myobj = Time.local(2008,1,1,1,0)
1439 topic = Topic.create("content" => myobj).reload
1440 assert_equal(myobj, topic.content)
1441 end
1442
1443 def test_serialized_string_attribute
1444 myobj = "Yes"
1445 topic = Topic.create("content" => myobj).reload
1446 assert_equal(myobj, topic.content)
1447 end
1448
1449 def test_nil_serialized_attribute_with_class_constraint
1450 myobj = MyObject.new('value1', 'value2')
1451 topic = Topic.new
1452 assert_nil topic.content
1453 end
1454
1455 def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
1456 myobj = MyObject.new('value1', 'value2')
1457 topic = Topic.new(:content => myobj)
1458 assert topic.save
1459 Topic.serialize(:content, Hash)
1460 assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
1461 ensure
1462 Topic.serialize(:content)
1463 end
1464
1465 def test_serialized_attribute_with_class_constraint
1466 settings = { "color" => "blue" }
1467 Topic.serialize(:content, Hash)
1468 topic = Topic.new(:content => settings)
1469 assert topic.save
1470 assert_equal(settings, Topic.find(topic.id).content)
1471 ensure
1472 Topic.serialize(:content)
1473 end
1474
1475 def test_quote
1476 author_name = "\\ \001 ' \n \\n \""
1477 topic = Topic.create('author_name' => author_name)
1478 assert_equal author_name, Topic.find(topic.id).author_name
1479 end
1480
1481 if RUBY_VERSION < '1.9'
1482 def test_quote_chars
1483 with_kcode('UTF8') do
1484 str = 'The Narrator'
1485 topic = Topic.create(:author_name => str)
1486 assert_equal str, topic.author_name
1487
1488 assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
1489 topic = Topic.find_by_author_name(str.mb_chars)
1490
1491 assert_kind_of Topic, topic
1492 assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1493 end
1494 end
1495 end
1496
1497 def test_class_level_destroy
1498 should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1499 Topic.find(1).replies << should_be_destroyed_reply
1500
1501 Topic.destroy(1)
1502 assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1503 assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
1504 end
1505
1506 def test_class_level_delete
1507 should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1508 Topic.find(1).replies << should_be_destroyed_reply
1509
1510 Topic.delete(1)
1511 assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1512 assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
1513 end
1514
1515 def test_increment_attribute
1516 assert_equal 50, accounts(:signals37).credit_limit
1517 accounts(:signals37).increment! :credit_limit
1518 assert_equal 51, accounts(:signals37, :reload).credit_limit
1519
1520 accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
1521 assert_equal 53, accounts(:signals37, :reload).credit_limit
1522 end
1523
1524 def test_increment_nil_attribute
1525 assert_nil topics(:first).parent_id
1526 topics(:first).increment! :parent_id
1527 assert_equal 1, topics(:first).parent_id
1528 end
1529
1530 def test_increment_attribute_by
1531 assert_equal 50, accounts(:signals37).credit_limit
1532 accounts(:signals37).increment! :credit_limit, 5
1533 assert_equal 55, accounts(:signals37, :reload).credit_limit
1534
1535 accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3)
1536 assert_equal 59, accounts(:signals37, :reload).credit_limit
1537 end
1538
1539 def test_decrement_attribute
1540 assert_equal 50, accounts(:signals37).credit_limit
1541
1542 accounts(:signals37).decrement!(:credit_limit)
1543 assert_equal 49, accounts(:signals37, :reload).credit_limit
1544
1545 accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
1546 assert_equal 47, accounts(:signals37, :reload).credit_limit
1547 end
1548
1549 def test_decrement_attribute_by
1550 assert_equal 50, accounts(:signals37).credit_limit
1551 accounts(:signals37).decrement! :credit_limit, 5
1552 assert_equal 45, accounts(:signals37, :reload).credit_limit
1553
1554 accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3)
1555 assert_equal 41, accounts(:signals37, :reload).credit_limit
1556 end
1557
1558 def test_toggle_attribute
1559 assert !topics(:first).approved?
1560 topics(:first).toggle!(:approved)
1561 assert topics(:first).approved?
1562 topic = topics(:first)
1563 topic.toggle(:approved)
1564 assert !topic.approved?
1565 topic.reload
1566 assert topic.approved?
1567 end
1568
1569 def test_reload
1570 t1 = Topic.find(1)
1571 t2 = Topic.find(1)
1572 t1.title = "something else"
1573 t1.save
1574 t2.reload
1575 assert_equal t1.title, t2.title
1576 end
1577
1578 def test_define_attr_method_with_value
1579 k = Class.new( ActiveRecord::Base )
1580 k.send(:define_attr_method, :table_name, "foo")
1581 assert_equal "foo", k.table_name
1582 end
1583
1584 def test_define_attr_method_with_block
1585 k = Class.new( ActiveRecord::Base )
1586 k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1587 assert_equal "sys_id", k.primary_key
1588 end
1589
1590 def test_set_table_name_with_value
1591 k = Class.new( ActiveRecord::Base )
1592 k.table_name = "foo"
1593 assert_equal "foo", k.table_name
1594 k.set_table_name "bar"
1595 assert_equal "bar", k.table_name
1596 end
1597
1598 def test_set_table_name_with_block
1599 k = Class.new( ActiveRecord::Base )
1600 k.set_table_name { "ks" }
1601 assert_equal "ks", k.table_name
1602 end
1603
1604 def test_set_primary_key_with_value
1605 k = Class.new( ActiveRecord::Base )
1606 k.primary_key = "foo"
1607 assert_equal "foo", k.primary_key
1608 k.set_primary_key "bar"
1609 assert_equal "bar", k.primary_key
1610 end
1611
1612 def test_set_primary_key_with_block
1613 k = Class.new( ActiveRecord::Base )
1614 k.set_primary_key { "sys_" + original_primary_key }
1615 assert_equal "sys_id", k.primary_key
1616 end
1617
1618 def test_set_inheritance_column_with_value
1619 k = Class.new( ActiveRecord::Base )
1620 k.inheritance_column = "foo"
1621 assert_equal "foo", k.inheritance_column
1622 k.set_inheritance_column "bar"
1623 assert_equal "bar", k.inheritance_column
1624 end
1625
1626 def test_set_inheritance_column_with_block
1627 k = Class.new( ActiveRecord::Base )
1628 k.set_inheritance_column { original_inheritance_column + "_id" }
1629 assert_equal "type_id", k.inheritance_column
1630 end
1631
1632 def test_count_with_join
1633 res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1634
1635 res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1636 assert_equal res, res2
1637
1638 res3 = nil
1639 assert_nothing_raised do
1640 res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1641 :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1642 end
1643 assert_equal res, res3
1644
1645 res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1646 res5 = nil
1647 assert_nothing_raised do
1648 res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1649 :joins => "p, comments co",
1650 :select => "p.id")
1651 end
1652
1653 assert_equal res4, res5
1654
1655 unless current_adapter?(:SQLite2Adapter, :DeprecatedSQLiteAdapter)
1656 res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1657 res7 = nil
1658 assert_nothing_raised do
1659 res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1660 :joins => "p, comments co",
1661 :select => "p.id",
1662 :distinct => true)
1663 end
1664 assert_equal res6, res7
1665 end
1666 end
1667
1668 def test_clear_association_cache_stored
1669 firm = Firm.find(1)
1670 assert_kind_of Firm, firm
1671
1672 firm.clear_association_cache
1673 assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
1674 end
1675
1676 def test_clear_association_cache_new_record
1677 firm = Firm.new
1678 client_stored = Client.find(3)
1679 client_new = Client.new
1680 client_new.name = "The Joneses"
1681 clients = [ client_stored, client_new ]
1682
1683 firm.clients << clients
1684 assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1685
1686 firm.clear_association_cache
1687 assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1688 end
1689
1690 def test_interpolate_sql
1691 assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') }
1692 assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
1693 assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
1694 end
1695
1696 def test_scoped_find_conditions
1697 scoped_developers = Developer.with_scope(:find => { :conditions => 'salary > 90000' }) do
1698 Developer.find(:all, :conditions => 'id < 5')
1699 end
1700 assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1701 assert_equal 3, scoped_developers.size
1702 end
1703
1704 def test_scoped_find_limit_offset
1705 scoped_developers = Developer.with_scope(:find => { :limit => 3, :offset => 2 }) do
1706 Developer.find(:all, :order => 'id')
1707 end
1708 assert !scoped_developers.include?(developers(:david))
1709 assert !scoped_developers.include?(developers(:jamis))
1710 assert_equal 3, scoped_developers.size
1711
1712 # Test without scoped find conditions to ensure we get the whole thing
1713 developers = Developer.find(:all, :order => 'id')
1714 assert_equal Developer.count, developers.size
1715 end
1716
1717 def test_scoped_find_order
1718 # Test order in scope
1719 scoped_developers = Developer.with_scope(:find => { :limit => 1, :order => 'salary DESC' }) do
1720 Developer.find(:all)
1721 end
1722 assert_equal 'Jamis', scoped_developers.first.name
1723 assert scoped_developers.include?(developers(:jamis))
1724 # Test scope without order and order in find
1725 scoped_developers = Developer.with_scope(:find => { :limit => 1 }) do
1726 Developer.find(:all, :order => 'salary DESC')
1727 end
1728 # Test scope order + find order, find has priority
1729 scoped_developers = Developer.with_scope(:find => { :limit => 3, :order => 'id DESC' }) do
1730 Developer.find(:all, :order => 'salary ASC')
1731 end
1732 assert scoped_developers.include?(developers(:poor_jamis))
1733 assert scoped_developers.include?(developers(:david))
1734 assert scoped_developers.include?(developers(:dev_10))
1735 # Test without scoped find conditions to ensure we get the right thing
1736 developers = Developer.find(:all, :order => 'id', :limit => 1)
1737 assert scoped_developers.include?(developers(:david))
1738 end
1739
1740 def test_scoped_find_limit_offset_including_has_many_association
1741 topics = Topic.with_scope(:find => {:limit => 1, :offset => 1, :include => :replies}) do
1742 Topic.find(:all, :order => "topics.id")
1743 end
1744 assert_equal 1, topics.size
1745 assert_equal 2, topics.first.id
1746 end
1747
1748 def test_scoped_find_order_including_has_many_association
1749 developers = Developer.with_scope(:find => { :order => 'developers.salary DESC', :include => :projects }) do
1750 Developer.find(:all)
1751 end
1752 assert developers.size >= 2
1753 for i in 1...developers.size
1754 assert developers[i-1].salary >= developers[i].salary
1755 end
1756 end
1757
1758 def test_scoped_find_with_group_and_having
1759 developers = Developer.with_scope(:find => { :group => 'salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
1760 Developer.find(:all)
1761 end
1762 assert_equal 3, developers.size
1763 end
1764
1765 def test_find_last
1766 last = Developer.find :last
1767 assert_equal last, Developer.find(:first, :order => 'id desc')
1768 end
1769
1770 def test_last
1771 assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
1772 end
1773
1774 def test_all_with_conditions
1775 assert_equal Developer.find(:all, :order => 'id desc'), Developer.all(:order => 'id desc')
1776 end
1777
1778 def test_find_ordered_last
1779 last = Developer.find :last, :order => 'developers.salary ASC'
1780 assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
1781 end
1782
1783 def test_find_reverse_ordered_last
1784 last = Developer.find :last, :order => 'developers.salary DESC'
1785 assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
1786 end
1787
1788 def test_find_multiple_ordered_last
1789 last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
1790 assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
1791 end
1792
1793 def test_find_symbol_ordered_last
1794 last = Developer.find :last, :order => :salary
1795 assert_equal last, Developer.find(:all, :order => :salary).last
1796 end
1797
1798 def test_find_scoped_ordered_last
1799 last_developer = Developer.with_scope(:find => { :order => 'developers.salary ASC' }) do
1800 Developer.find(:last)
1801 end
1802 assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
1803 end
1804
1805 def test_abstract_class
1806 assert !ActiveRecord::Base.abstract_class?
1807 assert LoosePerson.abstract_class?
1808 assert !LooseDescendant.abstract_class?
1809 end
1810
1811 def test_base_class
1812 assert_equal LoosePerson, LoosePerson.base_class
1813 assert_equal LooseDescendant, LooseDescendant.base_class
1814 assert_equal TightPerson, TightPerson.base_class
1815 assert_equal TightPerson, TightDescendant.base_class
1816
1817 assert_equal Post, Post.base_class
1818 assert_equal Post, SpecialPost.base_class
1819 assert_equal Post, StiPost.base_class
1820 assert_equal SubStiPost, SubStiPost.base_class
1821 end
1822
1823 def test_descends_from_active_record
1824 # Tries to call Object.abstract_class?
1825 assert_raise(NoMethodError) do
1826 ActiveRecord::Base.descends_from_active_record?
1827 end
1828
1829 # Abstract subclass of AR::Base.
1830 assert LoosePerson.descends_from_active_record?
1831
1832 # Concrete subclass of an abstract class.
1833 assert LooseDescendant.descends_from_active_record?
1834
1835 # Concrete subclass of AR::Base.
1836 assert TightPerson.descends_from_active_record?
1837
1838 # Concrete subclass of a concrete class but has no type column.
1839 assert TightDescendant.descends_from_active_record?
1840
1841 # Concrete subclass of AR::Base.
1842 assert Post.descends_from_active_record?
1843
1844 # Abstract subclass of a concrete class which has a type column.
1845 # This is pathological, as you'll never have Sub < Abstract < Concrete.
1846 assert !StiPost.descends_from_active_record?
1847
1848 # Concrete subclasses an abstract class which has a type column.
1849 assert !SubStiPost.descends_from_active_record?
1850 end
1851
1852 def test_find_on_abstract_base_class_doesnt_use_type_condition
1853 old_class = LooseDescendant
1854 Object.send :remove_const, :LooseDescendant
1855
1856 descendant = old_class.create! :first_name => 'bob'
1857 assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1858 ensure
1859 unless Object.const_defined?(:LooseDescendant)
1860 Object.const_set :LooseDescendant, old_class
1861 end
1862 end
1863
1864 def test_assert_queries
1865 query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1866 assert_queries(2) { 2.times { query.call } }
1867 assert_queries 1, &query
1868 assert_no_queries { assert true }
1869 end
1870
1871 def test_to_xml
1872 xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
1873 bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
1874 written_on_in_current_timezone = topics(:first).written_on.xmlschema
1875 last_read_in_current_timezone = topics(:first).last_read.xmlschema
1876
1877 assert_equal "topic", xml.root.name
1878 assert_equal "The First Topic" , xml.elements["//title"].text
1879 assert_equal "David" , xml.elements["//author-name"].text
1880
1881 assert_equal "1", xml.elements["//id"].text
1882 assert_equal "integer" , xml.elements["//id"].attributes['type']
1883
1884 assert_equal "1", xml.elements["//replies-count"].text
1885 assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
1886
1887 assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
1888 assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
1889
1890 assert_equal "--- Have a nice day\n" , xml.elements["//content"].text
1891 assert_equal "yaml" , xml.elements["//content"].attributes['type']
1892
1893 assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
1894
1895 assert_equal nil, xml.elements["//parent-id"].text
1896 assert_equal "integer", xml.elements["//parent-id"].attributes['type']
1897 assert_equal "true", xml.elements["//parent-id"].attributes['nil']
1898
1899 if current_adapter?(:SybaseAdapter, :OracleAdapter)
1900 assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
1901 assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
1902 else
1903 assert_equal "2004-04-15", xml.elements["//last-read"].text
1904 assert_equal "date" , xml.elements["//last-read"].attributes['type']
1905 end
1906
1907 # Oracle and DB2 don't have true boolean or time-only fields
1908 unless current_adapter?(:OracleAdapter, :DB2Adapter)
1909 assert_equal "false", xml.elements["//approved"].text
1910 assert_equal "boolean" , xml.elements["//approved"].attributes['type']
1911
1912 assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
1913 assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
1914 end
1915 end
1916
1917 def test_to_xml_skipping_attributes
1918 xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
1919 assert_equal "<topic>", xml.first(7)
1920 assert !xml.include?(%(<title>The First Topic</title>))
1921 assert xml.include?(%(<author-name>David</author-name>))
1922
1923 xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
1924 assert !xml.include?(%(<title>The First Topic</title>))
1925 assert !xml.include?(%(<author-name>David</author-name>))
1926 end
1927
1928 def test_to_xml_including_has_many_association
1929 xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
1930 assert_equal "<topic>", xml.first(7)
1931 assert xml.include?(%(<replies type="array"><reply>))
1932 assert xml.include?(%(<title>The Second Topic of the day</title>))
1933 end
1934
1935 def test_array_to_xml_including_has_many_association
1936 xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
1937 assert xml.include?(%(<replies type="array"><reply>))
1938 end
1939
1940 def test_array_to_xml_including_methods
1941 xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
1942 assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
1943 assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
1944 end
1945
1946 def test_array_to_xml_including_has_one_association
1947 xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
1948 assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
1949 assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
1950 end
1951
1952 def test_array_to_xml_including_belongs_to_association
1953 xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1954 assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
1955 assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
1956 assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
1957 end
1958
1959 def test_to_xml_including_belongs_to_association
1960 xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1961 assert !xml.include?("<firm>")
1962
1963 xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1964 assert xml.include?("<firm>")
1965 end
1966
1967 def test_to_xml_including_multiple_associations
1968 xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
1969 assert_equal "<firm>", xml.first(6)
1970 assert xml.include?(%(<account>))
1971 assert xml.include?(%(<clients type="array"><client>))
1972 end
1973
1974 def test_to_xml_including_multiple_associations_with_options
1975 xml = companies(:first_firm).to_xml(
1976 :indent => 0, :skip_instruct => true,
1977 :include => { :clients => { :only => :name } }
1978 )
1979
1980 assert_equal "<firm>", xml.first(6)
1981 assert xml.include?(%(<client><name>Summit</name></client>))
1982 assert xml.include?(%(<clients type="array"><client>))
1983 end
1984
1985 def test_to_xml_including_methods
1986 xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
1987 assert_equal "<company>", xml.first(9)
1988 assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
1989 end
1990
1991 def test_to_xml_with_block
1992 value = "Rockin' the block"
1993 xml = Company.new.to_xml(:skip_instruct => true) do |xml|
1994 xml.tag! "arbitrary-element", value
1995 end
1996 assert_equal "<company>", xml.first(9)
1997 assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
1998 end
1999
2000 def test_type_name_with_module_should_handle_beginning
2001 assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
2002 assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
2003 end
2004
2005 def test_to_param_should_return_string
2006 assert_kind_of String, Client.find(:first).to_param
2007 end
2008
2009 def test_inspect_class
2010 assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
2011 assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
2012 assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
2013 end
2014
2015 def test_inspect_instance
2016 topic = topics(:first)
2017 assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, type: nil>), topic.inspect
2018 end
2019
2020 def test_inspect_new_instance
2021 assert_match /Topic id: nil/, Topic.new.inspect
2022 end
2023
2024 def test_inspect_limited_select_instance
2025 assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
2026 assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
2027 end
2028
2029 def test_inspect_class_without_table
2030 assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
2031 end
2032
2033 def test_attribute_for_inspect
2034 t = topics(:first)
2035 t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
2036
2037 assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
2038 assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
2039 end
2040
2041 def test_becomes
2042 assert_kind_of Reply, topics(:first).becomes(Reply)
2043 assert_equal "The First Topic", topics(:first).becomes(Reply).title
2044 end
2045
2046 def test_silence_sets_log_level_to_error_in_block
2047 original_logger = ActiveRecord::Base.logger
2048 log = StringIO.new
2049 ActiveRecord::Base.logger = Logger.new(log)
2050 ActiveRecord::Base.logger.level = Logger::DEBUG
2051 ActiveRecord::Base.silence do
2052 ActiveRecord::Base.logger.warn "warn"
2053 ActiveRecord::Base.logger.error "error"
2054 end
2055 assert_equal "error\n", log.string
2056 ensure
2057 ActiveRecord::Base.logger = original_logger
2058 end
2059
2060 def test_silence_sets_log_level_back_to_level_before_yield
2061 original_logger = ActiveRecord::Base.logger
2062 log = StringIO.new
2063 ActiveRecord::Base.logger = Logger.new(log)
2064 ActiveRecord::Base.logger.level = Logger::WARN
2065 ActiveRecord::Base.silence do
2066 end
2067 assert_equal Logger::WARN, ActiveRecord::Base.logger.level
2068 ensure
2069 ActiveRecord::Base.logger = original_logger
2070 end
2071
2072 def test_benchmark_with_log_level
2073 original_logger = ActiveRecord::Base.logger
2074 log = StringIO.new
2075 ActiveRecord::Base.logger = Logger.new(log)
2076 ActiveRecord::Base.logger.level = Logger::WARN
2077 ActiveRecord::Base.benchmark("Debug Topic Count", Logger::DEBUG) { Topic.count }
2078 ActiveRecord::Base.benchmark("Warn Topic Count", Logger::WARN) { Topic.count }
2079 ActiveRecord::Base.benchmark("Error Topic Count", Logger::ERROR) { Topic.count }
2080 assert_no_match /Debug Topic Count/, log.string
2081 assert_match /Warn Topic Count/, log.string
2082 assert_match /Error Topic Count/, log.string
2083 ensure
2084 ActiveRecord::Base.logger = original_logger
2085 end
2086
2087 def test_benchmark_with_use_silence
2088 original_logger = ActiveRecord::Base.logger
2089 log = StringIO.new
2090 ActiveRecord::Base.logger = Logger.new(log)
2091 ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, true) { ActiveRecord::Base.logger.debug "Loud" }
2092 ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, false) { ActiveRecord::Base.logger.debug "Quiet" }
2093 assert_no_match /Loud/, log.string
2094 assert_match /Quiet/, log.string
2095 ensure
2096 ActiveRecord::Base.logger = original_logger
2097 end
2098
2099 def test_create_with_custom_timestamps
2100 custom_datetime = 1.hour.ago.beginning_of_day
2101
2102 %w(created_at created_on updated_at updated_on).each do |attribute|
2103 parrot = LiveParrot.create(:name => "colombian", attribute => custom_datetime)
2104 assert_equal custom_datetime, parrot[attribute]
2105 end
2106 end
2107 end