ibm_db 0.4.0 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/test/base_test.rb ADDED
@@ -0,0 +1,1579 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/topic'
3
+ require 'fixtures/reply'
4
+ require 'fixtures/company'
5
+ require 'fixtures/customer'
6
+ require 'fixtures/developer'
7
+ require 'fixtures/project'
8
+ require 'fixtures/default'
9
+ require 'fixtures/auto_id'
10
+ require 'fixtures/column_name'
11
+ require 'fixtures/subscriber'
12
+ require 'fixtures/keyboard'
13
+ require 'fixtures/post'
14
+
15
+ class Category < ActiveRecord::Base; end
16
+ class Smarts < ActiveRecord::Base; end
17
+ class CreditCard < ActiveRecord::Base
18
+ class PinNumber < ActiveRecord::Base
19
+ class CvvCode < ActiveRecord::Base; end
20
+ class SubCvvCode < CvvCode; end
21
+ end
22
+ class SubPinNumber < PinNumber; end
23
+ class Brand < Category; end
24
+ end
25
+ class MasterCreditCard < ActiveRecord::Base; end
26
+ class Post < ActiveRecord::Base; end
27
+ class Computer < ActiveRecord::Base; end
28
+ class NonExistentTable < ActiveRecord::Base; end
29
+ class TestOracleDefault < ActiveRecord::Base; end
30
+
31
+ class LoosePerson < ActiveRecord::Base
32
+ self.table_name = 'people'
33
+ self.abstract_class = true
34
+ attr_protected :credit_rating, :administrator
35
+ end
36
+
37
+ class LooseDescendant < LoosePerson
38
+ attr_protected :phone_number
39
+ end
40
+
41
+ class TightPerson < ActiveRecord::Base
42
+ self.table_name = 'people'
43
+ attr_accessible :name, :address
44
+ end
45
+
46
+ class TightDescendant < TightPerson
47
+ attr_accessible :phone_number
48
+ end
49
+
50
+ class Booleantest < ActiveRecord::Base; end
51
+
52
+ class Task < ActiveRecord::Base
53
+ attr_protected :starting
54
+ end
55
+
56
+ class BasicsTest < Test::Unit::TestCase
57
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts
58
+
59
+ def test_table_exists
60
+ assert !NonExistentTable.table_exists?
61
+ assert Topic.table_exists?
62
+ end
63
+
64
+ def test_set_attributes
65
+ topic = Topic.find(1)
66
+ topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
67
+ topic.save
68
+ assert_equal("Budget", topic.title)
69
+ assert_equal("Jason", topic.author_name)
70
+ assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
71
+ end
72
+
73
+ def test_integers_as_nil
74
+ test = AutoId.create('value' => '')
75
+ assert_nil AutoId.find(test.id).value
76
+ end
77
+
78
+ def test_set_attributes_with_block
79
+ topic = Topic.new do |t|
80
+ t.title = "Budget"
81
+ t.author_name = "Jason"
82
+ end
83
+
84
+ assert_equal("Budget", topic.title)
85
+ assert_equal("Jason", topic.author_name)
86
+ end
87
+
88
+ def test_respond_to?
89
+ topic = Topic.find(1)
90
+ assert topic.respond_to?("title")
91
+ assert topic.respond_to?("title?")
92
+ assert topic.respond_to?("title=")
93
+ assert topic.respond_to?(:title)
94
+ assert topic.respond_to?(:title?)
95
+ assert topic.respond_to?(:title=)
96
+ assert topic.respond_to?("author_name")
97
+ assert topic.respond_to?("attribute_names")
98
+ assert !topic.respond_to?("nothingness")
99
+ assert !topic.respond_to?(:nothingness)
100
+ end
101
+
102
+ def test_array_content
103
+ topic = Topic.new
104
+ topic.content = %w( one two three )
105
+ topic.save
106
+
107
+ assert_equal(%w( one two three ), Topic.find(topic.id).content)
108
+ end
109
+
110
+ def test_hash_content
111
+ topic = Topic.new
112
+ topic.content = { "one" => 1, "two" => 2 }
113
+ topic.save
114
+
115
+ assert_equal 2, Topic.find(topic.id).content["two"]
116
+
117
+ topic.content["three"] = 3
118
+ topic.save
119
+
120
+ assert_equal 3, Topic.find(topic.id).content["three"]
121
+ end
122
+
123
+ def test_update_array_content
124
+ topic = Topic.new
125
+ topic.content = %w( one two three )
126
+
127
+ topic.content.push "four"
128
+ assert_equal(%w( one two three four ), topic.content)
129
+
130
+ topic.save
131
+
132
+ topic = Topic.find(topic.id)
133
+ topic.content << "five"
134
+ assert_equal(%w( one two three four five ), topic.content)
135
+ end
136
+
137
+ def test_case_sensitive_attributes_hash
138
+ # DB2 is not case-sensitive
139
+ return true if current_adapter?(:DB2Adapter, :IBM_DBAdapter)
140
+
141
+ assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
142
+ end
143
+
144
+ def test_create
145
+ topic = Topic.new
146
+ topic.title = "New Topic"
147
+ topic.save
148
+ topic_reloaded = Topic.find(topic.id)
149
+ assert_equal("New Topic", topic_reloaded.title)
150
+ end
151
+
152
+ def test_save!
153
+ topic = Topic.new(:title => "New Topic")
154
+ assert topic.save!
155
+
156
+ reply = Reply.new
157
+ assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
158
+ end
159
+
160
+ def test_save_null_string_attributes
161
+ topic = Topic.find(1)
162
+ topic.attributes = { "title" => "null", "author_name" => "null" }
163
+ topic.save!
164
+ topic.reload
165
+ assert_equal("null", topic.title)
166
+ assert_equal("null", topic.author_name)
167
+ end
168
+
169
+ def test_hashes_not_mangled
170
+ new_topic = { :title => "New Topic" }
171
+ new_topic_values = { :title => "AnotherTopic" }
172
+
173
+ topic = Topic.new(new_topic)
174
+ assert_equal new_topic[:title], topic.title
175
+
176
+ topic.attributes= new_topic_values
177
+ assert_equal new_topic_values[:title], topic.title
178
+ end
179
+
180
+ def test_create_many
181
+ topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
182
+ assert_equal 2, topics.size
183
+ assert_equal "first", topics.first.title
184
+ end
185
+
186
+ def test_create_columns_not_equal_attributes
187
+ topic = Topic.new
188
+ topic.title = 'Another New Topic'
189
+ topic.send :write_attribute, 'does_not_exist', 'test'
190
+ assert_nothing_raised { topic.save }
191
+ end
192
+
193
+ def test_create_through_factory
194
+ topic = Topic.create("title" => "New Topic")
195
+ topicReloaded = Topic.find(topic.id)
196
+ assert_equal(topic, topicReloaded)
197
+ end
198
+
199
+ def test_update
200
+ topic = Topic.new
201
+ topic.title = "Another New Topic"
202
+ topic.written_on = "2003-12-12 23:23:00"
203
+ topic.save
204
+ topicReloaded = Topic.find(topic.id)
205
+ assert_equal("Another New Topic", topicReloaded.title)
206
+
207
+ topicReloaded.title = "Updated topic"
208
+ topicReloaded.save
209
+
210
+ topicReloadedAgain = Topic.find(topic.id)
211
+
212
+ assert_equal("Updated topic", topicReloadedAgain.title)
213
+ end
214
+
215
+ def test_update_columns_not_equal_attributes
216
+ topic = Topic.new
217
+ topic.title = "Still another topic"
218
+ topic.save
219
+
220
+ topicReloaded = Topic.find(topic.id)
221
+ topicReloaded.title = "A New Topic"
222
+ topicReloaded.send :write_attribute, 'does_not_exist', 'test'
223
+ assert_nothing_raised { topicReloaded.save }
224
+ end
225
+
226
+ def test_write_attribute
227
+ topic = Topic.new
228
+ topic.send(:write_attribute, :title, "Still another topic")
229
+ assert_equal "Still another topic", topic.title
230
+
231
+ topic.send(:write_attribute, "title", "Still another topic: part 2")
232
+ assert_equal "Still another topic: part 2", topic.title
233
+ end
234
+
235
+ def test_read_attribute
236
+ topic = Topic.new
237
+ topic.title = "Don't change the topic"
238
+ assert_equal "Don't change the topic", topic.send(:read_attribute, "title")
239
+ assert_equal "Don't change the topic", topic["title"]
240
+
241
+ assert_equal "Don't change the topic", topic.send(:read_attribute, :title)
242
+ assert_equal "Don't change the topic", topic[:title]
243
+ end
244
+
245
+ def test_read_attribute_when_false
246
+ topic = topics(:first)
247
+ topic.approved = false
248
+ assert !topic.approved?, "approved should be false"
249
+ topic.approved = "false"
250
+ assert !topic.approved?, "approved should be false"
251
+ end
252
+
253
+ def test_read_attribute_when_true
254
+ topic = topics(:first)
255
+ topic.approved = true
256
+ assert topic.approved?, "approved should be true"
257
+ topic.approved = "true"
258
+ assert topic.approved?, "approved should be true"
259
+ end
260
+
261
+ def test_read_write_boolean_attribute
262
+ topic = Topic.new
263
+ # puts ""
264
+ # puts "New Topic"
265
+ # puts topic.inspect
266
+ topic.approved = "false"
267
+ # puts "Expecting false"
268
+ # puts topic.inspect
269
+ assert !topic.approved?, "approved should be false"
270
+ topic.approved = "false"
271
+ # puts "Expecting false"
272
+ # puts topic.inspect
273
+ assert !topic.approved?, "approved should be false"
274
+ topic.approved = "true"
275
+ # puts "Expecting true"
276
+ # puts topic.inspect
277
+ assert topic.approved?, "approved should be true"
278
+ topic.approved = "true"
279
+ # puts "Expecting true"
280
+ # puts topic.inspect
281
+ assert topic.approved?, "approved should be true"
282
+ # puts ""
283
+ end
284
+
285
+ def test_reader_generation
286
+ Topic.find(:first).title
287
+ Firm.find(:first).name
288
+ Client.find(:first).name
289
+ if ActiveRecord::Base.generate_read_methods
290
+ assert_readers(Topic, %w(type replies_count))
291
+ assert_readers(Firm, %w(type))
292
+ assert_readers(Client, %w(type ruby_type rating?))
293
+ else
294
+ [Topic, Firm, Client].each {|klass| assert_equal klass.read_methods, {}}
295
+ end
296
+ end
297
+
298
+ def test_reader_for_invalid_column_names
299
+ # column names which aren't legal ruby ids
300
+ topic = Topic.find(:first)
301
+ topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
302
+ assert !Topic.read_methods.include?("mumub-jumbo")
303
+ end
304
+
305
+ def test_non_attribute_access_and_assignment
306
+ topic = Topic.new
307
+ assert !topic.respond_to?("mumbo")
308
+ assert_raises(NoMethodError) { topic.mumbo }
309
+ assert_raises(NoMethodError) { topic.mumbo = 5 }
310
+ end
311
+
312
+ def test_preserving_date_objects
313
+ # SQL Server doesn't have a separate column type just for dates, so all are returned as time
314
+ return true if current_adapter?(:SQLServerAdapter)
315
+
316
+ if current_adapter?(:SybaseAdapter)
317
+ # Sybase ctlib does not (yet?) support the date type; use datetime instead.
318
+ assert_kind_of(
319
+ Time, Topic.find(1).last_read,
320
+ "The last_read attribute should be of the Time class"
321
+ )
322
+ else
323
+ assert_kind_of(
324
+ Date, Topic.find(1).last_read,
325
+ "The last_read attribute should be of the Date class"
326
+ )
327
+ end
328
+ end
329
+
330
+ def test_preserving_time_objects
331
+ assert_kind_of(
332
+ Time, Topic.find(1).bonus_time,
333
+ "The bonus_time attribute should be of the Time class"
334
+ )
335
+
336
+ assert_kind_of(
337
+ Time, Topic.find(1).written_on,
338
+ "The written_on attribute should be of the Time class"
339
+ )
340
+
341
+ # For adapters which support microsecond resolution.
342
+ if current_adapter?(:PostgreSQLAdapter)
343
+ assert_equal 11, Topic.find(1).written_on.sec
344
+ assert_equal 223300, Topic.find(1).written_on.usec
345
+ assert_equal 9900, Topic.find(2).written_on.usec
346
+ end
347
+ end
348
+
349
+ def test_destroy
350
+ topic = Topic.find(1)
351
+ assert_equal topic, topic.destroy, 'topic.destroy did not return self'
352
+ assert topic.frozen?, 'topic not frozen after destroy'
353
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
354
+ end
355
+
356
+ def test_record_not_found_exception
357
+ assert_raises(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
358
+ end
359
+
360
+ def test_initialize_with_attributes
361
+ topic = Topic.new({
362
+ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
363
+ })
364
+
365
+ assert_equal("initialized from attributes", topic.title)
366
+ end
367
+
368
+ def test_initialize_with_invalid_attribute
369
+ begin
370
+ topic = Topic.new({ "title" => "test",
371
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
372
+ rescue ActiveRecord::MultiparameterAssignmentErrors => ex
373
+ assert_equal(1, ex.errors.size)
374
+ assert_equal("last_read", ex.errors[0].attribute)
375
+ end
376
+ end
377
+
378
+ def test_load
379
+ topics = Topic.find(:all, :order => 'id')
380
+ assert_equal(2, topics.size)
381
+ assert_equal(topics(:first).title, topics.first.title)
382
+ end
383
+
384
+ def test_load_with_condition
385
+ topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
386
+
387
+ assert_equal(1, topics.size)
388
+ assert_equal(topics(:second).title, topics.first.title)
389
+ end
390
+
391
+ def test_table_name_guesses
392
+ classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
393
+
394
+ assert_equal "topics", Topic.table_name
395
+
396
+ assert_equal "categories", Category.table_name
397
+ assert_equal "smarts", Smarts.table_name
398
+ assert_equal "credit_cards", CreditCard.table_name
399
+ assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
400
+ assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
401
+ assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
402
+ assert_equal "categories", CreditCard::Brand.table_name
403
+ assert_equal "master_credit_cards", MasterCreditCard.table_name
404
+
405
+ ActiveRecord::Base.pluralize_table_names = false
406
+ classes.each(&:reset_table_name)
407
+
408
+ assert_equal "category", Category.table_name
409
+ assert_equal "smarts", Smarts.table_name
410
+ assert_equal "credit_card", CreditCard.table_name
411
+ assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
412
+ assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
413
+ assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
414
+ assert_equal "category", CreditCard::Brand.table_name
415
+ assert_equal "master_credit_card", MasterCreditCard.table_name
416
+
417
+ ActiveRecord::Base.pluralize_table_names = true
418
+ classes.each(&:reset_table_name)
419
+
420
+ ActiveRecord::Base.table_name_prefix = "test_"
421
+ Category.reset_table_name
422
+ assert_equal "test_categories", Category.table_name
423
+ ActiveRecord::Base.table_name_suffix = "_test"
424
+ Category.reset_table_name
425
+ assert_equal "test_categories_test", Category.table_name
426
+ ActiveRecord::Base.table_name_prefix = ""
427
+ Category.reset_table_name
428
+ assert_equal "categories_test", Category.table_name
429
+ ActiveRecord::Base.table_name_suffix = ""
430
+ Category.reset_table_name
431
+ assert_equal "categories", Category.table_name
432
+
433
+ ActiveRecord::Base.pluralize_table_names = false
434
+ ActiveRecord::Base.table_name_prefix = "test_"
435
+ Category.reset_table_name
436
+ assert_equal "test_category", Category.table_name
437
+ ActiveRecord::Base.table_name_suffix = "_test"
438
+ Category.reset_table_name
439
+ assert_equal "test_category_test", Category.table_name
440
+ ActiveRecord::Base.table_name_prefix = ""
441
+ Category.reset_table_name
442
+ assert_equal "category_test", Category.table_name
443
+ ActiveRecord::Base.table_name_suffix = ""
444
+ Category.reset_table_name
445
+ assert_equal "category", Category.table_name
446
+
447
+ ActiveRecord::Base.pluralize_table_names = true
448
+ classes.each(&:reset_table_name)
449
+ end
450
+
451
+ def test_destroy_all
452
+ assert_equal 2, Topic.count
453
+
454
+ Topic.destroy_all "author_name = 'Mary'"
455
+ assert_equal 1, Topic.count
456
+ end
457
+
458
+ def test_destroy_many
459
+ assert_equal 3, Client.count
460
+ Client.destroy([2, 3])
461
+ assert_equal 1, Client.count
462
+ end
463
+
464
+ def test_delete_many
465
+ Topic.delete([1, 2])
466
+ assert_equal 0, Topic.count
467
+ end
468
+
469
+ def test_boolean_attributes
470
+ assert ! Topic.find(1).approved?
471
+ assert Topic.find(2).approved?
472
+ end
473
+
474
+ def test_increment_counter
475
+ Topic.increment_counter("replies_count", 1)
476
+ assert_equal 2, Topic.find(1).replies_count
477
+
478
+ Topic.increment_counter("replies_count", 1)
479
+ assert_equal 3, Topic.find(1).replies_count
480
+ end
481
+
482
+ def test_decrement_counter
483
+ Topic.decrement_counter("replies_count", 2)
484
+ assert_equal -1, Topic.find(2).replies_count
485
+
486
+ Topic.decrement_counter("replies_count", 2)
487
+ assert_equal -2, Topic.find(2).replies_count
488
+ end
489
+
490
+ def test_update_all
491
+ # The ADO library doesn't support the number of affected rows
492
+ return true if current_adapter?(:SQLServerAdapter)
493
+
494
+ assert_equal 2, Topic.update_all("content = 'bulk updated!'")
495
+ assert_equal "bulk updated!", Topic.find(1).content
496
+ assert_equal "bulk updated!", Topic.find(2).content
497
+ assert_equal 2, Topic.update_all(['content = ?', 'bulk updated again!'])
498
+ assert_equal "bulk updated again!", Topic.find(1).content
499
+ assert_equal "bulk updated again!", Topic.find(2).content
500
+ end
501
+
502
+ def test_update_many
503
+ topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
504
+ updated = Topic.update(topic_data.keys, topic_data.values)
505
+
506
+ assert_equal 2, updated.size
507
+ assert_equal "1 updated", Topic.find(1).content
508
+ assert_equal "2 updated", Topic.find(2).content
509
+ end
510
+
511
+ def test_delete_all
512
+ # The ADO library doesn't support the number of affected rows
513
+ return true if current_adapter?(:SQLServerAdapter)
514
+
515
+ assert_equal 2, Topic.delete_all
516
+ end
517
+
518
+ def test_update_by_condition
519
+ Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
520
+ assert_equal "Have a nice day", Topic.find(1).content
521
+ assert_equal "bulk updated!", Topic.find(2).content
522
+ end
523
+
524
+ def test_attribute_present
525
+ t = Topic.new
526
+ t.title = "hello there!"
527
+ t.written_on = Time.now
528
+ assert t.attribute_present?("title")
529
+ assert t.attribute_present?("written_on")
530
+ assert !t.attribute_present?("content")
531
+ end
532
+
533
+ def test_attribute_keys_on_new_instance
534
+ t = Topic.new
535
+ assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
536
+ assert_raise(NoMethodError) { t.title2 }
537
+ end
538
+
539
+ def test_class_name
540
+ assert_equal "Firm", ActiveRecord::Base.class_name("firms")
541
+ assert_equal "Category", ActiveRecord::Base.class_name("categories")
542
+ assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder")
543
+
544
+ ActiveRecord::Base.pluralize_table_names = false
545
+ assert_equal "Firms", ActiveRecord::Base.class_name( "firms" )
546
+ ActiveRecord::Base.pluralize_table_names = true
547
+
548
+ ActiveRecord::Base.table_name_prefix = "test_"
549
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" )
550
+ ActiveRecord::Base.table_name_suffix = "_tests"
551
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" )
552
+ ActiveRecord::Base.table_name_prefix = ""
553
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" )
554
+ ActiveRecord::Base.table_name_suffix = ""
555
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
556
+ end
557
+
558
+ def test_null_fields
559
+ assert_nil Topic.find(1).parent_id
560
+ assert_nil Topic.create("title" => "Hey you").parent_id
561
+ end
562
+
563
+ def test_default_values
564
+ topic = Topic.new
565
+ assert topic.approved?
566
+ assert_nil topic.written_on
567
+ assert_nil topic.bonus_time
568
+ assert_nil topic.last_read
569
+
570
+ topic.save
571
+
572
+ topic = Topic.find(topic.id)
573
+ assert topic.approved?
574
+ assert_nil topic.last_read
575
+
576
+ # Oracle has some funky default handling, so it requires a bit of
577
+ # extra testing. See ticket #2788.
578
+ if current_adapter?(:OracleAdapter)
579
+ test = TestOracleDefault.new
580
+ assert_equal "X", test.test_char
581
+ assert_equal "hello", test.test_string
582
+ assert_equal 3, test.test_int
583
+ end
584
+ end
585
+
586
+ # Oracle, SQLServer, and Sybase do not have a TIME datatype.
587
+ unless current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
588
+ def test_utc_as_time_zone
589
+ Topic.default_timezone = :utc
590
+ attributes = { "bonus_time" => "5:42:00AM" }
591
+ topic = Topic.find(1)
592
+ topic.attributes = attributes
593
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
594
+ Topic.default_timezone = :local
595
+ end
596
+
597
+ def test_utc_as_time_zone_and_new
598
+ Topic.default_timezone = :utc
599
+ attributes = { "bonus_time(1i)"=>"2000",
600
+ "bonus_time(2i)"=>"1",
601
+ "bonus_time(3i)"=>"1",
602
+ "bonus_time(4i)"=>"10",
603
+ "bonus_time(5i)"=>"35",
604
+ "bonus_time(6i)"=>"50" }
605
+ topic = Topic.new(attributes)
606
+ assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
607
+ Topic.default_timezone = :local
608
+ end
609
+ end
610
+
611
+ def test_default_values_on_empty_strings
612
+ topic = Topic.new
613
+ topic.approved = nil
614
+ topic.last_read = nil
615
+
616
+ topic.save
617
+
618
+ topic = Topic.find(topic.id)
619
+ assert_nil topic.last_read
620
+
621
+ # Sybase adapter does not allow nulls in boolean columns
622
+ if current_adapter?(:SybaseAdapter)
623
+ assert topic.approved == false
624
+ else
625
+ assert_nil topic.approved
626
+ end
627
+ end
628
+
629
+ def test_equality
630
+ assert_equal Topic.find(1), Topic.find(2).topic
631
+ end
632
+
633
+ def test_equality_of_new_records
634
+ assert_not_equal Topic.new, Topic.new
635
+ end
636
+
637
+ def test_hashing
638
+ assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
639
+ end
640
+
641
+ def test_destroy_new_record
642
+ client = Client.new
643
+ client.destroy
644
+ assert client.frozen?
645
+ end
646
+
647
+ def test_destroy_record_with_associations
648
+ client = Client.find(3)
649
+ client.destroy
650
+ assert client.frozen?
651
+ assert_kind_of Firm, client.firm
652
+ assert_raises(TypeError) { client.name = "something else" }
653
+ end
654
+
655
+ def test_update_attribute
656
+ assert !Topic.find(1).approved?
657
+ Topic.find(1).update_attribute("approved", true)
658
+ assert Topic.find(1).approved?
659
+
660
+ Topic.find(1).update_attribute(:approved, false)
661
+ assert !Topic.find(1).approved?
662
+ end
663
+
664
+ def test_update_attributes
665
+ topic = Topic.find(1)
666
+ assert !topic.approved?
667
+ assert_equal "The First Topic", topic.title
668
+
669
+ topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
670
+ topic.reload
671
+ assert topic.approved?
672
+ assert_equal "The First Topic Updated", topic.title
673
+
674
+ topic.update_attributes(:approved => false, :title => "The First Topic")
675
+ topic.reload
676
+ assert !topic.approved?
677
+ assert_equal "The First Topic", topic.title
678
+ end
679
+
680
+ def test_update_attributes!
681
+ reply = Reply.find(2)
682
+ assert_equal "The Second Topic's of the day", reply.title
683
+ assert_equal "Have a nice day", reply.content
684
+
685
+ reply.update_attributes!("title" => "The Second Topic's of the day updated", "content" => "Have a nice evening")
686
+ reply.reload
687
+ assert_equal "The Second Topic's of the day updated", reply.title
688
+ assert_equal "Have a nice evening", reply.content
689
+
690
+ reply.update_attributes!(:title => "The Second Topic's of the day", :content => "Have a nice day")
691
+ reply.reload
692
+ assert_equal "The Second Topic's of the day", reply.title
693
+ assert_equal "Have a nice day", reply.content
694
+
695
+ assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
696
+ end
697
+
698
+ def test_mass_assignment_protection
699
+ firm = Firm.new
700
+ firm.attributes = { "name" => "Next Angle", "rating" => 5 }
701
+ assert_equal 1, firm.rating
702
+ end
703
+
704
+ def test_customized_primary_key_remains_protected
705
+ subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
706
+ assert_nil subscriber.id
707
+
708
+ keyboard = Keyboard.new(:key_number => 9, :name => 'nice try')
709
+ assert_nil keyboard.id
710
+ end
711
+
712
+ def test_customized_primary_key_remains_protected_when_refered_to_as_id
713
+ subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
714
+ assert_nil subscriber.id
715
+
716
+ keyboard = Keyboard.new(:id => 9, :name => 'nice try')
717
+ assert_nil keyboard.id
718
+ end
719
+
720
+ def test_mass_assignment_protection_on_defaults
721
+ firm = Firm.new
722
+ firm.attributes = { "id" => 5, "type" => "Client" }
723
+ assert_nil firm.id
724
+ assert_equal "Firm", firm[:type]
725
+ end
726
+
727
+ def test_mass_assignment_accessible
728
+ reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
729
+ reply.save
730
+
731
+ assert reply.approved?
732
+
733
+ reply.approved = false
734
+ reply.save
735
+
736
+ assert !reply.approved?
737
+ end
738
+
739
+ def test_mass_assignment_protection_inheritance
740
+ assert_nil LoosePerson.accessible_attributes
741
+ assert_equal [ :credit_rating, :administrator ], LoosePerson.protected_attributes
742
+
743
+ assert_nil LooseDescendant.accessible_attributes
744
+ assert_equal [ :credit_rating, :administrator, :phone_number ], LooseDescendant.protected_attributes
745
+
746
+ assert_nil TightPerson.protected_attributes
747
+ assert_equal [ :name, :address ], TightPerson.accessible_attributes
748
+
749
+ assert_nil TightDescendant.protected_attributes
750
+ assert_equal [ :name, :address, :phone_number ], TightDescendant.accessible_attributes
751
+ end
752
+
753
+ def test_multiparameter_attributes_on_date
754
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
755
+ topic = Topic.find(1)
756
+ topic.attributes = attributes
757
+ # note that extra #to_date call allows test to pass for Oracle, which
758
+ # treats dates/times the same
759
+ assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
760
+ end
761
+
762
+ def test_multiparameter_attributes_on_date_with_empty_date
763
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
764
+ topic = Topic.find(1)
765
+ topic.attributes = attributes
766
+ # note that extra #to_date call allows test to pass for Oracle, which
767
+ # treats dates/times the same
768
+ assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
769
+ end
770
+
771
+ def test_multiparameter_attributes_on_date_with_all_empty
772
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
773
+ topic = Topic.find(1)
774
+ topic.attributes = attributes
775
+ assert_nil topic.last_read
776
+ end
777
+
778
+ def test_multiparameter_attributes_on_time
779
+ attributes = {
780
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
781
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
782
+ }
783
+ topic = Topic.find(1)
784
+ topic.attributes = attributes
785
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
786
+ end
787
+
788
+ def test_multiparameter_attributes_on_time_with_empty_seconds
789
+ attributes = {
790
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
791
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
792
+ }
793
+ topic = Topic.find(1)
794
+ topic.attributes = attributes
795
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
796
+ end
797
+
798
+ def test_multiparameter_mass_assignment_protector
799
+ task = Task.new
800
+ time = Time.mktime(2000, 1, 1, 1)
801
+ task.starting = time
802
+ attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" }
803
+ task.attributes = attributes
804
+ assert_equal time, task.starting
805
+ end
806
+
807
+ def test_multiparameter_assignment_of_aggregation
808
+ customer = Customer.new
809
+ address = Address.new("The Street", "The City", "The Country")
810
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
811
+ customer.attributes = attributes
812
+ assert_equal address, customer.address
813
+ end
814
+
815
+ def test_attributes_on_dummy_time
816
+ # Oracle, SQL Server, and Sybase do not have a TIME datatype.
817
+ return true if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
818
+
819
+ attributes = {
820
+ "bonus_time" => "5:42:00AM"
821
+ }
822
+ topic = Topic.find(1)
823
+ topic.attributes = attributes
824
+ assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
825
+ end
826
+
827
+ def test_boolean
828
+ b_false = Booleantest.create({ "value" => false })
829
+ false_id = b_false.id
830
+ b_true = Booleantest.create({ "value" => true })
831
+ true_id = b_true.id
832
+
833
+ b_false = Booleantest.find(false_id)
834
+ assert !b_false.value?
835
+ b_true = Booleantest.find(true_id)
836
+ assert b_true.value?
837
+ end
838
+
839
+ def test_boolean_cast_from_string
840
+ b_false = Booleantest.create({ "value" => "0" })
841
+ false_id = b_false.id
842
+ b_true = Booleantest.create({ "value" => "1" })
843
+ true_id = b_true.id
844
+
845
+ b_false = Booleantest.find(false_id)
846
+ assert !b_false.value?
847
+ b_true = Booleantest.find(true_id)
848
+ assert b_true.value?
849
+ end
850
+
851
+ def test_clone
852
+ topic = Topic.find(1)
853
+ cloned_topic = nil
854
+ assert_nothing_raised { cloned_topic = topic.clone }
855
+ assert_equal topic.title, cloned_topic.title
856
+ assert cloned_topic.new_record?
857
+
858
+ # test if the attributes have been cloned
859
+ topic.title = "a"
860
+ cloned_topic.title = "b"
861
+ assert_equal "a", topic.title
862
+ assert_equal "b", cloned_topic.title
863
+
864
+ # test if the attribute values have been cloned
865
+ topic.title = {"a" => "b"}
866
+ cloned_topic = topic.clone
867
+ cloned_topic.title["a"] = "c"
868
+ assert_equal "b", topic.title["a"]
869
+
870
+ cloned_topic.save
871
+ assert !cloned_topic.new_record?
872
+ assert cloned_topic.id != topic.id
873
+ end
874
+
875
+ def test_clone_with_aggregate_of_same_name_as_attribute
876
+ dev = DeveloperWithAggregate.find(1)
877
+ assert_kind_of DeveloperSalary, dev.salary
878
+
879
+ clone = nil
880
+ assert_nothing_raised { clone = dev.clone }
881
+ assert_kind_of DeveloperSalary, clone.salary
882
+ assert_equal dev.salary.amount, clone.salary.amount
883
+ assert clone.new_record?
884
+
885
+ # test if the attributes have been cloned
886
+ original_amount = clone.salary.amount
887
+ dev.salary.amount = 1
888
+ assert_equal original_amount, clone.salary.amount
889
+
890
+ assert clone.save
891
+ assert !clone.new_record?
892
+ assert clone.id != dev.id
893
+ end
894
+
895
+ def test_clone_preserves_subtype
896
+ clone = nil
897
+ assert_nothing_raised { clone = Company.find(3).clone }
898
+ assert_kind_of Client, clone
899
+ end
900
+
901
+ def test_bignum
902
+ company = Company.find(1)
903
+ company.rating = 2147483647
904
+ company.save
905
+ company = Company.find(1)
906
+ assert_equal 2147483647, company.rating
907
+ end
908
+
909
+ # TODO: extend defaults tests to other databases!
910
+ if current_adapter?(:PostgreSQLAdapter)
911
+ def test_default
912
+ default = Default.new
913
+
914
+ # fixed dates / times
915
+ assert_equal Date.new(2004, 1, 1), default.fixed_date
916
+ assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
917
+
918
+ # char types
919
+ assert_equal 'Y', default.char1
920
+ assert_equal 'a varchar field', default.char2
921
+ assert_equal 'a text field', default.char3
922
+ end
923
+
924
+ class Geometric < ActiveRecord::Base; end
925
+ def test_geometric_content
926
+
927
+ # accepted format notes:
928
+ # ()'s aren't required
929
+ # values can be a mix of float or integer
930
+
931
+ g = Geometric.new(
932
+ :a_point => '(5.0, 6.1)',
933
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
934
+ :a_line_segment => '(2.0, 3), (5.5, 7.0)',
935
+ :a_box => '2.0, 3, 5.5, 7.0',
936
+ :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
937
+ :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
938
+ :a_circle => '<(5.3, 10.4), 2>'
939
+ )
940
+
941
+ assert g.save
942
+
943
+ # Reload and check that we have all the geometric attributes.
944
+ h = Geometric.find(g.id)
945
+
946
+ assert_equal '(5,6.1)', h.a_point
947
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
948
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
949
+ assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
950
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
951
+ assert_equal '<(5.3,10.4),2>', h.a_circle
952
+
953
+ # use a geometric function to test for an open path
954
+ objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
955
+ assert_equal objs[0].isopen, 't'
956
+
957
+ # test alternate formats when defining the geometric types
958
+
959
+ g = Geometric.new(
960
+ :a_point => '5.0, 6.1',
961
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
962
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
963
+ :a_box => '(2.0, 3), (5.5, 7.0)',
964
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
965
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
966
+ :a_circle => '((5.3, 10.4), 2)'
967
+ )
968
+
969
+ assert g.save
970
+
971
+ # Reload and check that we have all the geometric attributes.
972
+ h = Geometric.find(g.id)
973
+
974
+ assert_equal '(5,6.1)', h.a_point
975
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
976
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
977
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
978
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
979
+ assert_equal '<(5.3,10.4),2>', h.a_circle
980
+
981
+ # use a geometric function to test for an closed path
982
+ objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
983
+ assert_equal objs[0].isclosed, 't'
984
+ end
985
+ end
986
+
987
+ class NumericData < ActiveRecord::Base
988
+ self.table_name = 'numeric_data'
989
+ end
990
+
991
+ def test_numeric_fields
992
+ m = NumericData.new(
993
+ :bank_balance => 1586.43,
994
+ :big_bank_balance => BigDecimal("1000234000567.95"),
995
+ :world_population => 6000000000,
996
+ :my_house_population => 3
997
+ )
998
+ assert m.save
999
+
1000
+ m1 = NumericData.find(m.id)
1001
+ assert_not_nil m1
1002
+
1003
+ # As with migration_test.rb, we should make world_population >= 2**62
1004
+ # to cover 64-bit platforms and test it is a Bignum, but the main thing
1005
+ # is that it's an Integer.
1006
+ unless current_adapter?(:IBM_DBAdapter)
1007
+ assert_kind_of Integer, m1.world_population
1008
+ else
1009
+ assert_kind_of BigDecimal, m1.world_population
1010
+ end
1011
+ assert_equal 6000000000, m1.world_population
1012
+
1013
+ unless current_adapter?(:IBM_DBAdapter)
1014
+ assert_kind_of Fixnum, m1.my_house_population
1015
+ else
1016
+ assert_kind_of BigDecimal, m1.my_house_population
1017
+ end
1018
+ assert_equal 3, m1.my_house_population
1019
+
1020
+ assert_kind_of BigDecimal, m1.bank_balance
1021
+ assert_equal BigDecimal("1586.43"), m1.bank_balance
1022
+
1023
+ assert_kind_of BigDecimal, m1.big_bank_balance
1024
+ assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
1025
+ end
1026
+
1027
+ def test_auto_id
1028
+ auto = AutoId.new
1029
+ auto.save
1030
+ assert (auto.id > 0)
1031
+ end
1032
+
1033
+ def quote_column_name(name)
1034
+ "<#{name}>"
1035
+ end
1036
+
1037
+ def test_quote_keys
1038
+ ar = AutoId.new
1039
+ source = {"foo" => "bar", "baz" => "quux"}
1040
+ actual = ar.send(:quote_columns, self, source)
1041
+ inverted = actual.invert
1042
+ assert_equal("<foo>", inverted["bar"])
1043
+ assert_equal("<baz>", inverted["quux"])
1044
+ end
1045
+
1046
+ def test_sql_injection_via_find
1047
+ assert_raises(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1048
+ Topic.find("123456 OR id > 0")
1049
+ end
1050
+ end
1051
+
1052
+ def test_column_name_properly_quoted
1053
+ col_record = ColumnName.new
1054
+ col_record.references = 40
1055
+ assert col_record.save
1056
+ col_record.references = 41
1057
+ assert col_record.save
1058
+ assert_not_nil c2 = ColumnName.find(col_record.id)
1059
+ assert_equal(41, c2.references)
1060
+ end
1061
+
1062
+ def test_quoting_arrays
1063
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
1064
+ assert_equal topics(:first).replies.size, replies.size
1065
+
1066
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
1067
+ assert_equal 0, replies.size
1068
+ end
1069
+
1070
+ MyObject = Struct.new :attribute1, :attribute2
1071
+
1072
+ def test_serialized_attribute
1073
+ myobj = MyObject.new('value1', 'value2')
1074
+ topic = Topic.create("content" => myobj)
1075
+ Topic.serialize("content", MyObject)
1076
+ assert_equal(myobj, topic.content)
1077
+ end
1078
+
1079
+ def test_serialized_attribute_with_class_constraint
1080
+ myobj = MyObject.new('value1', 'value2')
1081
+ topic = Topic.create("content" => myobj)
1082
+ Topic.serialize(:content, Hash)
1083
+
1084
+ assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
1085
+
1086
+ settings = { "color" => "blue" }
1087
+ Topic.find(topic.id).update_attribute("content", settings)
1088
+ assert_equal(settings, Topic.find(topic.id).content)
1089
+ Topic.serialize(:content)
1090
+ end
1091
+
1092
+ def test_quote
1093
+ author_name = "\\ \001 ' \n \\n \""
1094
+ topic = Topic.create('author_name' => author_name)
1095
+ assert_equal author_name, Topic.find(topic.id).author_name
1096
+ end
1097
+
1098
+ def test_quote_chars
1099
+ str = 'The Narrator'
1100
+ topic = Topic.create(:author_name => str)
1101
+ assert_equal str, topic.author_name
1102
+
1103
+ assert_kind_of ActiveSupport::Multibyte::Chars, str.chars
1104
+ topic = Topic.find_by_author_name(str.chars)
1105
+
1106
+ assert_kind_of Topic, topic
1107
+ assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1108
+ end
1109
+
1110
+ def test_class_level_destroy
1111
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1112
+ Topic.find(1).replies << should_be_destroyed_reply
1113
+
1114
+ Topic.destroy(1)
1115
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1116
+ assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
1117
+ end
1118
+
1119
+ def test_class_level_delete
1120
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1121
+ Topic.find(1).replies << should_be_destroyed_reply
1122
+
1123
+ Topic.delete(1)
1124
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1125
+ assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
1126
+ end
1127
+
1128
+ def test_increment_attribute
1129
+ assert_equal 1, topics(:first).replies_count
1130
+ topics(:first).increment! :replies_count
1131
+ assert_equal 2, topics(:first, :reload).replies_count
1132
+
1133
+ topics(:first).increment(:replies_count).increment!(:replies_count)
1134
+ assert_equal 4, topics(:first, :reload).replies_count
1135
+ end
1136
+
1137
+ def test_increment_nil_attribute
1138
+ assert_nil topics(:first).parent_id
1139
+ topics(:first).increment! :parent_id
1140
+ assert_equal 1, topics(:first).parent_id
1141
+ end
1142
+
1143
+ def test_decrement_attribute
1144
+ topics(:first).increment(:replies_count).increment!(:replies_count)
1145
+ assert_equal 3, topics(:first).replies_count
1146
+
1147
+ topics(:first).decrement!(:replies_count)
1148
+ assert_equal 2, topics(:first, :reload).replies_count
1149
+
1150
+ topics(:first).decrement(:replies_count).decrement!(:replies_count)
1151
+ assert_equal 0, topics(:first, :reload).replies_count
1152
+ end
1153
+
1154
+ def test_toggle_attribute
1155
+ assert !topics(:first).approved?
1156
+ topics(:first).toggle!(:approved)
1157
+ assert topics(:first).approved?
1158
+ topic = topics(:first)
1159
+ topic.toggle(:approved)
1160
+ assert !topic.approved?
1161
+ topic.reload
1162
+ assert topic.approved?
1163
+ end
1164
+
1165
+ def test_reload
1166
+ t1 = Topic.find(1)
1167
+ t2 = Topic.find(1)
1168
+ t1.title = "something else"
1169
+ t1.save
1170
+ t2.reload
1171
+ assert_equal t1.title, t2.title
1172
+ end
1173
+
1174
+ def test_define_attr_method_with_value
1175
+ k = Class.new( ActiveRecord::Base )
1176
+ k.send(:define_attr_method, :table_name, "foo")
1177
+ assert_equal "foo", k.table_name
1178
+ end
1179
+
1180
+ def test_define_attr_method_with_block
1181
+ k = Class.new( ActiveRecord::Base )
1182
+ k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1183
+ assert_equal "sys_id", k.primary_key
1184
+ end
1185
+
1186
+ def test_set_table_name_with_value
1187
+ k = Class.new( ActiveRecord::Base )
1188
+ k.table_name = "foo"
1189
+ assert_equal "foo", k.table_name
1190
+ k.set_table_name "bar"
1191
+ assert_equal "bar", k.table_name
1192
+ end
1193
+
1194
+ def test_set_table_name_with_block
1195
+ k = Class.new( ActiveRecord::Base )
1196
+ k.set_table_name { "ks" }
1197
+ assert_equal "ks", k.table_name
1198
+ end
1199
+
1200
+ def test_set_primary_key_with_value
1201
+ k = Class.new( ActiveRecord::Base )
1202
+ k.primary_key = "foo"
1203
+ assert_equal "foo", k.primary_key
1204
+ k.set_primary_key "bar"
1205
+ assert_equal "bar", k.primary_key
1206
+ end
1207
+
1208
+ def test_set_primary_key_with_block
1209
+ k = Class.new( ActiveRecord::Base )
1210
+ k.set_primary_key { "sys_" + original_primary_key }
1211
+ assert_equal "sys_id", k.primary_key
1212
+ end
1213
+
1214
+ def test_set_inheritance_column_with_value
1215
+ k = Class.new( ActiveRecord::Base )
1216
+ k.inheritance_column = "foo"
1217
+ assert_equal "foo", k.inheritance_column
1218
+ k.set_inheritance_column "bar"
1219
+ assert_equal "bar", k.inheritance_column
1220
+ end
1221
+
1222
+ def test_set_inheritance_column_with_block
1223
+ k = Class.new( ActiveRecord::Base )
1224
+ k.set_inheritance_column { original_inheritance_column + "_id" }
1225
+ assert_equal "type_id", k.inheritance_column
1226
+ end
1227
+
1228
+ def test_count_with_join
1229
+ res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1230
+ res2 = nil
1231
+ assert_deprecated 'count' do
1232
+ res2 = Post.count("posts.#{QUOTED_TYPE} = 'Post'",
1233
+ "LEFT JOIN comments ON posts.id=comments.post_id")
1234
+ end
1235
+ assert_equal res, res2
1236
+
1237
+ res3 = nil
1238
+ assert_nothing_raised do
1239
+ res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1240
+ :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1241
+ end
1242
+ assert_equal res, res3
1243
+
1244
+ 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"
1245
+ res5 = nil
1246
+ assert_nothing_raised do
1247
+ res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1248
+ :joins => "p, comments co",
1249
+ :select => "p.id")
1250
+ end
1251
+
1252
+ assert_equal res4, res5
1253
+
1254
+ 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"
1255
+ res7 = nil
1256
+ assert_nothing_raised do
1257
+ res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1258
+ :joins => "p, comments co",
1259
+ :select => "p.id",
1260
+ :distinct => true)
1261
+ end
1262
+ assert_equal res6, res7
1263
+ end
1264
+
1265
+ def test_clear_association_cache_stored
1266
+ firm = Firm.find(1)
1267
+ assert_kind_of Firm, firm
1268
+
1269
+ firm.clear_association_cache
1270
+ assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
1271
+ end
1272
+
1273
+ def test_clear_association_cache_new_record
1274
+ firm = Firm.new
1275
+ client_stored = Client.find(3)
1276
+ client_new = Client.new
1277
+ client_new.name = "The Joneses"
1278
+ clients = [ client_stored, client_new ]
1279
+
1280
+ firm.clients << clients
1281
+
1282
+ firm.clear_association_cache
1283
+
1284
+ assert_equal firm.clients.collect{ |x| x.name }.sort, clients.collect{ |x| x.name }.sort
1285
+ end
1286
+
1287
+ def test_interpolate_sql
1288
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') }
1289
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
1290
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
1291
+ end
1292
+
1293
+ def test_scoped_find_conditions
1294
+ scoped_developers = Developer.with_scope(:find => { :conditions => 'salary > 90000' }) do
1295
+ Developer.find(:all, :conditions => 'id < 5')
1296
+ end
1297
+ assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1298
+ assert_equal 3, scoped_developers.size
1299
+ end
1300
+
1301
+ def test_scoped_find_limit_offset
1302
+ scoped_developers = Developer.with_scope(:find => { :limit => 3, :offset => 2 }) do
1303
+ Developer.find(:all, :order => 'id')
1304
+ end
1305
+ assert !scoped_developers.include?(developers(:david))
1306
+ assert !scoped_developers.include?(developers(:jamis))
1307
+ assert_equal 3, scoped_developers.size
1308
+
1309
+ # Test without scoped find conditions to ensure we get the whole thing
1310
+ developers = Developer.find(:all, :order => 'id')
1311
+ assert_equal Developer.count, developers.size
1312
+ end
1313
+
1314
+ def test_scoped_find_order
1315
+ # Test order in scope
1316
+ scoped_developers = Developer.with_scope(:find => { :limit => 1, :order => 'salary DESC' }) do
1317
+ Developer.find(:all)
1318
+ end
1319
+ assert_equal 'Jamis', scoped_developers.first.name
1320
+ assert scoped_developers.include?(developers(:jamis))
1321
+ # Test scope without order and order in find
1322
+ scoped_developers = Developer.with_scope(:find => { :limit => 1 }) do
1323
+ Developer.find(:all, :order => 'salary DESC')
1324
+ end
1325
+ # Test scope order + find order, find has priority
1326
+ scoped_developers = Developer.with_scope(:find => { :limit => 3, :order => 'id DESC' }) do
1327
+ Developer.find(:all, :order => 'salary ASC')
1328
+ end
1329
+ assert scoped_developers.include?(developers(:poor_jamis))
1330
+ assert scoped_developers.include?(developers(:david))
1331
+ assert scoped_developers.include?(developers(:dev_10))
1332
+ # Test without scoped find conditions to ensure we get the right thing
1333
+ developers = Developer.find(:all, :order => 'id', :limit => 1)
1334
+ assert scoped_developers.include?(developers(:david))
1335
+ end
1336
+
1337
+ def test_scoped_find_limit_offset_including_has_many_association
1338
+ topics = Topic.with_scope(:find => {:limit => 1, :offset => 1, :include => :replies}) do
1339
+ Topic.find(:all, :order => "topics.id")
1340
+ end
1341
+ assert_equal 1, topics.size
1342
+ assert_equal 2, topics.first.id
1343
+ end
1344
+
1345
+ def test_scoped_find_order_including_has_many_association
1346
+ developers = Developer.with_scope(:find => { :order => 'developers.salary DESC', :include => :projects }) do
1347
+ Developer.find(:all)
1348
+ end
1349
+ assert developers.size >= 2
1350
+ for i in 1...developers.size
1351
+ assert developers[i-1].salary >= developers[i].salary
1352
+ end
1353
+ end
1354
+
1355
+ def test_abstract_class
1356
+ assert !ActiveRecord::Base.abstract_class?
1357
+ assert LoosePerson.abstract_class?
1358
+ assert !LooseDescendant.abstract_class?
1359
+ end
1360
+
1361
+ def test_base_class
1362
+ assert_equal LoosePerson, LoosePerson.base_class
1363
+ assert_equal LooseDescendant, LooseDescendant.base_class
1364
+ assert_equal TightPerson, TightPerson.base_class
1365
+ assert_equal TightPerson, TightDescendant.base_class
1366
+
1367
+ assert_equal Post, Post.base_class
1368
+ assert_equal Post, SpecialPost.base_class
1369
+ assert_equal Post, StiPost.base_class
1370
+ assert_equal SubStiPost, SubStiPost.base_class
1371
+ end
1372
+
1373
+ def test_descends_from_active_record
1374
+ # Tries to call Object.abstract_class?
1375
+ assert_raise(NoMethodError) do
1376
+ ActiveRecord::Base.descends_from_active_record?
1377
+ end
1378
+
1379
+ # Abstract subclass of AR::Base.
1380
+ assert LoosePerson.descends_from_active_record?
1381
+
1382
+ # Concrete subclass of an abstract class.
1383
+ assert LooseDescendant.descends_from_active_record?
1384
+
1385
+ # Concrete subclass of AR::Base.
1386
+ assert TightPerson.descends_from_active_record?
1387
+
1388
+ # Concrete subclass of a concrete class but has no type column.
1389
+ assert TightDescendant.descends_from_active_record?
1390
+
1391
+ # Concrete subclass of AR::Base.
1392
+ assert Post.descends_from_active_record?
1393
+
1394
+ # Abstract subclass of a concrete class which has a type column.
1395
+ # This is pathological, as you'll never have Sub < Abstract < Concrete.
1396
+ assert !StiPost.descends_from_active_record?
1397
+
1398
+ # Concrete subclasses an abstract class which has a type column.
1399
+ assert !SubStiPost.descends_from_active_record?
1400
+ end
1401
+
1402
+ def test_find_on_abstract_base_class_doesnt_use_type_condition
1403
+ old_class = LooseDescendant
1404
+
1405
+ Object.send :remove_const, :LooseDescendant
1406
+ descendant = old_class.create!
1407
+
1408
+ assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1409
+ ensure
1410
+ unless Object.const_defined?(:LooseDescendant)
1411
+ Object.const_set :LooseDescendant, old_class
1412
+ end
1413
+ end
1414
+
1415
+ def test_assert_queries
1416
+ query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1417
+ assert_queries(2) { 2.times { query.call } }
1418
+ assert_queries 1, &query
1419
+ assert_no_queries { assert true }
1420
+ end
1421
+
1422
+ def test_to_xml
1423
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true)
1424
+ bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
1425
+ written_on_in_current_timezone = topics(:first).written_on.xmlschema
1426
+ last_read_in_current_timezone = topics(:first).last_read.xmlschema
1427
+ assert_equal "<topic>", xml.first(7)
1428
+ assert xml.include?(%(<title>The First Topic</title>))
1429
+ assert xml.include?(%(<author-name>David</author-name>))
1430
+ assert xml.include?(%(<id type="integer">1</id>))
1431
+ assert xml.include?(%(<replies-count type="integer">1</replies-count>))
1432
+ unless current_adapter?(:IBM_DBAdapter)
1433
+ assert xml.include?(%(<written-on type="datetime">#{written_on_in_current_timezone}</written-on>))
1434
+ end
1435
+ assert xml.include?(%(<content>Have a nice day</content>))
1436
+ assert xml.include?(%(<author-email-address>david@loudthinking.com</author-email-address>))
1437
+ assert xml.match(%(<parent-id type="integer"></parent-id>))
1438
+ if current_adapter?(:SybaseAdapter, :SQLServerAdapter, :OracleAdapter)
1439
+ assert xml.include?(%(<last-read type="datetime">#{last_read_in_current_timezone}</last-read>))
1440
+ else
1441
+ assert xml.include?(%(<last-read type="date">2004-04-15</last-read>))
1442
+ end
1443
+ # Oracle and DB2 don't have true boolean or time-only fields
1444
+ unless current_adapter?(:OracleAdapter, :DB2Adapter, :IBM_DBAdapter)
1445
+ assert xml.include?(%(<approved type="boolean">false</approved>)), "Approved should be a boolean"
1446
+ assert xml.include?(%(<bonus-time type="datetime">#{bonus_time_in_current_timezone}</bonus-time>))
1447
+ end
1448
+ end
1449
+
1450
+ def test_to_xml_skipping_attributes
1451
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
1452
+ assert_equal "<topic>", xml.first(7)
1453
+ assert !xml.include?(%(<title>The First Topic</title>))
1454
+ assert xml.include?(%(<author-name>David</author-name>))
1455
+
1456
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
1457
+ assert !xml.include?(%(<title>The First Topic</title>))
1458
+ assert !xml.include?(%(<author-name>David</author-name>))
1459
+ end
1460
+
1461
+ def test_to_xml_including_has_many_association
1462
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
1463
+ assert_equal "<topic>", xml.first(7)
1464
+ assert xml.include?(%(<replies><reply>))
1465
+ assert xml.include?(%(<title>The Second Topic's of the day</title>))
1466
+ end
1467
+
1468
+ def test_array_to_xml_including_has_many_association
1469
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
1470
+ assert xml.include?(%(<replies><reply>))
1471
+ end
1472
+
1473
+ def test_array_to_xml_including_methods
1474
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
1475
+ assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
1476
+ assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
1477
+ end
1478
+
1479
+ def test_array_to_xml_including_has_one_association
1480
+ xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
1481
+ assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
1482
+ assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
1483
+ end
1484
+
1485
+ def test_array_to_xml_including_belongs_to_association
1486
+ xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1487
+ assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
1488
+ assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
1489
+ assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
1490
+ end
1491
+
1492
+ def test_to_xml_including_belongs_to_association
1493
+ xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1494
+ assert !xml.include?("<firm>")
1495
+
1496
+ xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1497
+ assert xml.include?("<firm>")
1498
+ end
1499
+
1500
+ def test_to_xml_including_multiple_associations
1501
+ xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
1502
+ assert_equal "<firm>", xml.first(6)
1503
+ assert xml.include?(%(<account>))
1504
+ assert xml.include?(%(<clients><client>))
1505
+ end
1506
+
1507
+ def test_to_xml_including_multiple_associations_with_options
1508
+ xml = companies(:first_firm).to_xml(
1509
+ :indent => 0, :skip_instruct => true,
1510
+ :include => { :clients => { :only => :name } }
1511
+ )
1512
+
1513
+ assert_equal "<firm>", xml.first(6)
1514
+ assert xml.include?(%(<client><name>Summit</name></client>))
1515
+ assert xml.include?(%(<clients><client>))
1516
+ end
1517
+
1518
+ def test_to_xml_including_methods
1519
+ xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
1520
+ assert_equal "<company>", xml.first(9)
1521
+ assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
1522
+ end
1523
+
1524
+ def test_except_attributes
1525
+ assert_equal(
1526
+ %w( author_name type id approved replies_count bonus_time written_on content author_email_address parent_id last_read),
1527
+ topics(:first).attributes(:except => :title).keys
1528
+ )
1529
+
1530
+ assert_equal(
1531
+ %w( replies_count bonus_time written_on content author_email_address parent_id last_read),
1532
+ topics(:first).attributes(:except => [ :title, :id, :type, :approved, :author_name ]).keys
1533
+ )
1534
+ end
1535
+
1536
+ def test_include_attributes
1537
+ assert_equal(%w( title ), topics(:first).attributes(:only => :title).keys)
1538
+ assert_equal(%w( title author_name type id approved ), topics(:first).attributes(:only => [ :title, :id, :type, :approved, :author_name ]).keys)
1539
+ end
1540
+
1541
+ def test_type_name_with_module_should_handle_beginning
1542
+ assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
1543
+ assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
1544
+ end
1545
+
1546
+ def test_to_param_should_return_string
1547
+ assert_kind_of String, Client.find(:first).to_param
1548
+ end
1549
+
1550
+ # FIXME: this test ought to run, but it needs to run sandboxed so that it
1551
+ # doesn't b0rk the current test environment by undefing everything.
1552
+ #
1553
+ #def test_dev_mode_memory_leak
1554
+ # counts = []
1555
+ # 2.times do
1556
+ # require_dependency 'fixtures/company'
1557
+ # Firm.find(:first)
1558
+ # Dependencies.clear
1559
+ # ActiveRecord::Base.reset_subclasses
1560
+ # Dependencies.remove_subclasses_for(ActiveRecord::Base)
1561
+ #
1562
+ # GC.start
1563
+ #
1564
+ # count = 0
1565
+ # ObjectSpace.each_object(Proc) { count += 1 }
1566
+ # counts << count
1567
+ # end
1568
+ # assert counts.last <= counts.first,
1569
+ # "expected last count (#{counts.last}) to be <= first count (#{counts.first})"
1570
+ #end
1571
+
1572
+ private
1573
+ def assert_readers(model, exceptions)
1574
+ expected_readers = Set.new(model.column_names - ['id'])
1575
+ expected_readers += expected_readers.map { |col| "#{col}?" }
1576
+ expected_readers -= exceptions
1577
+ assert_equal expected_readers, model.read_methods
1578
+ end
1579
+ end