base_test.rb 63.6 KB
Newer Older
1
require "cases/helper"
J
Jeremy Kemper 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15
require 'models/topic'
require 'models/reply'
require 'models/company'
require 'models/customer'
require 'models/developer'
require 'models/project'
require 'models/default'
require 'models/auto_id'
require 'models/column_name'
require 'models/subscriber'
require 'models/keyboard'
require 'models/post'
require 'models/minimalistic'
require 'models/warehouse_thing'
16
require 'rexml/document'
D
Initial  
David Heinemeier Hansson 已提交
17 18 19

class Category < ActiveRecord::Base; end
class Smarts < ActiveRecord::Base; end
20
class CreditCard < ActiveRecord::Base
21 22 23 24 25 26
  class PinNumber < ActiveRecord::Base
    class CvvCode < ActiveRecord::Base; end
    class SubCvvCode < CvvCode; end
  end
  class SubPinNumber < PinNumber; end
  class Brand < Category; end
27
end
D
Initial  
David Heinemeier Hansson 已提交
28
class MasterCreditCard < ActiveRecord::Base; end
29
class Post < ActiveRecord::Base; end
30
class Computer < ActiveRecord::Base; end
31
class NonExistentTable < ActiveRecord::Base; end
32
class TestOracleDefault < ActiveRecord::Base; end
D
Initial  
David Heinemeier Hansson 已提交
33 34

class LoosePerson < ActiveRecord::Base
35
  self.table_name = 'people'
36
  self.abstract_class = true
37
  attr_protected :credit_rating, :administrator
D
Initial  
David Heinemeier Hansson 已提交
38 39
end

40 41 42 43
class LooseDescendant < LoosePerson
  attr_protected :phone_number
end

44 45 46 47 48
class LooseDescendantSecond< LoosePerson
  attr_protected :phone_number
  attr_protected :name
end

D
Initial  
David Heinemeier Hansson 已提交
49
class TightPerson < ActiveRecord::Base
50
  self.table_name = 'people'
D
Initial  
David Heinemeier Hansson 已提交
51 52 53
  attr_accessible :name, :address
end

54
class TightDescendant < TightPerson
D
Initial  
David Heinemeier Hansson 已提交
55 56 57
  attr_accessible :phone_number
end

58 59 60 61
class ReadonlyTitlePost < Post
  attr_readonly :title
end

D
Initial  
David Heinemeier Hansson 已提交
62 63
class Booleantest < ActiveRecord::Base; end

64 65 66 67
class Task < ActiveRecord::Base
  attr_protected :starting
end

J
Jeremy Kemper 已提交
68 69
class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
  self.table_name = 'topics'
70 71 72 73
  attr_accessible :author_name
  attr_protected  :content
end

74
class BasicsTest < ActiveRecord::TestCase
75
  fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things'
D
Initial  
David Heinemeier Hansson 已提交
76

77 78 79 80
  def test_table_exists
    assert !NonExistentTable.table_exists?
    assert Topic.table_exists?
  end
J
Jeremy Kemper 已提交
81

D
Initial  
David Heinemeier Hansson 已提交
82 83 84 85 86 87
  def test_set_attributes
    topic = Topic.find(1)
    topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
    topic.save
    assert_equal("Budget", topic.title)
    assert_equal("Jason", topic.author_name)
88
    assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
D
Initial  
David Heinemeier Hansson 已提交
89
  end
90

D
Initial  
David Heinemeier Hansson 已提交
91
  def test_integers_as_nil
J
Jeremy Kemper 已提交
92 93
    test = AutoId.create('value' => '')
    assert_nil AutoId.find(test.id).value
D
Initial  
David Heinemeier Hansson 已提交
94
  end
J
Jeremy Kemper 已提交
95

D
Initial  
David Heinemeier Hansson 已提交
96 97 98 99 100 101 102 103 104
  def test_set_attributes_with_block
    topic = Topic.new do |t|
      t.title       = "Budget"
      t.author_name = "Jason"
    end

    assert_equal("Budget", topic.title)
    assert_equal("Jason", topic.author_name)
  end
J
Jeremy Kemper 已提交
105

D
Initial  
David Heinemeier Hansson 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118
  def test_respond_to?
    topic = Topic.find(1)
    assert topic.respond_to?("title")
    assert topic.respond_to?("title?")
    assert topic.respond_to?("title=")
    assert topic.respond_to?(:title)
    assert topic.respond_to?(:title?)
    assert topic.respond_to?(:title=)
    assert topic.respond_to?("author_name")
    assert topic.respond_to?("attribute_names")
    assert !topic.respond_to?("nothingness")
    assert !topic.respond_to?(:nothingness)
  end
J
Jeremy Kemper 已提交
119

D
Initial  
David Heinemeier Hansson 已提交
120 121 122 123 124 125 126 127 128 129 130 131 132 133
  def test_array_content
    topic = Topic.new
    topic.content = %w( one two three )
    topic.save

    assert_equal(%w( one two three ), Topic.find(topic.id).content)
  end

  def test_hash_content
    topic = Topic.new
    topic.content = { "one" => 1, "two" => 2 }
    topic.save

    assert_equal 2, Topic.find(topic.id).content["two"]
J
Jeremy Kemper 已提交
134

D
Initial  
David Heinemeier Hansson 已提交
135 136 137 138 139
    topic.content["three"] = 3
    topic.save

    assert_equal 3, Topic.find(topic.id).content["three"]
  end
J
Jeremy Kemper 已提交
140

D
Initial  
David Heinemeier Hansson 已提交
141 142 143 144 145 146 147 148
  def test_update_array_content
    topic = Topic.new
    topic.content = %w( one two three )

    topic.content.push "four"
    assert_equal(%w( one two three four ), topic.content)

    topic.save
J
Jeremy Kemper 已提交
149

D
Initial  
David Heinemeier Hansson 已提交
150 151 152 153
    topic = Topic.find(topic.id)
    topic.content << "five"
    assert_equal(%w( one two three four five ), topic.content)
  end
J
Jeremy Kemper 已提交
154

155 156 157 158
  def test_case_sensitive_attributes_hash
    # DB2 is not case-sensitive
    return true if current_adapter?(:DB2Adapter)

159
    assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
160
  end
161

D
Initial  
David Heinemeier Hansson 已提交
162 163 164 165
  def test_create
    topic = Topic.new
    topic.title = "New Topic"
    topic.save
166 167 168
    topic_reloaded = Topic.find(topic.id)
    assert_equal("New Topic", topic_reloaded.title)
  end
J
Jeremy Kemper 已提交
169

170 171 172
  def test_save!
    topic = Topic.new(:title => "New Topic")
    assert topic.save!
J
Jeremy Kemper 已提交
173

174 175 176
    reply = Reply.new
    assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
  end
177 178 179 180 181 182 183 184

  def test_save_null_string_attributes
    topic = Topic.find(1)
    topic.attributes = { "title" => "null", "author_name" => "null" }
    topic.save!
    topic.reload
    assert_equal("null", topic.title)
    assert_equal("null", topic.author_name)
185 186 187 188 189 190 191 192 193 194
  end

  def test_save_nil_string_attributes
    topic = Topic.find(1)
    topic.title = nil
    topic.save!
    topic.reload
    assert_nil topic.title
  end

195 196 197 198 199 200 201 202 203
  def test_save_for_record_with_only_primary_key
    minimalistic = Minimalistic.new
    assert_nothing_raised { minimalistic.save }
  end

  def test_save_for_record_with_only_primary_key_that_is_provided
    assert_nothing_raised { Minimalistic.create!(:id => 2) }
  end

204 205 206 207 208 209 210 211
  def test_hashes_not_mangled
    new_topic = { :title => "New Topic" }
    new_topic_values = { :title => "AnotherTopic" }

    topic = Topic.new(new_topic)
    assert_equal new_topic[:title], topic.title

    topic.attributes= new_topic_values
212
    assert_equal new_topic_values[:title], topic.title
D
Initial  
David Heinemeier Hansson 已提交
213
  end
J
Jeremy Kemper 已提交
214

215 216 217 218 219
  def test_create_many
    topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
    assert_equal 2, topics.size
    assert_equal "first", topics.first.title
  end
220 221 222 223 224 225 226 227

  def test_create_columns_not_equal_attributes
    topic = Topic.new
    topic.title = 'Another New Topic'
    topic.send :write_attribute, 'does_not_exist', 'test'
    assert_nothing_raised { topic.save }
  end

D
Initial  
David Heinemeier Hansson 已提交
228 229 230 231 232 233 234 235 236
  def test_create_through_factory
    topic = Topic.create("title" => "New Topic")
    topicReloaded = Topic.find(topic.id)
    assert_equal(topic, topicReloaded)
  end

  def test_update
    topic = Topic.new
    topic.title = "Another New Topic"
237
    topic.written_on = "2003-12-12 23:23:00"
D
Initial  
David Heinemeier Hansson 已提交
238
    topic.save
239
    topicReloaded = Topic.find(topic.id)
D
Initial  
David Heinemeier Hansson 已提交
240 241 242 243
    assert_equal("Another New Topic", topicReloaded.title)

    topicReloaded.title = "Updated topic"
    topicReloaded.save
J
Jeremy Kemper 已提交
244

245
    topicReloadedAgain = Topic.find(topic.id)
J
Jeremy Kemper 已提交
246

D
Initial  
David Heinemeier Hansson 已提交
247 248 249
    assert_equal("Updated topic", topicReloadedAgain.title)
  end

250 251 252 253
  def test_update_columns_not_equal_attributes
    topic = Topic.new
    topic.title = "Still another topic"
    topic.save
J
Jeremy Kemper 已提交
254

255
    topicReloaded = Topic.find(topic.id)
256 257 258 259
    topicReloaded.title = "A New Topic"
    topicReloaded.send :write_attribute, 'does_not_exist', 'test'
    assert_nothing_raised { topicReloaded.save }
  end
260 261 262 263 264

  def test_update_for_record_with_only_primary_key
    minimalistic = minimalistics(:first)
    assert_nothing_raised { minimalistic.save }
  end
J
Jeremy Kemper 已提交
265

266 267 268 269 270 271 272 273
  def test_write_attribute
    topic = Topic.new
    topic.send(:write_attribute, :title, "Still another topic")
    assert_equal "Still another topic", topic.title

    topic.send(:write_attribute, "title", "Still another topic: part 2")
    assert_equal "Still another topic: part 2", topic.title
  end
274

275 276 277 278 279 280 281 282 283 284
  def test_read_attribute
    topic = Topic.new
    topic.title = "Don't change the topic"
    assert_equal "Don't change the topic", topic.send(:read_attribute, "title")
    assert_equal "Don't change the topic", topic["title"]

    assert_equal "Don't change the topic", topic.send(:read_attribute, :title)
    assert_equal "Don't change the topic", topic[:title]
  end

285 286 287
  def test_read_attribute_when_false
    topic = topics(:first)
    topic.approved = false
J
Jeremy Kemper 已提交
288
    assert !topic.approved?, "approved should be false"
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    topic.approved = "false"
    assert !topic.approved?, "approved should be false"
  end

  def test_read_attribute_when_true
    topic = topics(:first)
    topic.approved = true
    assert topic.approved?, "approved should be true"
    topic.approved = "true"
    assert topic.approved?, "approved should be true"
  end

  def test_read_write_boolean_attribute
    topic = Topic.new
    # puts ""
    # puts "New Topic"
    # puts topic.inspect
    topic.approved = "false"
    # puts "Expecting false"
    # puts topic.inspect
309
    assert !topic.approved?, "approved should be false"
310 311 312
    topic.approved = "false"
    # puts "Expecting false"
    # puts topic.inspect
313
    assert !topic.approved?, "approved should be false"
314 315 316
    topic.approved = "true"
    # puts "Expecting true"
    # puts topic.inspect
317
    assert topic.approved?, "approved should be true"
318 319 320
    topic.approved = "true"
    # puts "Expecting true"
    # puts topic.inspect
321
    assert topic.approved?, "approved should be true"
322
    # puts ""
323
  end
J
Jeremy Kemper 已提交
324

325 326 327 328
  def test_query_attribute_string
    [nil, "", " "].each do |value|
      assert_equal false, Topic.new(:author_name => value).author_name?
    end
J
Jeremy Kemper 已提交
329

330 331
    assert_equal true, Topic.new(:author_name => "Name").author_name?
  end
J
Jeremy Kemper 已提交
332

333 334 335 336
  def test_query_attribute_number
    [nil, 0, "0"].each do |value|
      assert_equal false, Developer.new(:salary => value).salary?
    end
J
Jeremy Kemper 已提交
337

338 339 340
    assert_equal true, Developer.new(:salary => 1).salary?
    assert_equal true, Developer.new(:salary => "1").salary?
  end
J
Jeremy Kemper 已提交
341

342 343 344 345
  def test_query_attribute_boolean
    [nil, "", false, "false", "f", 0].each do |value|
      assert_equal false, Topic.new(:approved => value).approved?
    end
J
Jeremy Kemper 已提交
346

347 348 349 350
    [true, "true", "1", 1].each do |value|
      assert_equal true, Topic.new(:approved => value).approved?
    end
  end
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365

  def test_query_attribute_with_custom_fields
    object = Company.find_by_sql(<<-SQL).first
      SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value
        FROM companies c1, companies c2
       WHERE c1.firm_id = c2.id
         AND c1.id = 2
    SQL

    assert_equal "Firm", object.string_value
    assert object.string_value?

    object.string_value = "  "
    assert !object.string_value?

J
Jamis Buck 已提交
366
    assert_equal 1, object.int_value.to_i
367 368 369 370 371 372
    assert object.int_value?

    object.int_value = "0"
    assert !object.int_value?
  end

373

374
  def test_reader_for_invalid_column_names
375 376
    Topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
    assert !Topic.generated_methods.include?("mumub-jumbo")
377 378
  end

379 380 381 382 383 384 385
  def test_non_attribute_access_and_assignment
    topic = Topic.new
    assert !topic.respond_to?("mumbo")
    assert_raises(NoMethodError) { topic.mumbo }
    assert_raises(NoMethodError) { topic.mumbo = 5 }
  end

D
Initial  
David Heinemeier Hansson 已提交
386 387
  def test_preserving_date_objects
    # SQL Server doesn't have a separate column type just for dates, so all are returned as time
388
    return true if current_adapter?(:SQLServerAdapter)
D
Initial  
David Heinemeier Hansson 已提交
389

390
    if current_adapter?(:SybaseAdapter, :OracleAdapter)
391
      # Sybase ctlib does not (yet?) support the date type; use datetime instead.
392
      # Oracle treats all dates/times as Time.
393
      assert_kind_of(
J
Jeremy Kemper 已提交
394
        Time, Topic.find(1).last_read,
395 396 397 398
        "The last_read attribute should be of the Time class"
      )
    else
      assert_kind_of(
J
Jeremy Kemper 已提交
399
        Date, Topic.find(1).last_read,
400 401 402
        "The last_read attribute should be of the Date class"
      )
    end
403
  end
404

405
  def test_preserving_time_objects
406 407 408 409
    assert_kind_of(
      Time, Topic.find(1).bonus_time,
      "The bonus_time attribute should be of the Time class"
    )
D
Initial  
David Heinemeier Hansson 已提交
410 411 412 413 414

    assert_kind_of(
      Time, Topic.find(1).written_on,
      "The written_on attribute should be of the Time class"
    )
415 416 417 418 419

    # For adapters which support microsecond resolution.
    if current_adapter?(:PostgreSQLAdapter)
      assert_equal 11, Topic.find(1).written_on.sec
      assert_equal 223300, Topic.find(1).written_on.usec
420
      assert_equal 9900, Topic.find(2).written_on.usec
421
    end
D
Initial  
David Heinemeier Hansson 已提交
422
  end
J
Jeremy Kemper 已提交
423

424 425 426 427 428 429
  def test_custom_mutator
    topic = Topic.find(1)
    # This mutator is protected in the class definition
    topic.send(:approved=, true)
    assert topic.instance_variable_get("@custom_approved")
  end
J
Jeremy Kemper 已提交
430

D
Initial  
David Heinemeier Hansson 已提交
431
  def test_destroy
J
Jeremy Kemper 已提交
432 433 434
    topic = Topic.find(1)
    assert_equal topic, topic.destroy, 'topic.destroy did not return self'
    assert topic.frozen?, 'topic not frozen after destroy'
435
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
D
Initial  
David Heinemeier Hansson 已提交
436
  end
J
Jeremy Kemper 已提交
437

D
Initial  
David Heinemeier Hansson 已提交
438
  def test_record_not_found_exception
439
    assert_raises(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
D
Initial  
David Heinemeier Hansson 已提交
440
  end
J
Jeremy Kemper 已提交
441

D
Initial  
David Heinemeier Hansson 已提交
442
  def test_initialize_with_attributes
J
Jeremy Kemper 已提交
443
    topic = Topic.new({
D
Initial  
David Heinemeier Hansson 已提交
444 445
      "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
    })
J
Jeremy Kemper 已提交
446

D
Initial  
David Heinemeier Hansson 已提交
447 448
    assert_equal("initialized from attributes", topic.title)
  end
J
Jeremy Kemper 已提交
449

450 451
  def test_initialize_with_invalid_attribute
    begin
J
Jeremy Kemper 已提交
452
      topic = Topic.new({ "title" => "test",
453 454 455 456 457 458
        "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
    rescue ActiveRecord::MultiparameterAssignmentErrors => ex
      assert_equal(1, ex.errors.size)
      assert_equal("last_read", ex.errors[0].attribute)
    end
  end
J
Jeremy Kemper 已提交
459

D
Initial  
David Heinemeier Hansson 已提交
460
  def test_load
J
Jeremy Kemper 已提交
461
    topics = Topic.find(:all, :order => 'id')
D
Initial  
David Heinemeier Hansson 已提交
462
    assert_equal(2, topics.size)
463
    assert_equal(topics(:first).title, topics.first.title)
D
Initial  
David Heinemeier Hansson 已提交
464
  end
J
Jeremy Kemper 已提交
465

D
Initial  
David Heinemeier Hansson 已提交
466
  def test_load_with_condition
467
    topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
J
Jeremy Kemper 已提交
468

D
Initial  
David Heinemeier Hansson 已提交
469
    assert_equal(1, topics.size)
470
    assert_equal(topics(:second).title, topics.first.title)
D
Initial  
David Heinemeier Hansson 已提交
471 472 473
  end

  def test_table_name_guesses
474 475
    classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]

D
Initial  
David Heinemeier Hansson 已提交
476
    assert_equal "topics", Topic.table_name
477

D
Initial  
David Heinemeier Hansson 已提交
478 479 480
    assert_equal "categories", Category.table_name
    assert_equal "smarts", Smarts.table_name
    assert_equal "credit_cards", CreditCard.table_name
481
    assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
482 483 484
    assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
    assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
    assert_equal "categories", CreditCard::Brand.table_name
D
Initial  
David Heinemeier Hansson 已提交
485 486 487
    assert_equal "master_credit_cards", MasterCreditCard.table_name

    ActiveRecord::Base.pluralize_table_names = false
488 489
    classes.each(&:reset_table_name)

D
Initial  
David Heinemeier Hansson 已提交
490 491 492
    assert_equal "category", Category.table_name
    assert_equal "smarts", Smarts.table_name
    assert_equal "credit_card", CreditCard.table_name
493
    assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
494 495 496
    assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
    assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
    assert_equal "category", CreditCard::Brand.table_name
D
Initial  
David Heinemeier Hansson 已提交
497
    assert_equal "master_credit_card", MasterCreditCard.table_name
498

D
Initial  
David Heinemeier Hansson 已提交
499
    ActiveRecord::Base.pluralize_table_names = true
500
    classes.each(&:reset_table_name)
D
Initial  
David Heinemeier Hansson 已提交
501 502

    ActiveRecord::Base.table_name_prefix = "test_"
503
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
504 505
    assert_equal "test_categories", Category.table_name
    ActiveRecord::Base.table_name_suffix = "_test"
506
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
507 508
    assert_equal "test_categories_test", Category.table_name
    ActiveRecord::Base.table_name_prefix = ""
509
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
510 511
    assert_equal "categories_test", Category.table_name
    ActiveRecord::Base.table_name_suffix = ""
512
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
513 514 515 516
    assert_equal "categories", Category.table_name

    ActiveRecord::Base.pluralize_table_names = false
    ActiveRecord::Base.table_name_prefix = "test_"
517
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
518 519
    assert_equal "test_category", Category.table_name
    ActiveRecord::Base.table_name_suffix = "_test"
520
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
521 522
    assert_equal "test_category_test", Category.table_name
    ActiveRecord::Base.table_name_prefix = ""
523
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
524 525
    assert_equal "category_test", Category.table_name
    ActiveRecord::Base.table_name_suffix = ""
526
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
527
    assert_equal "category", Category.table_name
528

D
Initial  
David Heinemeier Hansson 已提交
529
    ActiveRecord::Base.pluralize_table_names = true
530
    classes.each(&:reset_table_name)
D
Initial  
David Heinemeier Hansson 已提交
531
  end
J
Jeremy Kemper 已提交
532

D
Initial  
David Heinemeier Hansson 已提交
533
  def test_destroy_all
534
    assert_equal 2, Topic.count
D
Initial  
David Heinemeier Hansson 已提交
535 536

    Topic.destroy_all "author_name = 'Mary'"
537
    assert_equal 1, Topic.count
D
Initial  
David Heinemeier Hansson 已提交
538
  end
539 540

  def test_destroy_many
541
    assert_equal 3, Client.count
542
    Client.destroy([2, 3])
543
    assert_equal 1, Client.count
544 545 546 547 548 549 550
  end

  def test_delete_many
    Topic.delete([1, 2])
    assert_equal 0, Topic.count
  end

D
Initial  
David Heinemeier Hansson 已提交
551 552 553 554
  def test_boolean_attributes
    assert ! Topic.find(1).approved?
    assert Topic.find(2).approved?
  end
J
Jeremy Kemper 已提交
555

D
Initial  
David Heinemeier Hansson 已提交
556 557
  def test_increment_counter
    Topic.increment_counter("replies_count", 1)
558
    assert_equal 2, Topic.find(1).replies_count
D
Initial  
David Heinemeier Hansson 已提交
559 560

    Topic.increment_counter("replies_count", 1)
561
    assert_equal 3, Topic.find(1).replies_count
D
Initial  
David Heinemeier Hansson 已提交
562
  end
J
Jeremy Kemper 已提交
563

D
Initial  
David Heinemeier Hansson 已提交
564 565
  def test_decrement_counter
    Topic.decrement_counter("replies_count", 2)
566
    assert_equal -1, Topic.find(2).replies_count
D
Initial  
David Heinemeier Hansson 已提交
567 568

    Topic.decrement_counter("replies_count", 2)
569
    assert_equal -2, Topic.find(2).replies_count
D
Initial  
David Heinemeier Hansson 已提交
570
  end
571

572 573 574 575
  def test_update_all
    assert_equal 2, Topic.update_all("content = 'bulk updated!'")
    assert_equal "bulk updated!", Topic.find(1).content
    assert_equal "bulk updated!", Topic.find(2).content
576

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
    assert_equal 2, Topic.update_all(['content = ?', 'bulk updated again!'])
    assert_equal "bulk updated again!", Topic.find(1).content
    assert_equal "bulk updated again!", Topic.find(2).content

    assert_equal 2, Topic.update_all(['content = ?', nil])
    assert_nil Topic.find(1).content
  end

  def test_update_all_with_hash
    assert_not_nil Topic.find(1).last_read
    assert_equal 2, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
    assert_equal "bulk updated with hash!", Topic.find(1).content
    assert_equal "bulk updated with hash!", Topic.find(2).content
    assert_nil Topic.find(1).last_read
    assert_nil Topic.find(2).last_read
D
Initial  
David Heinemeier Hansson 已提交
592
  end
593

594 595 596 597 598
  def test_update_all_with_non_standard_table_name
    assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1])
    assert_equal 0, WarehouseThing.find(1).value
  end

599 600 601 602 603 604
  if current_adapter?(:MysqlAdapter)
    def test_update_all_with_order_and_limit
      assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
    end
  end

605 606 607 608 609 610 611
  def test_update_all_ignores_order_limit_from_association
    author = Author.find(1)
    assert_nothing_raised do
      assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all("body = 'bulk update!'")
    end
  end

612
  def test_update_many
613
    topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
614
    updated = Topic.update(topic_data.keys, topic_data.values)
615 616 617 618 619 620

    assert_equal 2, updated.size
    assert_equal "1 updated", Topic.find(1).content
    assert_equal "2 updated", Topic.find(2).content
  end

621 622 623 624
  def test_delete_all
    assert_equal 2, Topic.delete_all
  end

D
Initial  
David Heinemeier Hansson 已提交
625
  def test_update_by_condition
626
    Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
D
Initial  
David Heinemeier Hansson 已提交
627 628 629
    assert_equal "Have a nice day", Topic.find(1).content
    assert_equal "bulk updated!", Topic.find(2).content
  end
J
Jeremy Kemper 已提交
630

D
Initial  
David Heinemeier Hansson 已提交
631 632 633 634 635 636 637 638
  def test_attribute_present
    t = Topic.new
    t.title = "hello there!"
    t.written_on = Time.now
    assert t.attribute_present?("title")
    assert t.attribute_present?("written_on")
    assert !t.attribute_present?("content")
  end
J
Jeremy Kemper 已提交
639

D
Initial  
David Heinemeier Hansson 已提交
640 641 642
  def test_attribute_keys_on_new_instance
    t = Topic.new
    assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
643
    assert_raise(NoMethodError) { t.title2 }
D
Initial  
David Heinemeier Hansson 已提交
644
  end
J
Jeremy Kemper 已提交
645

D
Initial  
David Heinemeier Hansson 已提交
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
  def test_class_name
    assert_equal "Firm", ActiveRecord::Base.class_name("firms")
    assert_equal "Category", ActiveRecord::Base.class_name("categories")
    assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder")

    ActiveRecord::Base.pluralize_table_names = false
    assert_equal "Firms", ActiveRecord::Base.class_name( "firms" )
    ActiveRecord::Base.pluralize_table_names = true

    ActiveRecord::Base.table_name_prefix = "test_"
    assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" )
    ActiveRecord::Base.table_name_suffix = "_tests"
    assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" )
    ActiveRecord::Base.table_name_prefix = ""
    assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" )
    ActiveRecord::Base.table_name_suffix = ""
    assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
  end
J
Jeremy Kemper 已提交
664

D
Initial  
David Heinemeier Hansson 已提交
665 666 667 668
  def test_null_fields
    assert_nil Topic.find(1).parent_id
    assert_nil Topic.create("title" => "Hey you").parent_id
  end
J
Jeremy Kemper 已提交
669

D
Initial  
David Heinemeier Hansson 已提交
670 671
  def test_default_values
    topic = Topic.new
J
Jeremy Kemper 已提交
672
    assert topic.approved?
D
Initial  
David Heinemeier Hansson 已提交
673
    assert_nil topic.written_on
674
    assert_nil topic.bonus_time
D
Initial  
David Heinemeier Hansson 已提交
675
    assert_nil topic.last_read
J
Jeremy Kemper 已提交
676

D
Initial  
David Heinemeier Hansson 已提交
677 678 679
    topic.save

    topic = Topic.find(topic.id)
J
Jeremy Kemper 已提交
680
    assert topic.approved?
D
Initial  
David Heinemeier Hansson 已提交
681
    assert_nil topic.last_read
682

J
Jeremy Kemper 已提交
683
    # Oracle has some funky default handling, so it requires a bit of
684
    # extra testing. See ticket #2788.
685 686
    if current_adapter?(:OracleAdapter)
      test = TestOracleDefault.new
687 688 689 690
      assert_equal "X", test.test_char
      assert_equal "hello", test.test_string
      assert_equal 3, test.test_int
    end
D
Initial  
David Heinemeier Hansson 已提交
691
  end
692

693 694
  # Oracle, SQLServer, and Sybase do not have a TIME datatype.
  unless current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
695 696 697 698 699 700 701 702
    def test_utc_as_time_zone
      Topic.default_timezone = :utc
      attributes = { "bonus_time" => "5:42:00AM" }
      topic = Topic.find(1)
      topic.attributes = attributes
      assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
      Topic.default_timezone = :local
    end
703

704 705 706 707 708 709 710 711 712 713 714 715
    def test_utc_as_time_zone_and_new
      Topic.default_timezone = :utc
      attributes = { "bonus_time(1i)"=>"2000",
                     "bonus_time(2i)"=>"1",
                     "bonus_time(3i)"=>"1",
                     "bonus_time(4i)"=>"10",
                     "bonus_time(5i)"=>"35",
                     "bonus_time(6i)"=>"50" }
      topic = Topic.new(attributes)
      assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
      Topic.default_timezone = :local
    end
716 717
  end

D
Initial  
David Heinemeier Hansson 已提交
718 719 720 721 722 723 724 725 726
  def test_default_values_on_empty_strings
    topic = Topic.new
    topic.approved  = nil
    topic.last_read = nil

    topic.save

    topic = Topic.find(topic.id)
    assert_nil topic.last_read
727 728 729 730 731 732 733

    # Sybase adapter does not allow nulls in boolean columns
    if current_adapter?(:SybaseAdapter)
      assert topic.approved == false
    else
      assert_nil topic.approved
    end
D
Initial  
David Heinemeier Hansson 已提交
734
  end
735

D
Initial  
David Heinemeier Hansson 已提交
736
  def test_equality
737
    assert_equal Topic.find(1), Topic.find(2).topic
D
Initial  
David Heinemeier Hansson 已提交
738
  end
J
Jeremy Kemper 已提交
739

740 741 742
  def test_equality_of_new_records
    assert_not_equal Topic.new, Topic.new
  end
J
Jeremy Kemper 已提交
743

D
Initial  
David Heinemeier Hansson 已提交
744
  def test_hashing
745
    assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
D
Initial  
David Heinemeier Hansson 已提交
746
  end
J
Jeremy Kemper 已提交
747

D
Initial  
David Heinemeier Hansson 已提交
748 749 750 751 752
  def test_destroy_new_record
    client = Client.new
    client.destroy
    assert client.frozen?
  end
J
Jeremy Kemper 已提交
753

754 755 756 757 758
  def test_destroy_record_with_associations
    client = Client.find(3)
    client.destroy
    assert client.frozen?
    assert_kind_of Firm, client.firm
759
    assert_raises(ActiveSupport::FrozenObjectError) { client.name = "something else" }
760
  end
J
Jeremy Kemper 已提交
761

D
Initial  
David Heinemeier Hansson 已提交
762 763 764 765
  def test_update_attribute
    assert !Topic.find(1).approved?
    Topic.find(1).update_attribute("approved", true)
    assert Topic.find(1).approved?
766 767 768

    Topic.find(1).update_attribute(:approved, false)
    assert !Topic.find(1).approved?
D
Initial  
David Heinemeier Hansson 已提交
769
  end
J
Jeremy Kemper 已提交
770

771 772 773 774
  def test_update_attributes
    topic = Topic.find(1)
    assert !topic.approved?
    assert_equal "The First Topic", topic.title
J
Jeremy Kemper 已提交
775

776 777 778 779 780 781 782 783 784 785
    topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
    topic.reload
    assert topic.approved?
    assert_equal "The First Topic Updated", topic.title

    topic.update_attributes(:approved => false, :title => "The First Topic")
    topic.reload
    assert !topic.approved?
    assert_equal "The First Topic", topic.title
  end
J
Jeremy Kemper 已提交
786

787 788 789 790
  def test_update_attributes!
    reply = Reply.find(2)
    assert_equal "The Second Topic's of the day", reply.title
    assert_equal "Have a nice day", reply.content
J
Jeremy Kemper 已提交
791

792 793 794 795
    reply.update_attributes!("title" => "The Second Topic's of the day updated", "content" => "Have a nice evening")
    reply.reload
    assert_equal "The Second Topic's of the day updated", reply.title
    assert_equal "Have a nice evening", reply.content
J
Jeremy Kemper 已提交
796

797 798 799 800
    reply.update_attributes!(:title => "The Second Topic's of the day", :content => "Have a nice day")
    reply.reload
    assert_equal "The Second Topic's of the day", reply.title
    assert_equal "Have a nice day", reply.content
J
Jeremy Kemper 已提交
801

802 803
    assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
  end
J
Jeremy Kemper 已提交
804

805 806 807 808 809
  def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
    topic = TopicWithProtectedContentAndAccessibleAuthorName.new
    assert_raises(RuntimeError) { topic.attributes = { "author_name" => "me" } }
    assert_raises(RuntimeError) { topic.attributes = { "content" => "stuff" } }
  end
J
Jeremy Kemper 已提交
810

D
Initial  
David Heinemeier Hansson 已提交
811 812 813 814 815
  def test_mass_assignment_protection
    firm = Firm.new
    firm.attributes = { "name" => "Next Angle", "rating" => 5 }
    assert_equal 1, firm.rating
  end
J
Jeremy Kemper 已提交
816

817 818
  def test_mass_assignment_protection_against_class_attribute_writers
    [:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
819
      :default_timezone, :allow_concurrency, :schema_format, :verification_timeout, :lock_optimistically, :record_timestamps].each do |method|
820 821 822 823 824 825
      assert  Task.respond_to?(method)
      assert  Task.respond_to?("#{method}=")
      assert  Task.new.respond_to?(method)
      assert !Task.new.respond_to?("#{method}=")
    end
  end
826 827 828 829 830 831 832 833 834

  def test_customized_primary_key_remains_protected
    subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
    assert_nil subscriber.id

    keyboard = Keyboard.new(:key_number => 9, :name => 'nice try')
    assert_nil keyboard.id
  end

835
  def test_customized_primary_key_remains_protected_when_referred_to_as_id
836 837 838 839 840 841
    subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
    assert_nil subscriber.id

    keyboard = Keyboard.new(:id => 9, :name => 'nice try')
    assert_nil keyboard.id
  end
J
Jeremy Kemper 已提交
842

843 844 845 846 847 848
  def test_mass_assignment_protection_on_defaults
    firm = Firm.new
    firm.attributes = { "id" => 5, "type" => "Client" }
    assert_nil firm.id
    assert_equal "Firm", firm[:type]
  end
J
Jeremy Kemper 已提交
849

D
Initial  
David Heinemeier Hansson 已提交
850
  def test_mass_assignment_accessible
851
    reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
D
Initial  
David Heinemeier Hansson 已提交
852
    reply.save
J
Jeremy Kemper 已提交
853 854

    assert reply.approved?
J
Jeremy Kemper 已提交
855

J
Jeremy Kemper 已提交
856
    reply.approved = false
D
Initial  
David Heinemeier Hansson 已提交
857 858
    reply.save

J
Jeremy Kemper 已提交
859
    assert !reply.approved?
D
Initial  
David Heinemeier Hansson 已提交
860
  end
J
Jeremy Kemper 已提交
861

D
Initial  
David Heinemeier Hansson 已提交
862
  def test_mass_assignment_protection_inheritance
863
    assert_nil LoosePerson.accessible_attributes
864
    assert_equal Set.new([ 'credit_rating', 'administrator' ]), LoosePerson.protected_attributes
865 866

    assert_nil LooseDescendant.accessible_attributes
867 868 869 870
    assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number' ]), LooseDescendant.protected_attributes

    assert_nil LooseDescendantSecond.accessible_attributes
    assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number', 'name' ]), LooseDescendantSecond.protected_attributes, 'Running attr_protected twice in one class should merge the protections'
871

D
Initial  
David Heinemeier Hansson 已提交
872
    assert_nil TightPerson.protected_attributes
873
    assert_equal Set.new([ 'name', 'address' ]), TightPerson.accessible_attributes
874 875

    assert_nil TightDescendant.protected_attributes
876
    assert_equal Set.new([ 'name', 'address', 'phone_number' ]), TightDescendant.accessible_attributes
D
Initial  
David Heinemeier Hansson 已提交
877
  end
J
Jeremy Kemper 已提交
878

879
  def test_readonly_attributes
880
    assert_equal Set.new([ 'title', 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
J
Jeremy Kemper 已提交
881

882 883 884
    post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
    post.reload
    assert_equal "cannot change this", post.title
J
Jeremy Kemper 已提交
885

886 887 888 889 890
    post.update_attributes(:title => "try to change", :body => "changed")
    post.reload
    assert_equal "cannot change this", post.title
    assert_equal "changed", post.body
  end
D
Initial  
David Heinemeier Hansson 已提交
891 892 893 894 895

  def test_multiparameter_attributes_on_date
    attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
    topic = Topic.find(1)
    topic.attributes = attributes
J
Jeremy Kemper 已提交
896
    # note that extra #to_date call allows test to pass for Oracle, which
897
    # treats dates/times the same
898
    assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
D
Initial  
David Heinemeier Hansson 已提交
899 900 901 902 903 904
  end

  def test_multiparameter_attributes_on_date_with_empty_date
    attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
    topic = Topic.find(1)
    topic.attributes = attributes
J
Jeremy Kemper 已提交
905
    # note that extra #to_date call allows test to pass for Oracle, which
906
    # treats dates/times the same
907
    assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
D
Initial  
David Heinemeier Hansson 已提交
908 909 910 911 912 913 914 915 916 917
  end

  def test_multiparameter_attributes_on_date_with_all_empty
    attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
    topic = Topic.find(1)
    topic.attributes = attributes
    assert_nil topic.last_read
  end

  def test_multiparameter_attributes_on_time
J
Jeremy Kemper 已提交
918 919
    attributes = {
      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
D
Initial  
David Heinemeier Hansson 已提交
920 921 922 923 924 925
      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
    }
    topic = Topic.find(1)
    topic.attributes = attributes
    assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
  end
926 927 928 929 930 931 932 933 934 935 936
  
  def test_multiparameter_attributes_on_time_with_old_date
    attributes = {
      "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
    }
    topic = Topic.find(1)
    topic.attributes = attributes
    # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
    assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
  end
D
Initial  
David Heinemeier Hansson 已提交
937

938 939 940 941 942 943 944 945 946 947 948 949 950
  def test_multiparameter_attributes_on_time_with_utc
    ActiveRecord::Base.default_timezone = :utc
    attributes = {
      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
    }
    topic = Topic.find(1)
    topic.attributes = attributes
    assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
  ensure
    ActiveRecord::Base.default_timezone = :local
  end

951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
  uses_tzinfo "test_multiparameter_attributes_on_time_with_time_zone_aware_attributes" do
    def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
      ActiveRecord::Base.time_zone_aware_attributes = true
      ActiveRecord::Base.default_timezone = :utc
      Time.zone = TimeZone[-28800]
      attributes = {
        "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
        "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
      }
      topic = Topic.find(1)
      topic.attributes = attributes
      assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
      assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
      assert_equal Time.zone, topic.written_on.time_zone
    ensure
      ActiveRecord::Base.time_zone_aware_attributes = false
      ActiveRecord::Base.default_timezone = :local
      Time.zone = nil
    end
970
  end
971

972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
  def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
    ActiveRecord::Base.time_zone_aware_attributes = true
    ActiveRecord::Base.default_timezone = :utc
    Time.zone = TimeZone[-28800]
    Topic.skip_time_zone_conversion_for_attributes = [:written_on]
    attributes = {
      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
    }
    topic = Topic.find(1)
    topic.attributes = attributes
    assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
    assert_equal false, topic.written_on.respond_to?(:time_zone)
  ensure
    ActiveRecord::Base.time_zone_aware_attributes = false
    ActiveRecord::Base.default_timezone = :local
    Time.zone = nil
    Topic.skip_time_zone_conversion_for_attributes = []
  end

D
Initial  
David Heinemeier Hansson 已提交
992
  def test_multiparameter_attributes_on_time_with_empty_seconds
J
Jeremy Kemper 已提交
993 994
    attributes = {
      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
D
Initial  
David Heinemeier Hansson 已提交
995 996 997 998 999 1000 1001
      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
    }
    topic = Topic.find(1)
    topic.attributes = attributes
    assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
  end

1002 1003
  def test_multiparameter_mass_assignment_protector
    task = Task.new
1004
    time = Time.mktime(2000, 1, 1, 1)
J
Jeremy Kemper 已提交
1005
    task.starting = time
1006 1007 1008 1009
    attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" }
    task.attributes = attributes
    assert_equal time, task.starting
  end
J
Jeremy Kemper 已提交
1010

1011 1012 1013 1014 1015 1016 1017
  def test_multiparameter_assignment_of_aggregation
    customer = Customer.new
    address = Address.new("The Street", "The City", "The Country")
    attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
    customer.attributes = attributes
    assert_equal address, customer.address
  end
1018

1019
  def test_attributes_on_dummy_time
1020 1021
    # Oracle, SQL Server, and Sybase do not have a TIME datatype.
    return true if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
1022

1023 1024 1025 1026 1027 1028 1029 1030
    attributes = {
      "bonus_time" => "5:42:00AM"
    }
    topic = Topic.find(1)
    topic.attributes = attributes
    assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
  end

D
Initial  
David Heinemeier Hansson 已提交
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
  def test_boolean
    b_false = Booleantest.create({ "value" => false })
    false_id = b_false.id
    b_true = Booleantest.create({ "value" => true })
    true_id = b_true.id

    b_false = Booleantest.find(false_id)
    assert !b_false.value?
    b_true = Booleantest.find(true_id)
    assert b_true.value?
  end
1042 1043

  def test_boolean_cast_from_string
D
David Heinemeier Hansson 已提交
1044
    b_false = Booleantest.create({ "value" => "0" })
1045
    false_id = b_false.id
D
David Heinemeier Hansson 已提交
1046
    b_true = Booleantest.create({ "value" => "1" })
1047 1048 1049 1050 1051
    true_id = b_true.id

    b_false = Booleantest.find(false_id)
    assert !b_false.value?
    b_true = Booleantest.find(true_id)
J
Jeremy Kemper 已提交
1052
    assert b_true.value?
1053
  end
J
Jeremy Kemper 已提交
1054

D
Initial  
David Heinemeier Hansson 已提交
1055 1056
  def test_clone
    topic = Topic.find(1)
1057 1058
    cloned_topic = nil
    assert_nothing_raised { cloned_topic = topic.clone }
D
Initial  
David Heinemeier Hansson 已提交
1059
    assert_equal topic.title, cloned_topic.title
1060
    assert cloned_topic.new_record?
D
Initial  
David Heinemeier Hansson 已提交
1061 1062

    # test if the attributes have been cloned
J
Jeremy Kemper 已提交
1063 1064
    topic.title = "a"
    cloned_topic.title = "b"
D
Initial  
David Heinemeier Hansson 已提交
1065 1066 1067 1068 1069 1070
    assert_equal "a", topic.title
    assert_equal "b", cloned_topic.title

    # test if the attribute values have been cloned
    topic.title = {"a" => "b"}
    cloned_topic = topic.clone
J
Jeremy Kemper 已提交
1071
    cloned_topic.title["a"] = "c"
D
Initial  
David Heinemeier Hansson 已提交
1072
    assert_equal "b", topic.title["a"]
1073

1074 1075 1076 1077
    #test if attributes set as part of after_initialize are cloned correctly
    assert_equal topic.author_email_address, cloned_topic.author_email_address

    # test if saved clone object differs from original
1078
    cloned_topic.save
1079
    assert !cloned_topic.new_record?
1080
    assert cloned_topic.id != topic.id
D
Initial  
David Heinemeier Hansson 已提交
1081
  end
1082 1083 1084 1085 1086 1087 1088 1089 1090

  def test_clone_with_aggregate_of_same_name_as_attribute
    dev = DeveloperWithAggregate.find(1)
    assert_kind_of DeveloperSalary, dev.salary

    clone = nil
    assert_nothing_raised { clone = dev.clone }
    assert_kind_of DeveloperSalary, clone.salary
    assert_equal dev.salary.amount, clone.salary.amount
1091
    assert clone.new_record?
1092 1093 1094 1095 1096 1097 1098

    # test if the attributes have been cloned
    original_amount = clone.salary.amount
    dev.salary.amount = 1
    assert_equal original_amount, clone.salary.amount

    assert clone.save
1099
    assert !clone.new_record?
1100 1101 1102 1103 1104 1105 1106 1107 1108
    assert clone.id != dev.id
  end

  def test_clone_preserves_subtype
    clone = nil
    assert_nothing_raised { clone = Company.find(3).clone }
    assert_kind_of Client, clone
  end

D
Initial  
David Heinemeier Hansson 已提交
1109 1110 1111 1112 1113 1114 1115 1116
  def test_bignum
    company = Company.find(1)
    company.rating = 2147483647
    company.save
    company = Company.find(1)
    assert_equal 2147483647, company.rating
  end

1117
  # TODO: extend defaults tests to other databases!
1118
  if current_adapter?(:PostgreSQLAdapter)
1119
    def test_default
D
Initial  
David Heinemeier Hansson 已提交
1120
      default = Default.new
J
Jeremy Kemper 已提交
1121

D
Initial  
David Heinemeier Hansson 已提交
1122 1123 1124
      # fixed dates / times
      assert_equal Date.new(2004, 1, 1), default.fixed_date
      assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
J
Jeremy Kemper 已提交
1125

D
Initial  
David Heinemeier Hansson 已提交
1126 1127 1128 1129 1130
      # char types
      assert_equal 'Y', default.char1
      assert_equal 'a varchar field', default.char2
      assert_equal 'a text field', default.char3
    end
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147

    class Geometric < ActiveRecord::Base; end
    def test_geometric_content

      # accepted format notes:
      # ()'s aren't required
      # values can be a mix of float or integer

      g = Geometric.new(
        :a_point        => '(5.0, 6.1)',
        #:a_line         => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
        :a_line_segment => '(2.0, 3), (5.5, 7.0)',
        :a_box          => '2.0, 3, 5.5, 7.0',
        :a_path         => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]',  # [ ] is an open path
        :a_polygon      => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
        :a_circle       => '<(5.3, 10.4), 2>'
      )
J
Jeremy Kemper 已提交
1148

1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
      assert g.save

      # Reload and check that we have all the geometric attributes.
      h = Geometric.find(g.id)

      assert_equal '(5,6.1)', h.a_point
      assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
      assert_equal '(5.5,7),(2,3)', h.a_box   # reordered to store upper right corner then bottom left corner
      assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
      assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
      assert_equal '<(5.3,10.4),2>', h.a_circle

      # use a geometric function to test for an open path
      objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
      assert_equal objs[0].isopen, 't'

      # test alternate formats when defining the geometric types
J
Jeremy Kemper 已提交
1166

1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
      g = Geometric.new(
        :a_point        => '5.0, 6.1',
        #:a_line         => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
        :a_line_segment => '((2.0, 3), (5.5, 7.0))',
        :a_box          => '(2.0, 3), (5.5, 7.0)',
        :a_path         => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',  # ( ) is a closed path
        :a_polygon      => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
        :a_circle       => '((5.3, 10.4), 2)'
      )

      assert g.save

      # Reload and check that we have all the geometric attributes.
      h = Geometric.find(g.id)
J
Jeremy Kemper 已提交
1181

1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192
      assert_equal '(5,6.1)', h.a_point
      assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
      assert_equal '(5.5,7),(2,3)', h.a_box   # reordered to store upper right corner then bottom left corner
      assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
      assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
      assert_equal '<(5.3,10.4),2>', h.a_circle

      # use a geometric function to test for an closed path
      objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
      assert_equal objs[0].isclosed, 't'
    end
D
Initial  
David Heinemeier Hansson 已提交
1193 1194
  end

1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
  class NumericData < ActiveRecord::Base
    self.table_name = 'numeric_data'
  end

  def test_numeric_fields
    m = NumericData.new(
      :bank_balance => 1586.43,
      :big_bank_balance => BigDecimal("1000234000567.95"),
      :world_population => 6000000000,
      :my_house_population => 3
    )
    assert m.save

    m1 = NumericData.find(m.id)
    assert_not_nil m1

    # As with migration_test.rb, we should make world_population >= 2**62
    # to cover 64-bit platforms and test it is a Bignum, but the main thing
    # is that it's an Integer.
    assert_kind_of Integer, m1.world_population
    assert_equal 6000000000, m1.world_population

    assert_kind_of Fixnum, m1.my_house_population
    assert_equal 3, m1.my_house_population

    assert_kind_of BigDecimal, m1.bank_balance
    assert_equal BigDecimal("1586.43"), m1.bank_balance

    assert_kind_of BigDecimal, m1.big_bank_balance
    assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
  end

D
Initial  
David Heinemeier Hansson 已提交
1227 1228 1229 1230 1231
  def test_auto_id
    auto = AutoId.new
    auto.save
    assert (auto.id > 0)
  end
1232

D
Initial  
David Heinemeier Hansson 已提交
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
  def quote_column_name(name)
    "<#{name}>"
  end

  def test_quote_keys
    ar = AutoId.new
    source = {"foo" => "bar", "baz" => "quux"}
    actual = ar.send(:quote_columns, self, source)
    inverted = actual.invert
    assert_equal("<foo>", inverted["bar"])
    assert_equal("<baz>", inverted["quux"])
  end

1246
  def test_sql_injection_via_find
1247
    assert_raises(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1248 1249 1250 1251
      Topic.find("123456 OR id > 0")
    end
  end

D
Initial  
David Heinemeier Hansson 已提交
1252 1253 1254
  def test_column_name_properly_quoted
    col_record = ColumnName.new
    col_record.references = 40
1255
    assert col_record.save
D
Initial  
David Heinemeier Hansson 已提交
1256
    col_record.references = 41
1257 1258
    assert col_record.save
    assert_not_nil c2 = ColumnName.find(col_record.id)
D
Initial  
David Heinemeier Hansson 已提交
1259 1260 1261
    assert_equal(41, c2.references)
  end

1262
  def test_quoting_arrays
1263
    replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
1264 1265
    assert_equal topics(:first).replies.size, replies.size

1266
    replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
1267 1268 1269
    assert_equal 0, replies.size
  end

D
Initial  
David Heinemeier Hansson 已提交
1270
  MyObject = Struct.new :attribute1, :attribute2
J
Jeremy Kemper 已提交
1271

D
Initial  
David Heinemeier Hansson 已提交
1272 1273
  def test_serialized_attribute
    myobj = MyObject.new('value1', 'value2')
J
Jeremy Kemper 已提交
1274
    topic = Topic.create("content" => myobj)
D
Initial  
David Heinemeier Hansson 已提交
1275 1276 1277 1278
    Topic.serialize("content", MyObject)
    assert_equal(myobj, topic.content)
  end

1279
  def test_nil_serialized_attribute_with_class_constraint
D
Initial  
David Heinemeier Hansson 已提交
1280
    myobj = MyObject.new('value1', 'value2')
1281 1282 1283
    topic = Topic.new
    assert_nil topic.content
  end
D
Initial  
David Heinemeier Hansson 已提交
1284

1285 1286 1287 1288 1289
  def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
    myobj = MyObject.new('value1', 'value2')
    topic = Topic.new(:content => myobj)
    assert topic.save
    Topic.serialize(:content, Hash)
1290
    assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
1291 1292 1293
  ensure
    Topic.serialize(:content)
  end
D
Initial  
David Heinemeier Hansson 已提交
1294

1295
  def test_serialized_attribute_with_class_constraint
D
Initial  
David Heinemeier Hansson 已提交
1296
    settings = { "color" => "blue" }
1297 1298 1299
    Topic.serialize(:content, Hash)
    topic = Topic.new(:content => settings)
    assert topic.save
D
Initial  
David Heinemeier Hansson 已提交
1300
    assert_equal(settings, Topic.find(topic.id).content)
1301
  ensure
D
Initial  
David Heinemeier Hansson 已提交
1302 1303 1304 1305
    Topic.serialize(:content)
  end

  def test_quote
1306 1307 1308
    author_name = "\\ \001 ' \n \\n \""
    topic = Topic.create('author_name' => author_name)
    assert_equal author_name, Topic.find(topic.id).author_name
D
Initial  
David Heinemeier Hansson 已提交
1309
  end
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322

  if RUBY_VERSION < '1.9'
    def test_quote_chars
      str = 'The Narrator'
      topic = Topic.create(:author_name => str)
      assert_equal str, topic.author_name

      assert_kind_of ActiveSupport::Multibyte::Chars, str.chars
      topic = Topic.find_by_author_name(str.chars)

      assert_kind_of Topic, topic
      assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
    end
1323
  end
1324

1325 1326
  def test_class_level_destroy
    should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1327
    Topic.find(1).replies << should_be_destroyed_reply
1328 1329

    Topic.destroy(1)
1330 1331
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
    assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
1332 1333 1334 1335
  end

  def test_class_level_delete
    should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1336
    Topic.find(1).replies << should_be_destroyed_reply
1337 1338

    Topic.delete(1)
1339
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1340 1341
    assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
  end
1342 1343

  def test_increment_attribute
1344 1345
    assert_equal 50, accounts(:signals37).credit_limit
    accounts(:signals37).increment! :credit_limit
J
Jeremy Kemper 已提交
1346
    assert_equal 51, accounts(:signals37, :reload).credit_limit
1347 1348 1349

    accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
    assert_equal 53, accounts(:signals37, :reload).credit_limit
1350
  end
J
Jeremy Kemper 已提交
1351

1352
  def test_increment_nil_attribute
1353 1354 1355
    assert_nil topics(:first).parent_id
    topics(:first).increment! :parent_id
    assert_equal 1, topics(:first).parent_id
1356
  end
J
Jeremy Kemper 已提交
1357

1358 1359 1360
  def test_increment_attribute_by
    assert_equal 50, accounts(:signals37).credit_limit
    accounts(:signals37).increment! :credit_limit, 5
J
Jeremy Kemper 已提交
1361
    assert_equal 55, accounts(:signals37, :reload).credit_limit
1362 1363 1364 1365

    accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3)
    assert_equal 59, accounts(:signals37, :reload).credit_limit
  end
J
Jeremy Kemper 已提交
1366

1367
  def test_decrement_attribute
1368
    assert_equal 50, accounts(:signals37).credit_limit
1369

1370 1371
    accounts(:signals37).decrement!(:credit_limit)
    assert_equal 49, accounts(:signals37, :reload).credit_limit
J
Jeremy Kemper 已提交
1372

1373 1374
    accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
    assert_equal 47, accounts(:signals37, :reload).credit_limit
1375
  end
J
Jeremy Kemper 已提交
1376

1377 1378 1379
  def test_decrement_attribute_by
    assert_equal 50, accounts(:signals37).credit_limit
    accounts(:signals37).decrement! :credit_limit, 5
J
Jeremy Kemper 已提交
1380
    assert_equal 45, accounts(:signals37, :reload).credit_limit
1381 1382 1383 1384

    accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3)
    assert_equal 41, accounts(:signals37, :reload).credit_limit
  end
J
Jeremy Kemper 已提交
1385

1386
  def test_toggle_attribute
1387 1388 1389
    assert !topics(:first).approved?
    topics(:first).toggle!(:approved)
    assert topics(:first).approved?
1390 1391 1392 1393 1394
    topic = topics(:first)
    topic.toggle(:approved)
    assert !topic.approved?
    topic.reload
    assert topic.approved?
1395
  end
1396 1397 1398 1399 1400 1401 1402 1403 1404

  def test_reload
    t1 = Topic.find(1)
    t2 = Topic.find(1)
    t1.title = "something else"
    t1.save
    t2.reload
    assert_equal t1.title, t2.title
  end
1405 1406 1407

  def test_define_attr_method_with_value
    k = Class.new( ActiveRecord::Base )
1408
    k.send(:define_attr_method, :table_name, "foo")
1409 1410 1411 1412 1413
    assert_equal "foo", k.table_name
  end

  def test_define_attr_method_with_block
    k = Class.new( ActiveRecord::Base )
1414
    k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458
    assert_equal "sys_id", k.primary_key
  end

  def test_set_table_name_with_value
    k = Class.new( ActiveRecord::Base )
    k.table_name = "foo"
    assert_equal "foo", k.table_name
    k.set_table_name "bar"
    assert_equal "bar", k.table_name
  end

  def test_set_table_name_with_block
    k = Class.new( ActiveRecord::Base )
    k.set_table_name { "ks" }
    assert_equal "ks", k.table_name
  end

  def test_set_primary_key_with_value
    k = Class.new( ActiveRecord::Base )
    k.primary_key = "foo"
    assert_equal "foo", k.primary_key
    k.set_primary_key "bar"
    assert_equal "bar", k.primary_key
  end

  def test_set_primary_key_with_block
    k = Class.new( ActiveRecord::Base )
    k.set_primary_key { "sys_" + original_primary_key }
    assert_equal "sys_id", k.primary_key
  end

  def test_set_inheritance_column_with_value
    k = Class.new( ActiveRecord::Base )
    k.inheritance_column = "foo"
    assert_equal "foo", k.inheritance_column
    k.set_inheritance_column "bar"
    assert_equal "bar", k.inheritance_column
  end

  def test_set_inheritance_column_with_block
    k = Class.new( ActiveRecord::Base )
    k.set_inheritance_column { original_inheritance_column + "_id" }
    assert_equal "type_id", k.inheritance_column
  end
1459 1460

  def test_count_with_join
1461
    res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
J
Jeremy Kemper 已提交
1462

1463
    res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1464
    assert_equal res, res2
J
Jeremy Kemper 已提交
1465

1466
    res3 = nil
1467 1468 1469 1470 1471
    assert_nothing_raised do
      res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
                        :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
    end
    assert_equal res, res3
J
Jeremy Kemper 已提交
1472

1473
    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"
1474 1475
    res5 = nil
    assert_nothing_raised do
1476 1477
      res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
                        :joins => "p, comments co",
1478 1479 1480
                        :select => "p.id")
    end

J
Jeremy Kemper 已提交
1481
    assert_equal res4, res5
1482

1483 1484 1485 1486 1487 1488 1489 1490 1491 1492
    unless current_adapter?(:SQLite2Adapter, :DeprecatedSQLiteAdapter)
      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"
      res7 = nil
      assert_nothing_raised do
        res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
                          :joins => "p, comments co",
                          :select => "p.id",
                          :distinct => true)
      end
      assert_equal res6, res7
1493
    end
1494
  end
J
Jeremy Kemper 已提交
1495 1496

  def test_clear_association_cache_stored
1497 1498 1499 1500 1501 1502
    firm = Firm.find(1)
    assert_kind_of Firm, firm

    firm.clear_association_cache
    assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
  end
J
Jeremy Kemper 已提交
1503

1504 1505 1506 1507 1508 1509
  def test_clear_association_cache_new_record
     firm            = Firm.new
     client_stored   = Client.find(3)
     client_new      = Client.new
     client_new.name = "The Joneses"
     clients         = [ client_stored, client_new ]
1510

1511
     firm.clients    << clients
1512
     assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1513 1514

     firm.clear_association_cache
1515
     assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1516
  end
1517

1518 1519 1520 1521 1522 1523
  def test_interpolate_sql
    assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') }
    assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
    assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
  end

1524
  def test_scoped_find_conditions
M
Marcel Molina 已提交
1525
    scoped_developers = Developer.with_scope(:find => { :conditions => 'salary > 90000' }) do
1526 1527
      Developer.find(:all, :conditions => 'id < 5')
    end
M
Marcel Molina 已提交
1528 1529
    assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
    assert_equal 3, scoped_developers.size
1530
  end
J
Jeremy Kemper 已提交
1531

1532
  def test_scoped_find_limit_offset
M
Marcel Molina 已提交
1533
    scoped_developers = Developer.with_scope(:find => { :limit => 3, :offset => 2 }) do
1534
      Developer.find(:all, :order => 'id')
J
Jeremy Kemper 已提交
1535
    end
M
Marcel Molina 已提交
1536 1537 1538
    assert !scoped_developers.include?(developers(:david))
    assert !scoped_developers.include?(developers(:jamis))
    assert_equal 3, scoped_developers.size
J
Jeremy Kemper 已提交
1539

1540
    # Test without scoped find conditions to ensure we get the whole thing
1541
    developers = Developer.find(:all, :order => 'id')
M
Marcel Molina 已提交
1542
    assert_equal Developer.count, developers.size
1543
  end
1544

J
Jeremy Kemper 已提交
1545 1546
  def test_scoped_find_order
    # Test order in scope
1547 1548
    scoped_developers = Developer.with_scope(:find => { :limit => 1, :order => 'salary DESC' }) do
      Developer.find(:all)
J
Jeremy Kemper 已提交
1549
    end
1550 1551 1552 1553 1554
    assert_equal 'Jamis', scoped_developers.first.name
    assert scoped_developers.include?(developers(:jamis))
    # Test scope without order and order in find
    scoped_developers = Developer.with_scope(:find => { :limit => 1 }) do
      Developer.find(:all, :order => 'salary DESC')
J
Jeremy Kemper 已提交
1555
    end
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567
    # Test scope order + find order, find has priority
    scoped_developers = Developer.with_scope(:find => { :limit => 3, :order => 'id DESC' }) do
      Developer.find(:all, :order => 'salary ASC')
    end
    assert scoped_developers.include?(developers(:poor_jamis))
    assert scoped_developers.include?(developers(:david))
    assert scoped_developers.include?(developers(:dev_10))
    # Test without scoped find conditions to ensure we get the right thing
    developers = Developer.find(:all, :order => 'id', :limit => 1)
    assert scoped_developers.include?(developers(:david))
  end

1568 1569 1570 1571 1572 1573 1574 1575
  def test_scoped_find_limit_offset_including_has_many_association
    topics = Topic.with_scope(:find => {:limit => 1, :offset => 1, :include => :replies}) do
      Topic.find(:all, :order => "topics.id")
    end
    assert_equal 1, topics.size
    assert_equal 2, topics.first.id
  end

1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
  def test_scoped_find_order_including_has_many_association
    developers = Developer.with_scope(:find => { :order => 'developers.salary DESC', :include => :projects }) do
      Developer.find(:all)
    end
    assert developers.size >= 2
    for i in 1...developers.size
      assert developers[i-1].salary >= developers[i].salary
    end
  end

1586
  def test_abstract_class
1587
    assert !ActiveRecord::Base.abstract_class?
1588 1589
    assert LoosePerson.abstract_class?
    assert !LooseDescendant.abstract_class?
1590 1591 1592
  end

  def test_base_class
1593 1594 1595 1596
    assert_equal LoosePerson,     LoosePerson.base_class
    assert_equal LooseDescendant, LooseDescendant.base_class
    assert_equal TightPerson,     TightPerson.base_class
    assert_equal TightPerson,     TightDescendant.base_class
1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629

    assert_equal Post, Post.base_class
    assert_equal Post, SpecialPost.base_class
    assert_equal Post, StiPost.base_class
    assert_equal SubStiPost, SubStiPost.base_class
  end

  def test_descends_from_active_record
    # Tries to call Object.abstract_class?
    assert_raise(NoMethodError) do
      ActiveRecord::Base.descends_from_active_record?
    end

    # Abstract subclass of AR::Base.
    assert LoosePerson.descends_from_active_record?

    # Concrete subclass of an abstract class.
    assert LooseDescendant.descends_from_active_record?

    # Concrete subclass of AR::Base.
    assert TightPerson.descends_from_active_record?

    # Concrete subclass of a concrete class but has no type column.
    assert TightDescendant.descends_from_active_record?

    # Concrete subclass of AR::Base.
    assert Post.descends_from_active_record?

    # Abstract subclass of a concrete class which has a type column.
    # This is pathological, as you'll never have Sub < Abstract < Concrete.
    assert !StiPost.descends_from_active_record?

    # Concrete subclasses an abstract class which has a type column.
1630
    assert !SubStiPost.descends_from_active_record?
1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642
  end

  def test_find_on_abstract_base_class_doesnt_use_type_condition
    old_class = LooseDescendant
    Object.send :remove_const, :LooseDescendant

    descendant = old_class.create!
    assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
  ensure
    unless Object.const_defined?(:LooseDescendant)
      Object.const_set :LooseDescendant, old_class
    end
1643 1644
  end

1645 1646 1647 1648 1649 1650 1651
  def test_assert_queries
    query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
    assert_queries(2) { 2.times { query.call } }
    assert_queries 1, &query
    assert_no_queries { assert true }
  end

1652
  def test_to_xml
1653
    xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
1654 1655
    bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
    written_on_in_current_timezone = topics(:first).written_on.xmlschema
1656
    last_read_in_current_timezone = topics(:first).last_read.xmlschema
1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679

    assert_equal "topic", xml.root.name
    assert_equal "The First Topic" , xml.elements["//title"].text
    assert_equal "David" , xml.elements["//author-name"].text

    assert_equal "1", xml.elements["//id"].text
    assert_equal "integer" , xml.elements["//id"].attributes['type']

    assert_equal "1", xml.elements["//replies-count"].text
    assert_equal "integer" , xml.elements["//replies-count"].attributes['type']

    assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
    assert_equal "datetime" , xml.elements["//written-on"].attributes['type']

    assert_equal "--- Have a nice day\n" , xml.elements["//content"].text
    assert_equal "yaml" , xml.elements["//content"].attributes['type']

    assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text

    assert_equal nil, xml.elements["//parent-id"].text
    assert_equal "integer", xml.elements["//parent-id"].attributes['type']
    assert_equal "true", xml.elements["//parent-id"].attributes['nil']

1680
    if current_adapter?(:SybaseAdapter, :SQLServerAdapter, :OracleAdapter)
1681 1682
      assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
      assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
1683
    else
1684 1685
      assert_equal "2004-04-15", xml.elements["//last-read"].text
      assert_equal "date" , xml.elements["//last-read"].attributes['type']
1686
    end
1687

1688
    # Oracle and DB2 don't have true boolean or time-only fields
1689
    unless current_adapter?(:OracleAdapter, :DB2Adapter)
1690 1691 1692 1693 1694
      assert_equal "false", xml.elements["//approved"].text
      assert_equal "boolean" , xml.elements["//approved"].attributes['type']

      assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
      assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
1695
    end
1696
  end
1697

1698
  def test_to_xml_skipping_attributes
1699
    xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
1700
    assert_equal "<topic>", xml.first(7)
1701
    assert !xml.include?(%(<title>The First Topic</title>))
J
Jeremy Kemper 已提交
1702
    assert xml.include?(%(<author-name>David</author-name>))
1703

1704
    xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
1705
    assert !xml.include?(%(<title>The First Topic</title>))
J
Jeremy Kemper 已提交
1706
    assert !xml.include?(%(<author-name>David</author-name>))
1707
  end
1708

1709
  def test_to_xml_including_has_many_association
1710
    xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
1711
    assert_equal "<topic>", xml.first(7)
1712
    assert xml.include?(%(<replies type="array"><reply>))
1713
    assert xml.include?(%(<title>The Second Topic's of the day</title>))
1714
  end
1715 1716 1717

  def test_array_to_xml_including_has_many_association
    xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
1718
    assert xml.include?(%(<replies type="array"><reply>))
1719
  end
1720 1721 1722

  def test_array_to_xml_including_methods
    xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
1723 1724
    assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
    assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
1725
  end
J
Jeremy Kemper 已提交
1726

1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738
  def test_array_to_xml_including_has_one_association
    xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
    assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
    assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
  end

  def test_array_to_xml_including_belongs_to_association
    xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
    assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
    assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
    assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
  end
1739 1740 1741 1742 1743 1744 1745 1746

  def test_to_xml_including_belongs_to_association
    xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
    assert !xml.include?("<firm>")

    xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
    assert xml.include?("<firm>")
  end
J
Jeremy Kemper 已提交
1747

1748 1749 1750 1751
  def test_to_xml_including_multiple_associations
    xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
    assert_equal "<firm>", xml.first(6)
    assert xml.include?(%(<account>))
1752
    assert xml.include?(%(<clients type="array"><client>))
1753
  end
1754 1755 1756

  def test_to_xml_including_multiple_associations_with_options
    xml = companies(:first_firm).to_xml(
J
Jeremy Kemper 已提交
1757
      :indent  => 0, :skip_instruct => true,
1758 1759
      :include => { :clients => { :only => :name } }
    )
J
Jeremy Kemper 已提交
1760

1761
    assert_equal "<firm>", xml.first(6)
1762
    assert xml.include?(%(<client><name>Summit</name></client>))
1763
    assert xml.include?(%(<clients type="array"><client>))
1764
  end
J
Jeremy Kemper 已提交
1765

1766 1767 1768 1769 1770
  def test_to_xml_including_methods
    xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
    assert_equal "<company>", xml.first(9)
    assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
  end
J
Jeremy Kemper 已提交
1771

1772 1773 1774 1775 1776 1777 1778 1779
  def test_to_xml_with_block
    value = "Rockin' the block"
    xml = Company.new.to_xml(:skip_instruct => true) do |xml|
      xml.tag! "arbitrary-element", value
    end
    assert_equal "<company>", xml.first(9)
    assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
  end
J
Jeremy Kemper 已提交
1780

1781 1782 1783 1784
  def test_type_name_with_module_should_handle_beginning
    assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
    assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
  end
J
Jeremy Kemper 已提交
1785

1786 1787 1788
  def test_to_param_should_return_string
    assert_kind_of String, Client.find(:first).to_param
  end
J
Jeremy Kemper 已提交
1789

1790 1791 1792 1793 1794 1795 1796
  def test_inspect_class
    assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
    assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
    assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
  end

  def test_inspect_instance
1797
    topic = topics(:first)
1798
    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
1799
  end
1800

1801
  def test_inspect_new_instance
1802 1803 1804
    assert_match /Topic id: nil/, Topic.new.inspect
  end

1805 1806 1807 1808
  def test_inspect_limited_select_instance
    assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
    assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
  end
J
Jeremy Kemper 已提交
1809

1810 1811 1812
  def test_inspect_class_without_table
    assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
  end
1813

1814 1815
  def test_attribute_for_inspect
    t = topics(:first)
1816
    t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
1817 1818

    assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
1819
    assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
1820
  end
J
Jeremy Kemper 已提交
1821

1822 1823 1824 1825
  def test_becomes
    assert_kind_of Reply, topics(:first).becomes(Reply)
    assert_equal "The First Topic", topics(:first).becomes(Reply).title
  end
1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878

  def test_silence_sets_log_level_to_error_in_block
    original_logger = ActiveRecord::Base.logger
    log = StringIO.new
    ActiveRecord::Base.logger = Logger.new(log)
    ActiveRecord::Base.logger.level = Logger::DEBUG
    ActiveRecord::Base.silence do
      ActiveRecord::Base.logger.warn "warn"
      ActiveRecord::Base.logger.error "error"
    end
    assert_equal "error\n", log.string
  ensure
    ActiveRecord::Base.logger = original_logger
  end

  def test_silence_sets_log_level_back_to_level_before_yield
    original_logger = ActiveRecord::Base.logger
    log = StringIO.new
    ActiveRecord::Base.logger = Logger.new(log)
    ActiveRecord::Base.logger.level = Logger::WARN
    ActiveRecord::Base.silence do
    end
    assert_equal Logger::WARN, ActiveRecord::Base.logger.level
  ensure
    ActiveRecord::Base.logger = original_logger
  end

  def test_benchmark_with_log_level
    original_logger = ActiveRecord::Base.logger
    log = StringIO.new
    ActiveRecord::Base.logger = Logger.new(log)
    ActiveRecord::Base.logger.level = Logger::WARN
    ActiveRecord::Base.benchmark("Debug Topic Count", Logger::DEBUG) { Topic.count }
    ActiveRecord::Base.benchmark("Warn Topic Count",  Logger::WARN)  { Topic.count }
    ActiveRecord::Base.benchmark("Error Topic Count", Logger::ERROR) { Topic.count }
    assert_no_match /Debug Topic Count/, log.string
    assert_match /Warn Topic Count/, log.string
    assert_match /Error Topic Count/, log.string
  ensure
    ActiveRecord::Base.logger = original_logger
  end

  def test_benchmark_with_use_silence
    original_logger = ActiveRecord::Base.logger
    log = StringIO.new
    ActiveRecord::Base.logger = Logger.new(log)
    ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, true) { ActiveRecord::Base.logger.debug "Loud" }
    ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, false)  { ActiveRecord::Base.logger.debug "Quiet" }
    assert_no_match /Loud/, log.string
    assert_match /Quiet/, log.string
  ensure
    ActiveRecord::Base.logger = original_logger
  end
1879
end