base_test.rb 68.5 KB
Newer Older
1
require "cases/helper"
2
require 'models/post'
3
require 'models/author'
J
Jeremy Kemper 已提交
4 5
require 'models/topic'
require 'models/reply'
6
require 'models/category'
J
Jeremy Kemper 已提交
7 8 9 10 11 12 13 14 15
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'
16
require 'models/comment'
J
Jeremy Kemper 已提交
17 18
require 'models/minimalistic'
require 'models/warehouse_thing'
19
require 'models/parrot'
20
require 'models/loose_person'
21
require 'rexml/document'
J
Jeremy Kemper 已提交
22
require 'active_support/core_ext/exception'
D
Initial  
David Heinemeier Hansson 已提交
23 24

class Category < ActiveRecord::Base; end
25
class Categorization < ActiveRecord::Base; end
D
Initial  
David Heinemeier Hansson 已提交
26
class Smarts < ActiveRecord::Base; end
27
class CreditCard < ActiveRecord::Base
28 29 30 31 32 33
  class PinNumber < ActiveRecord::Base
    class CvvCode < ActiveRecord::Base; end
    class SubCvvCode < CvvCode; end
  end
  class SubPinNumber < PinNumber; end
  class Brand < Category; end
34
end
D
Initial  
David Heinemeier Hansson 已提交
35
class MasterCreditCard < ActiveRecord::Base; end
36
class Post < ActiveRecord::Base; end
37
class Computer < ActiveRecord::Base; end
38
class NonExistentTable < ActiveRecord::Base; end
39
class TestOracleDefault < ActiveRecord::Base; end
D
Initial  
David Heinemeier Hansson 已提交
40

41 42 43 44
class ReadonlyTitlePost < Post
  attr_readonly :title
end

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

47
class BasicsTest < ActiveRecord::TestCase
48
  fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
D
Initial  
David Heinemeier Hansson 已提交
49

50 51 52 53
  def test_table_exists
    assert !NonExistentTable.table_exists?
    assert Topic.table_exists?
  end
J
Jeremy Kemper 已提交
54

D
Initial  
David Heinemeier Hansson 已提交
55 56 57 58 59 60
  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)
61
    assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
D
Initial  
David Heinemeier Hansson 已提交
62
  end
63

64 65
  def test_set_attributes_without_hash
    topic = Topic.new
P
Pratik Naik 已提交
66
    assert_nothing_raised { topic.attributes = '' }
67 68
  end

D
Initial  
David Heinemeier Hansson 已提交
69
  def test_integers_as_nil
J
Jeremy Kemper 已提交
70 71
    test = AutoId.create('value' => '')
    assert_nil AutoId.find(test.id).value
D
Initial  
David Heinemeier Hansson 已提交
72
  end
J
Jeremy Kemper 已提交
73

D
Initial  
David Heinemeier Hansson 已提交
74 75 76 77 78 79 80 81 82
  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 已提交
83

D
Initial  
David Heinemeier Hansson 已提交
84 85
  def test_respond_to?
    topic = Topic.find(1)
86 87 88 89 90 91 92 93
    assert_respond_to topic, "title"
    assert_respond_to topic, "title?"
    assert_respond_to topic, "title="
    assert_respond_to topic, :title
    assert_respond_to topic, :title?
    assert_respond_to topic, :title=
    assert_respond_to topic, "author_name"
    assert_respond_to topic, "attribute_names"
D
Initial  
David Heinemeier Hansson 已提交
94 95 96
    assert !topic.respond_to?("nothingness")
    assert !topic.respond_to?(:nothingness)
  end
J
Jeremy Kemper 已提交
97

D
Initial  
David Heinemeier Hansson 已提交
98 99 100 101 102 103 104 105
  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

106 107
  def test_read_attributes_before_type_cast
    category = Category.new({:name=>"Test categoty", :type => nil})
108
    category_attrs = {"name"=>"Test categoty", "type" => nil, "categorizations_count" => nil}
109 110
    assert_equal category_attrs , category.attributes_before_type_cast
  end
111

112 113 114
  if current_adapter?(:MysqlAdapter)
    def test_read_attributes_before_type_cast_on_boolean
      bool = Booleantest.create({ "value" => false })
115
      assert_equal "0", bool.reload.attributes_before_type_cast["value"]
116
    end
117
  end
118

119 120
  def test_read_attributes_before_type_cast_on_datetime
    developer = Developer.find(:first)
121 122 123 124 125
    # Oracle adapter returns Time before type cast
    unless current_adapter?(:OracleAdapter)
      assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
    else
      assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db)
126 127 128 129 130 131 132 133

      developer.created_at = "345643456"
      assert_equal developer.created_at_before_type_cast, "345643456"
      assert_equal developer.created_at, nil

      developer.created_at = "2010-03-21T21:23:32+01:00"
      assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00"
      assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00")
134
    end
135
  end
136

D
Initial  
David Heinemeier Hansson 已提交
137 138 139 140 141 142
  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 已提交
143

144
    topic.content_will_change!
D
Initial  
David Heinemeier Hansson 已提交
145 146 147 148 149
    topic.content["three"] = 3
    topic.save

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

D
Initial  
David Heinemeier Hansson 已提交
151 152 153 154 155 156 157 158
  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 已提交
159

D
Initial  
David Heinemeier Hansson 已提交
160 161 162 163
    topic = Topic.find(topic.id)
    topic.content << "five"
    assert_equal(%w( one two three four five ), topic.content)
  end
J
Jeremy Kemper 已提交
164

165 166 167 168
  def test_case_sensitive_attributes_hash
    # DB2 is not case-sensitive
    return true if current_adapter?(:DB2Adapter)

169
    assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
170
  end
171

172 173 174 175 176 177 178 179
  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
180
    assert_equal new_topic_values[:title], topic.title
D
Initial  
David Heinemeier Hansson 已提交
181
  end
J
Jeremy Kemper 已提交
182

D
Initial  
David Heinemeier Hansson 已提交
183 184 185 186
  def test_create_through_factory
    topic = Topic.create("title" => "New Topic")
    topicReloaded = Topic.find(topic.id)
    assert_equal(topic, topicReloaded)
187
  end
188

189 190 191 192 193 194 195 196
  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
197

198 199 200 201 202 203 204 205 206 207
  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

208 209 210
  def test_read_attribute_when_false
    topic = topics(:first)
    topic.approved = false
J
Jeremy Kemper 已提交
211
    assert !topic.approved?, "approved should be false"
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
    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
232
    assert !topic.approved?, "approved should be false"
233 234 235
    topic.approved = "false"
    # puts "Expecting false"
    # puts topic.inspect
236
    assert !topic.approved?, "approved should be false"
237 238 239
    topic.approved = "true"
    # puts "Expecting true"
    # puts topic.inspect
240
    assert topic.approved?, "approved should be true"
241 242 243
    topic.approved = "true"
    # puts "Expecting true"
    # puts topic.inspect
244
    assert topic.approved?, "approved should be true"
245
    # puts ""
246
  end
J
Jeremy Kemper 已提交
247

248 249 250 251
  def test_query_attribute_string
    [nil, "", " "].each do |value|
      assert_equal false, Topic.new(:author_name => value).author_name?
    end
J
Jeremy Kemper 已提交
252

253 254
    assert_equal true, Topic.new(:author_name => "Name").author_name?
  end
J
Jeremy Kemper 已提交
255

256 257 258 259
  def test_query_attribute_number
    [nil, 0, "0"].each do |value|
      assert_equal false, Developer.new(:salary => value).salary?
    end
J
Jeremy Kemper 已提交
260

261 262 263
    assert_equal true, Developer.new(:salary => 1).salary?
    assert_equal true, Developer.new(:salary => "1").salary?
  end
J
Jeremy Kemper 已提交
264

265 266 267 268
  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 已提交
269

270 271 272 273
    [true, "true", "1", 1].each do |value|
      assert_equal true, Topic.new(:approved => value).approved?
    end
  end
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288

  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 已提交
289
    assert_equal 1, object.int_value.to_i
290 291 292 293 294 295
    assert object.int_value?

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

296 297 298
  def test_non_attribute_access_and_assignment
    topic = Topic.new
    assert !topic.respond_to?("mumbo")
299 300
    assert_raise(NoMethodError) { topic.mumbo }
    assert_raise(NoMethodError) { topic.mumbo = 5 }
301 302
  end

D
Initial  
David Heinemeier Hansson 已提交
303
  def test_preserving_date_objects
304
    if current_adapter?(:SybaseAdapter)
305 306
      # Sybase ctlib does not (yet?) support the date type; use datetime instead.
      assert_kind_of(
J
Jeremy Kemper 已提交
307
        Time, Topic.find(1).last_read,
308 309 310
        "The last_read attribute should be of the Time class"
      )
    else
311
      # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
312
      assert_kind_of(
J
Jeremy Kemper 已提交
313
        Date, Topic.find(1).last_read,
314 315 316
        "The last_read attribute should be of the Date class"
      )
    end
317
  end
318

319
  def test_preserving_time_objects
320 321 322 323
    assert_kind_of(
      Time, Topic.find(1).bonus_time,
      "The bonus_time attribute should be of the Time class"
    )
D
Initial  
David Heinemeier Hansson 已提交
324 325 326 327 328

    assert_kind_of(
      Time, Topic.find(1).written_on,
      "The written_on attribute should be of the Time class"
    )
329 330

    # For adapters which support microsecond resolution.
331
    if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLiteAdapter)
332 333
      assert_equal 11, Topic.find(1).written_on.sec
      assert_equal 223300, Topic.find(1).written_on.usec
334
      assert_equal 9900, Topic.find(2).written_on.usec
335
    end
D
Initial  
David Heinemeier Hansson 已提交
336
  end
J
Jeremy Kemper 已提交
337

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
  def test_preserving_time_objects_with_local_time_conversion_to_default_timezone_utc
    with_env_tz 'America/New_York' do
      with_active_record_default_timezone :utc do
        time = Time.local(2000)
        topic = Topic.create('written_on' => time)
        saved_time = Topic.find(topic.id).written_on
        assert_equal time, saved_time
        assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a
        assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
      end
    end
  end

  def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc
    with_env_tz 'America/New_York' do
      with_active_record_default_timezone :utc do
        Time.use_zone 'Central Time (US & Canada)' do
          time = Time.zone.local(2000)
          topic = Topic.create('written_on' => time)
          saved_time = Topic.find(topic.id).written_on
          assert_equal time, saved_time
          assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
          assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
        end
      end
    end
  end

  def test_preserving_time_objects_with_utc_time_conversion_to_default_timezone_local
    with_env_tz 'America/New_York' do
      time = Time.utc(2000)
      topic = Topic.create('written_on' => time)
      saved_time = Topic.find(topic.id).written_on
      assert_equal time, saved_time
      assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
      assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a
    end
  end

  def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local
    with_env_tz 'America/New_York' do
      with_active_record_default_timezone :local do
        Time.use_zone 'Central Time (US & Canada)' do
          time = Time.zone.local(2000)
          topic = Topic.create('written_on' => time)
          saved_time = Topic.find(topic.id).written_on
          assert_equal time, saved_time
          assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
          assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], saved_time.to_a
        end
      end
    end
  end

392 393 394 395 396 397
  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 已提交
398

D
Initial  
David Heinemeier Hansson 已提交
399
  def test_initialize_with_attributes
J
Jeremy Kemper 已提交
400
    topic = Topic.new({
D
Initial  
David Heinemeier Hansson 已提交
401 402
      "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
    })
J
Jeremy Kemper 已提交
403

D
Initial  
David Heinemeier Hansson 已提交
404 405
    assert_equal("initialized from attributes", topic.title)
  end
J
Jeremy Kemper 已提交
406

407 408
  def test_initialize_with_invalid_attribute
    begin
J
Jeremy Kemper 已提交
409
      topic = Topic.new({ "title" => "test",
410 411 412 413 414 415
        "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 已提交
416

D
Initial  
David Heinemeier Hansson 已提交
417
  def test_load
J
Jeremy Kemper 已提交
418
    topics = Topic.find(:all, :order => 'id')
419
    assert_equal(4, topics.size)
420
    assert_equal(topics(:first).title, topics.first.title)
D
Initial  
David Heinemeier Hansson 已提交
421
  end
J
Jeremy Kemper 已提交
422

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

D
Initial  
David Heinemeier Hansson 已提交
426
    assert_equal(1, topics.size)
427
    assert_equal(topics(:second).title, topics.first.title)
D
Initial  
David Heinemeier Hansson 已提交
428 429
  end

430
  GUESSED_CLASSES = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
431

432
  def test_table_name_guesses
D
Initial  
David Heinemeier Hansson 已提交
433
    assert_equal "topics", Topic.table_name
434

D
Initial  
David Heinemeier Hansson 已提交
435 436 437
    assert_equal "categories", Category.table_name
    assert_equal "smarts", Smarts.table_name
    assert_equal "credit_cards", CreditCard.table_name
438
    assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
439 440 441
    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 已提交
442
    assert_equal "master_credit_cards", MasterCreditCard.table_name
443 444 445
  ensure
    GUESSED_CLASSES.each(&:reset_table_name)
  end
D
Initial  
David Heinemeier Hansson 已提交
446

447
  def test_singular_table_name_guesses
D
Initial  
David Heinemeier Hansson 已提交
448
    ActiveRecord::Base.pluralize_table_names = false
449
    GUESSED_CLASSES.each(&:reset_table_name)
450

D
Initial  
David Heinemeier Hansson 已提交
451 452 453
    assert_equal "category", Category.table_name
    assert_equal "smarts", Smarts.table_name
    assert_equal "credit_card", CreditCard.table_name
454
    assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
455 456 457
    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 已提交
458
    assert_equal "master_credit_card", MasterCreditCard.table_name
459
  ensure
D
Initial  
David Heinemeier Hansson 已提交
460
    ActiveRecord::Base.pluralize_table_names = true
461 462
    GUESSED_CLASSES.each(&:reset_table_name)
  end
D
Initial  
David Heinemeier Hansson 已提交
463

464
  def test_table_name_guesses_with_prefixes_and_suffixes
D
Initial  
David Heinemeier Hansson 已提交
465
    ActiveRecord::Base.table_name_prefix = "test_"
466
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
467 468
    assert_equal "test_categories", Category.table_name
    ActiveRecord::Base.table_name_suffix = "_test"
469
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
470 471
    assert_equal "test_categories_test", Category.table_name
    ActiveRecord::Base.table_name_prefix = ""
472
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
473 474
    assert_equal "categories_test", Category.table_name
    ActiveRecord::Base.table_name_suffix = ""
475
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
476
    assert_equal "categories", Category.table_name
477 478 479 480 481
  ensure
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""
    GUESSED_CLASSES.each(&:reset_table_name)
  end
D
Initial  
David Heinemeier Hansson 已提交
482

483
  def test_singular_table_name_guesses_with_prefixes_and_suffixes
D
Initial  
David Heinemeier Hansson 已提交
484
    ActiveRecord::Base.pluralize_table_names = false
485

D
Initial  
David Heinemeier Hansson 已提交
486
    ActiveRecord::Base.table_name_prefix = "test_"
487
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
488 489
    assert_equal "test_category", Category.table_name
    ActiveRecord::Base.table_name_suffix = "_test"
490
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
491 492
    assert_equal "test_category_test", Category.table_name
    ActiveRecord::Base.table_name_prefix = ""
493
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
494 495
    assert_equal "category_test", Category.table_name
    ActiveRecord::Base.table_name_suffix = ""
496
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
497
    assert_equal "category", Category.table_name
498
  ensure
D
Initial  
David Heinemeier Hansson 已提交
499
    ActiveRecord::Base.pluralize_table_names = true
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""
    GUESSED_CLASSES.each(&:reset_table_name)
  end

  def test_table_name_guesses_with_inherited_prefixes_and_suffixes
    GUESSED_CLASSES.each(&:reset_table_name)

    CreditCard.table_name_prefix = "test_"
    CreditCard.reset_table_name
    Category.reset_table_name
    assert_equal "test_credit_cards", CreditCard.table_name
    assert_equal "categories", Category.table_name
    CreditCard.table_name_suffix = "_test"
    CreditCard.reset_table_name
    Category.reset_table_name
    assert_equal "test_credit_cards_test", CreditCard.table_name
    assert_equal "categories", Category.table_name
    CreditCard.table_name_prefix = ""
    CreditCard.reset_table_name
    Category.reset_table_name
    assert_equal "credit_cards_test", CreditCard.table_name
    assert_equal "categories", Category.table_name
    CreditCard.table_name_suffix = ""
    CreditCard.reset_table_name
    Category.reset_table_name
    assert_equal "credit_cards", CreditCard.table_name
    assert_equal "categories", Category.table_name
  ensure
    CreditCard.table_name_prefix = ""
    CreditCard.table_name_suffix = ""
    GUESSED_CLASSES.each(&:reset_table_name)
D
Initial  
David Heinemeier Hansson 已提交
532
  end
J
Jeremy Kemper 已提交
533

D
Initial  
David Heinemeier Hansson 已提交
534
  def test_destroy_all
535 536 537 538 539 540 541 542 543
    conditions = "author_name = 'Mary'"
    topics_by_mary = Topic.all(:conditions => conditions, :order => 'id')
    assert ! topics_by_mary.empty?

    assert_difference('Topic.count', -topics_by_mary.size) do
      destroyed = Topic.destroy_all(conditions).sort_by(&:id)
      assert_equal topics_by_mary, destroyed
      assert destroyed.all? { |topic| topic.frozen? }, "destroyed topics should be frozen"
    end
D
Initial  
David Heinemeier Hansson 已提交
544
  end
545 546

  def test_destroy_many
547 548
    clients = Client.find([2, 3], :order => 'id')

549
    assert_difference('Client.count', -2) do
550 551 552
      destroyed = Client.destroy([2, 3]).sort_by(&:id)
      assert_equal clients, destroyed
      assert destroyed.all? { |client| client.frozen? }, "destroyed clients should be frozen"
553
    end
554 555 556
  end

  def test_delete_many
557 558 559
    original_count = Topic.count
    Topic.delete(deleting = [1, 2])
    assert_equal original_count - deleting.size, Topic.count
560 561
  end

D
Initial  
David Heinemeier Hansson 已提交
562 563 564 565
  def test_boolean_attributes
    assert ! Topic.find(1).approved?
    assert Topic.find(2).approved?
  end
J
Jeremy Kemper 已提交
566

567 568 569 570 571 572
  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

573 574 575 576 577 578 579
  # Oracle UPDATE does not support ORDER BY
  unless current_adapter?(:OracleAdapter)
    def test_update_all_ignores_order_without_limit_from_association
      author = authors(:david)
      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
580 581
    end

582 583 584 585 586 587 588 589 590
    def test_update_all_with_order_and_limit_updates_subset_only
      author = authors(:david)
      assert_nothing_raised do
        assert_equal 1, author.posts_sorted_by_id_limited.size
        assert_equal 2, author.posts_sorted_by_id_limited.find(:all, :limit => 2).size
        assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ])
        assert_equal "bulk update!", posts(:welcome).body
        assert_not_equal "bulk update!", posts(:thinking).body
      end
591 592 593
    end
  end

594
  def test_update_many
595
    topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
596
    updated = Topic.update(topic_data.keys, topic_data.values)
597 598 599 600 601 602

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

603
  def test_delete_all
604
    assert Topic.count > 0
605

606
    assert_equal Topic.count, Topic.delete_all
607 608
  end

D
Initial  
David Heinemeier Hansson 已提交
609
  def test_update_by_condition
610
    Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
D
Initial  
David Heinemeier Hansson 已提交
611 612 613
    assert_equal "Have a nice day", Topic.find(1).content
    assert_equal "bulk updated!", Topic.find(2).content
  end
J
Jeremy Kemper 已提交
614

D
Initial  
David Heinemeier Hansson 已提交
615 616 617 618 619 620 621 622
  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 已提交
623

D
Initial  
David Heinemeier Hansson 已提交
624 625 626
  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"
627
    assert_raise(NoMethodError) { t.title2 }
D
Initial  
David Heinemeier Hansson 已提交
628
  end
J
Jeremy Kemper 已提交
629

D
Initial  
David Heinemeier Hansson 已提交
630 631 632 633
  def test_null_fields
    assert_nil Topic.find(1).parent_id
    assert_nil Topic.create("title" => "Hey you").parent_id
  end
J
Jeremy Kemper 已提交
634

D
Initial  
David Heinemeier Hansson 已提交
635 636
  def test_default_values
    topic = Topic.new
J
Jeremy Kemper 已提交
637
    assert topic.approved?
D
Initial  
David Heinemeier Hansson 已提交
638
    assert_nil topic.written_on
639
    assert_nil topic.bonus_time
D
Initial  
David Heinemeier Hansson 已提交
640
    assert_nil topic.last_read
J
Jeremy Kemper 已提交
641

D
Initial  
David Heinemeier Hansson 已提交
642 643 644
    topic.save

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

J
Jeremy Kemper 已提交
648
    # Oracle has some funky default handling, so it requires a bit of
649
    # extra testing. See ticket #2788.
650 651
    if current_adapter?(:OracleAdapter)
      test = TestOracleDefault.new
652 653 654 655
      assert_equal "X", test.test_char
      assert_equal "hello", test.test_string
      assert_equal 3, test.test_int
    end
D
Initial  
David Heinemeier Hansson 已提交
656
  end
657

658 659
  # Oracle, and Sybase do not have a TIME datatype.
  unless current_adapter?(:OracleAdapter, :SybaseAdapter)
660 661 662 663 664 665 666 667
    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
668

669 670 671 672 673 674 675 676 677 678 679 680
    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
681 682
  end

D
Initial  
David Heinemeier Hansson 已提交
683 684 685 686 687 688 689 690 691
  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
692 693 694 695 696 697 698

    # 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 已提交
699
  end
700

D
Initial  
David Heinemeier Hansson 已提交
701
  def test_equality
702
    assert_equal Topic.find(1), Topic.find(2).topic
D
Initial  
David Heinemeier Hansson 已提交
703
  end
J
Jeremy Kemper 已提交
704

705 706 707
  def test_equality_of_new_records
    assert_not_equal Topic.new, Topic.new
  end
J
Jeremy Kemper 已提交
708

D
Initial  
David Heinemeier Hansson 已提交
709
  def test_hashing
710
    assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
D
Initial  
David Heinemeier Hansson 已提交
711
  end
J
Jeremy Kemper 已提交
712 713 714



715
  def test_readonly_attributes
716
    assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
J
Jeremy Kemper 已提交
717

718 719 720
    post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
    post.reload
    assert_equal "cannot change this", post.title
J
Jeremy Kemper 已提交
721

722 723 724 725 726
    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 已提交
727 728 729 730 731

  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 已提交
732
    # note that extra #to_date call allows test to pass for Oracle, which
733
    # treats dates/times the same
734
    assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
D
Initial  
David Heinemeier Hansson 已提交
735 736
  end

737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
  def test_multiparameter_attributes_on_date_with_empty_year
    attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
    topic = Topic.find(1)
    topic.attributes = attributes
    # note that extra #to_date call allows test to pass for Oracle, which
    # treats dates/times the same
    assert_date_from_db Date.new(1, 6, 24), topic.last_read.to_date
  end

  def test_multiparameter_attributes_on_date_with_empty_month
    attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
    topic = Topic.find(1)
    topic.attributes = attributes
    # note that extra #to_date call allows test to pass for Oracle, which
    # treats dates/times the same
    assert_date_from_db Date.new(2004, 1, 24), topic.last_read.to_date
  end

  def test_multiparameter_attributes_on_date_with_empty_day
D
Initial  
David Heinemeier Hansson 已提交
756 757 758
    attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
    topic = Topic.find(1)
    topic.attributes = attributes
J
Jeremy Kemper 已提交
759
    # note that extra #to_date call allows test to pass for Oracle, which
760
    # treats dates/times the same
761
    assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
D
Initial  
David Heinemeier Hansson 已提交
762 763
  end

764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
  def test_multiparameter_attributes_on_date_with_empty_day_and_year
    attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
    topic = Topic.find(1)
    topic.attributes = attributes
    # note that extra #to_date call allows test to pass for Oracle, which
    # treats dates/times the same
    assert_date_from_db Date.new(1, 6, 1), topic.last_read.to_date
  end

  def test_multiparameter_attributes_on_date_with_empty_day_and_month
    attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
    topic = Topic.find(1)
    topic.attributes = attributes
    # note that extra #to_date call allows test to pass for Oracle, which
    # treats dates/times the same
    assert_date_from_db Date.new(2004, 1, 1), topic.last_read.to_date
  end

  def test_multiparameter_attributes_on_date_with_empty_year_and_month
    attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
    topic = Topic.find(1)
    topic.attributes = attributes
    # note that extra #to_date call allows test to pass for Oracle, which
    # treats dates/times the same
    assert_date_from_db Date.new(1, 1, 24), topic.last_read.to_date
  end

D
Initial  
David Heinemeier Hansson 已提交
791 792 793 794 795 796 797 798
  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 已提交
799 800
    attributes = {
      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
D
Initial  
David Heinemeier Hansson 已提交
801 802 803 804 805 806
      "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
807

808 809 810 811 812 813 814 815 816 817
  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 已提交
818

819 820 821 822 823 824 825 826 827 828 829 830 831
  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

832 833 834
  def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
    ActiveRecord::Base.time_zone_aware_attributes = true
    ActiveRecord::Base.default_timezone = :utc
835
    Time.zone = ActiveSupport::TimeZone[-28800]
836 837 838 839 840 841 842 843 844 845 846 847 848
    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
849
  end
850

851 852
  def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
    ActiveRecord::Base.time_zone_aware_attributes = false
853
    Time.zone = ActiveSupport::TimeZone[-28800]
854 855 856 857 858 859 860 861 862 863 864 865
    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.local(2004, 6, 24, 16, 24, 0), topic.written_on
    assert_equal false, topic.written_on.respond_to?(:time_zone)
  ensure
    Time.zone = nil
  end

866 867 868
  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
869
    Time.zone = ActiveSupport::TimeZone[-28800]
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
    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
885

886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
  # Oracle, and Sybase do not have a TIME datatype.
  unless current_adapter?(:OracleAdapter, :SybaseAdapter)
    def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
      ActiveRecord::Base.time_zone_aware_attributes = true
      ActiveRecord::Base.default_timezone = :utc
      Time.zone = ActiveSupport::TimeZone[-28800]
      attributes = {
        "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
        "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
      }
      topic = Topic.find(1)
      topic.attributes = attributes
      assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
      assert topic.bonus_time.utc?
    ensure
      ActiveRecord::Base.time_zone_aware_attributes = false
      ActiveRecord::Base.default_timezone = :local
      Time.zone = nil
    end
905
  end
906

D
Initial  
David Heinemeier Hansson 已提交
907
  def test_multiparameter_attributes_on_time_with_empty_seconds
J
Jeremy Kemper 已提交
908 909
    attributes = {
      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
D
Initial  
David Heinemeier Hansson 已提交
910 911 912 913 914 915 916
      "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

917 918 919 920 921 922 923
  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
924

925
  def test_attributes_on_dummy_time
926 927
    # Oracle, and Sybase do not have a TIME datatype.
    return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
928

929 930 931 932 933 934 935 936
    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 已提交
937
  def test_boolean
938 939
    b_nil = Booleantest.create({ "value" => nil })
    nil_id = b_nil.id
D
Initial  
David Heinemeier Hansson 已提交
940 941 942 943 944
    b_false = Booleantest.create({ "value" => false })
    false_id = b_false.id
    b_true = Booleantest.create({ "value" => true })
    true_id = b_true.id

945 946
    b_nil = Booleantest.find(nil_id)
    assert_nil b_nil.value
D
Initial  
David Heinemeier Hansson 已提交
947 948 949 950 951
    b_false = Booleantest.find(false_id)
    assert !b_false.value?
    b_true = Booleantest.find(true_id)
    assert b_true.value?
  end
952 953

  def test_boolean_cast_from_string
954 955
    b_blank = Booleantest.create({ "value" => "" })
    blank_id = b_blank.id
D
David Heinemeier Hansson 已提交
956
    b_false = Booleantest.create({ "value" => "0" })
957
    false_id = b_false.id
D
David Heinemeier Hansson 已提交
958
    b_true = Booleantest.create({ "value" => "1" })
959 960
    true_id = b_true.id

961 962
    b_blank = Booleantest.find(blank_id)
    assert_nil b_blank.value
963 964 965
    b_false = Booleantest.find(false_id)
    assert !b_false.value?
    b_true = Booleantest.find(true_id)
J
Jeremy Kemper 已提交
966
    assert b_true.value?
967
  end
J
Jeremy Kemper 已提交
968

969
  def test_new_record_returns_boolean
970 971
    assert_equal true, Topic.new.new_record?
    assert_equal false, Topic.find(1).new_record?
972 973
  end

D
Initial  
David Heinemeier Hansson 已提交
974 975
  def test_clone
    topic = Topic.find(1)
976 977
    cloned_topic = nil
    assert_nothing_raised { cloned_topic = topic.clone }
D
Initial  
David Heinemeier Hansson 已提交
978
    assert_equal topic.title, cloned_topic.title
979
    assert cloned_topic.new_record?
D
Initial  
David Heinemeier Hansson 已提交
980 981

    # test if the attributes have been cloned
J
Jeremy Kemper 已提交
982 983
    topic.title = "a"
    cloned_topic.title = "b"
D
Initial  
David Heinemeier Hansson 已提交
984 985 986 987 988 989
    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 已提交
990
    cloned_topic.title["a"] = "c"
D
Initial  
David Heinemeier Hansson 已提交
991
    assert_equal "b", topic.title["a"]
992

993
    # test if attributes set as part of after_initialize are cloned correctly
994 995 996
    assert_equal topic.author_email_address, cloned_topic.author_email_address

    # test if saved clone object differs from original
997
    cloned_topic.save
998
    assert !cloned_topic.new_record?
999
    assert_not_equal cloned_topic.id, topic.id
D
Initial  
David Heinemeier Hansson 已提交
1000
  end
1001 1002 1003 1004 1005 1006 1007 1008 1009

  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
1010
    assert clone.new_record?
1011 1012 1013 1014 1015 1016 1017

    # 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
1018
    assert !clone.new_record?
1019
    assert_not_equal clone.id, dev.id
1020 1021
  end

1022 1023 1024 1025 1026 1027 1028 1029
  def test_clone_does_not_clone_associations
    author = authors(:david)
    assert_not_equal [], author.posts

    author_clone = author.clone
    assert_equal [], author_clone.posts
  end

1030 1031 1032 1033 1034 1035
  def test_clone_preserves_subtype
    clone = nil
    assert_nothing_raised { clone = Company.find(3).clone }
    assert_kind_of Client, clone
  end

1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
  def test_clone_of_new_object_with_defaults
    developer = Developer.new
    assert !developer.name_changed?
    assert !developer.salary_changed?

    cloned_developer = developer.clone
    assert !cloned_developer.name_changed?
    assert !cloned_developer.salary_changed?
  end

  def test_clone_of_new_object_marks_attributes_as_dirty
    developer = Developer.new :name => 'Bjorn', :salary => 100000
    assert developer.name_changed?
    assert developer.salary_changed?

    cloned_developer = developer.clone
    assert cloned_developer.name_changed?
    assert cloned_developer.salary_changed?
  end

  def test_clone_of_new_object_marks_as_dirty_only_changed_attributes
    developer = Developer.new :name => 'Bjorn'
    assert developer.name_changed?            # obviously
    assert !developer.salary_changed?         # attribute has non-nil default value, so treated as not changed

    cloned_developer = developer.clone
    assert cloned_developer.name_changed?
    assert !cloned_developer.salary_changed?  # ... and cloned instance should behave same
  end

  def test_clone_of_saved_object_marks_attributes_as_dirty
    developer = Developer.create! :name => 'Bjorn', :salary => 100000
    assert !developer.name_changed?
    assert !developer.salary_changed?

    cloned_developer = developer.clone
    assert cloned_developer.name_changed?     # both attributes differ from defaults
    assert cloned_developer.salary_changed?
  end

  def test_clone_of_saved_object_marks_as_dirty_only_changed_attributes
    developer = Developer.create! :name => 'Bjorn'
    assert !developer.name_changed?           # both attributes of saved object should be threated as not changed
    assert !developer.salary_changed?

    cloned_developer = developer.clone
    assert cloned_developer.name_changed?     # ... but on cloned object should be
    assert !cloned_developer.salary_changed?  # ... BUT salary has non-nil default which should be threated as not changed on cloned instance
  end

D
Initial  
David Heinemeier Hansson 已提交
1086 1087 1088 1089 1090 1091 1092 1093
  def test_bignum
    company = Company.find(1)
    company.rating = 2147483647
    company.save
    company = Company.find(1)
    assert_equal 2147483647, company.rating
  end

1094
  # TODO: extend defaults tests to other databases!
1095
  if current_adapter?(:PostgreSQLAdapter)
1096
    def test_default
D
Initial  
David Heinemeier Hansson 已提交
1097
      default = Default.new
J
Jeremy Kemper 已提交
1098

D
Initial  
David Heinemeier Hansson 已提交
1099 1100 1101
      # 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 已提交
1102

D
Initial  
David Heinemeier Hansson 已提交
1103 1104 1105 1106 1107
      # char types
      assert_equal 'Y', default.char1
      assert_equal 'a varchar field', default.char2
      assert_equal 'a text field', default.char3
    end
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124

    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 已提交
1125

1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
      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 已提交
1143

1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
      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 已提交
1158

1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
      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 已提交
1170 1171
  end

1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
  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 已提交
1204 1205 1206
  def test_auto_id
    auto = AutoId.new
    auto.save
1207
    assert(auto.id > 0)
D
Initial  
David Heinemeier Hansson 已提交
1208
  end
1209

D
Initial  
David Heinemeier Hansson 已提交
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
  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

1223
  def test_sql_injection_via_find
1224
    assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1225 1226 1227 1228
      Topic.find("123456 OR id > 0")
    end
  end

D
Initial  
David Heinemeier Hansson 已提交
1229 1230 1231
  def test_column_name_properly_quoted
    col_record = ColumnName.new
    col_record.references = 40
1232
    assert col_record.save
D
Initial  
David Heinemeier Hansson 已提交
1233
    col_record.references = 41
1234 1235
    assert col_record.save
    assert_not_nil c2 = ColumnName.find(col_record.id)
D
Initial  
David Heinemeier Hansson 已提交
1236 1237 1238
    assert_equal(41, c2.references)
  end

1239
  def test_quoting_arrays
1240
    replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
1241 1242
    assert_equal topics(:first).replies.size, replies.size

1243
    replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
1244 1245 1246
    assert_equal 0, replies.size
  end

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

D
Initial  
David Heinemeier Hansson 已提交
1249 1250
  def test_serialized_attribute
    myobj = MyObject.new('value1', 'value2')
J
Jeremy Kemper 已提交
1251
    topic = Topic.create("content" => myobj)
D
Initial  
David Heinemeier Hansson 已提交
1252 1253 1254 1255
    Topic.serialize("content", MyObject)
    assert_equal(myobj, topic.content)
  end

1256 1257 1258 1259 1260
  def test_serialized_time_attribute
    myobj = Time.local(2008,1,1,1,0)
    topic = Topic.create("content" => myobj).reload
    assert_equal(myobj, topic.content)
  end
1261

1262 1263 1264 1265 1266
  def test_serialized_string_attribute
    myobj = "Yes"
    topic = Topic.create("content" => myobj).reload
    assert_equal(myobj, topic.content)
  end
1267

1268
  def test_nil_serialized_attribute_with_class_constraint
D
Initial  
David Heinemeier Hansson 已提交
1269
    myobj = MyObject.new('value1', 'value2')
1270 1271 1272
    topic = Topic.new
    assert_nil topic.content
  end
D
Initial  
David Heinemeier Hansson 已提交
1273

1274 1275 1276 1277 1278
  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)
1279
    assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
1280 1281 1282
  ensure
    Topic.serialize(:content)
  end
D
Initial  
David Heinemeier Hansson 已提交
1283

1284
  def test_serialized_attribute_with_class_constraint
D
Initial  
David Heinemeier Hansson 已提交
1285
    settings = { "color" => "blue" }
1286 1287 1288
    Topic.serialize(:content, Hash)
    topic = Topic.new(:content => settings)
    assert topic.save
D
Initial  
David Heinemeier Hansson 已提交
1289
    assert_equal(settings, Topic.find(topic.id).content)
1290
  ensure
D
Initial  
David Heinemeier Hansson 已提交
1291 1292 1293 1294
    Topic.serialize(:content)
  end

  def test_quote
1295 1296 1297
    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 已提交
1298
  end
1299 1300 1301

  if RUBY_VERSION < '1.9'
    def test_quote_chars
1302 1303 1304 1305
      with_kcode('UTF8') do
        str = 'The Narrator'
        topic = Topic.create(:author_name => str)
        assert_equal str, topic.author_name
1306

1307 1308
        assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
        topic = Topic.find_by_author_name(str.mb_chars)
1309

1310 1311 1312
        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
1313
    end
1314
  end
1315

1316
  def test_increment_attribute
1317 1318
    assert_equal 50, accounts(:signals37).credit_limit
    accounts(:signals37).increment! :credit_limit
J
Jeremy Kemper 已提交
1319
    assert_equal 51, accounts(:signals37, :reload).credit_limit
1320 1321 1322

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

1325
  def test_increment_nil_attribute
1326 1327 1328
    assert_nil topics(:first).parent_id
    topics(:first).increment! :parent_id
    assert_equal 1, topics(:first).parent_id
1329
  end
J
Jeremy Kemper 已提交
1330

1331 1332 1333
  def test_increment_attribute_by
    assert_equal 50, accounts(:signals37).credit_limit
    accounts(:signals37).increment! :credit_limit, 5
J
Jeremy Kemper 已提交
1334
    assert_equal 55, accounts(:signals37, :reload).credit_limit
1335 1336 1337 1338

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

1340
  def test_decrement_attribute
1341
    assert_equal 50, accounts(:signals37).credit_limit
1342

1343 1344
    accounts(:signals37).decrement!(:credit_limit)
    assert_equal 49, accounts(:signals37, :reload).credit_limit
J
Jeremy Kemper 已提交
1345

1346 1347
    accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
    assert_equal 47, accounts(:signals37, :reload).credit_limit
1348
  end
J
Jeremy Kemper 已提交
1349

1350 1351 1352
  def test_decrement_attribute_by
    assert_equal 50, accounts(:signals37).credit_limit
    accounts(:signals37).decrement! :credit_limit, 5
J
Jeremy Kemper 已提交
1353
    assert_equal 45, accounts(:signals37, :reload).credit_limit
1354 1355 1356 1357

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

1359
  def test_toggle_attribute
1360 1361 1362
    assert !topics(:first).approved?
    topics(:first).toggle!(:approved)
    assert topics(:first).approved?
1363 1364 1365 1366 1367
    topic = topics(:first)
    topic.toggle(:approved)
    assert !topic.approved?
    topic.reload
    assert topic.approved?
1368
  end
1369 1370 1371 1372 1373 1374 1375 1376 1377

  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
1378

1379 1380 1381 1382 1383 1384
  def test_reload_with_exclusive_scope
    dev = DeveloperCalledDavid.first
    dev.update_attributes!( :name => "NotDavid" )
    assert_equal dev, dev.reload
  end

1385 1386
  def test_define_attr_method_with_value
    k = Class.new( ActiveRecord::Base )
1387
    k.send(:define_attr_method, :table_name, "foo")
1388 1389 1390 1391 1392
    assert_equal "foo", k.table_name
  end

  def test_define_attr_method_with_block
    k = Class.new( ActiveRecord::Base )
1393
    k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404
    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

1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416
  def test_quoted_table_name_after_set_table_name
    klass = Class.new(ActiveRecord::Base)

    klass.set_table_name "foo"
    assert_equal "foo", klass.table_name
    assert_equal klass.connection.quote_table_name("foo"), klass.quoted_table_name

    klass.set_table_name "bar"
    assert_equal "bar", klass.table_name
    assert_equal klass.connection.quote_table_name("bar"), klass.quoted_table_name
  end

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
  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
1450 1451

  def test_count_with_join
1452
    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 已提交
1453

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

1457
    res3 = nil
1458 1459 1460 1461 1462
    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 已提交
1463

1464
    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"
1465 1466
    res5 = nil
    assert_nothing_raised do
1467 1468
      res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
                        :joins => "p, comments co",
1469 1470 1471
                        :select => "p.id")
    end

J
Jeremy Kemper 已提交
1472
    assert_equal res4, res5
1473

P
Pratik Naik 已提交
1474 1475 1476 1477 1478 1479 1480
    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)
1481
    end
P
Pratik Naik 已提交
1482
    assert_equal res6, res7
1483
  end
J
Jeremy Kemper 已提交
1484 1485

  def test_clear_association_cache_stored
1486 1487 1488 1489 1490 1491
    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 已提交
1492

1493 1494 1495 1496 1497 1498
  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 ]
1499

1500
     firm.clients    << clients
1501
     assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1502 1503

     firm.clear_association_cache
1504
     assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1505
  end
1506

1507 1508 1509 1510 1511 1512
  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

1513
  def test_scoped_find_conditions
1514
    scoped_developers = Developer.send(:with_scope, :find => { :conditions => 'salary > 90000' }) do
1515 1516
      Developer.find(:all, :conditions => 'id < 5')
    end
M
Marcel Molina 已提交
1517 1518
    assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
    assert_equal 3, scoped_developers.size
1519
  end
J
Jeremy Kemper 已提交
1520

1521
  def test_scoped_find_limit_offset
1522
    scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :offset => 2 }) do
1523
      Developer.find(:all, :order => 'id')
J
Jeremy Kemper 已提交
1524
    end
M
Marcel Molina 已提交
1525 1526 1527
    assert !scoped_developers.include?(developers(:david))
    assert !scoped_developers.include?(developers(:jamis))
    assert_equal 3, scoped_developers.size
J
Jeremy Kemper 已提交
1528

1529
    # Test without scoped find conditions to ensure we get the whole thing
1530
    developers = Developer.find(:all, :order => 'id')
M
Marcel Molina 已提交
1531
    assert_equal Developer.count, developers.size
1532
  end
1533

J
Jeremy Kemper 已提交
1534 1535
  def test_scoped_find_order
    # Test order in scope
1536
    scoped_developers = Developer.send(:with_scope, :find => { :limit => 1, :order => 'salary DESC' }) do
1537
      Developer.find(:all)
J
Jeremy Kemper 已提交
1538
    end
1539 1540 1541
    assert_equal 'Jamis', scoped_developers.first.name
    assert scoped_developers.include?(developers(:jamis))
    # Test scope without order and order in find
1542
    scoped_developers = Developer.send(:with_scope, :find => { :limit => 1 }) do
1543
      Developer.find(:all, :order => 'salary DESC')
J
Jeremy Kemper 已提交
1544
    end
1545
    # Test scope order + find order, find has priority
1546
    scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :order => 'id DESC' }) do
1547 1548 1549 1550
      Developer.find(:all, :order => 'salary ASC')
    end
    assert scoped_developers.include?(developers(:poor_jamis))
    assert scoped_developers.include?(developers(:david))
1551 1552 1553
    assert ! scoped_developers.include?(developers(:jamis))
    assert_equal 3, scoped_developers.size

1554 1555 1556 1557 1558
    # 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

1559
  def test_scoped_find_limit_offset_including_has_many_association
1560
    topics = Topic.send(:with_scope, :find => {:limit => 1, :offset => 1, :include => :replies}) do
1561 1562 1563 1564 1565 1566
      Topic.find(:all, :order => "topics.id")
    end
    assert_equal 1, topics.size
    assert_equal 2, topics.first.id
  end

1567
  def test_scoped_find_order_including_has_many_association
1568
    developers = Developer.send(:with_scope, :find => { :order => 'developers.salary DESC', :include => :projects }) do
1569 1570 1571 1572 1573 1574 1575 1576
      Developer.find(:all)
    end
    assert developers.size >= 2
    for i in 1...developers.size
      assert developers[i-1].salary >= developers[i].salary
    end
  end

1577
  def test_scoped_find_with_group_and_having
1578
    developers = Developer.send(:with_scope, :find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
1579 1580 1581 1582 1583
      Developer.find(:all)
    end
    assert_equal 3, developers.size
  end

1584 1585 1586 1587
  def test_find_last
    last  = Developer.find :last
    assert_equal last, Developer.find(:first, :order => 'id desc')
  end
1588

1589 1590 1591
  def test_last
    assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
  end
1592

1593 1594 1595 1596 1597 1598
  def test_all
    developers = Developer.all
    assert_kind_of Array, developers
    assert_equal Developer.find(:all), developers
  end

1599
  def test_all_with_conditions
1600
    assert_equal Developer.find(:all, :order => 'id desc'), Developer.order('id desc').all
1601
  end
1602

1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616
  def test_find_ordered_last
    last  = Developer.find :last, :order => 'developers.salary ASC'
    assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
  end

  def test_find_reverse_ordered_last
    last  = Developer.find :last, :order => 'developers.salary DESC'
    assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
  end

  def test_find_multiple_ordered_last
    last  = Developer.find :last, :order => 'developers.name, developers.salary DESC'
    assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
  end
1617

1618 1619 1620 1621 1622 1623 1624 1625 1626 1627
  def test_find_keeps_multiple_order_values
    combined = Developer.find(:all, :order => 'developers.name, developers.salary')
    assert_equal combined, Developer.find(:all, :order => ['developers.name', 'developers.salary'])
  end

  def test_find_keeps_multiple_group_values
    combined = Developer.find(:all, :group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at')
    assert_equal combined, Developer.find(:all, :group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at'])
  end

1628 1629 1630 1631 1632
  def test_find_symbol_ordered_last
    last  = Developer.find :last, :order => :salary
    assert_equal last, Developer.find(:all, :order => :salary).last
  end

1633
  def test_find_scoped_ordered_last
1634
    last_developer = Developer.send(:with_scope, :find => { :order => 'developers.salary ASC' }) do
1635 1636 1637 1638
      Developer.find(:last)
    end
    assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
  end
1639

1640
  def test_abstract_class
1641
    assert !ActiveRecord::Base.abstract_class?
1642 1643
    assert LoosePerson.abstract_class?
    assert !LooseDescendant.abstract_class?
1644 1645 1646
  end

  def test_base_class
1647 1648 1649 1650
    assert_equal LoosePerson,     LoosePerson.base_class
    assert_equal LooseDescendant, LooseDescendant.base_class
    assert_equal TightPerson,     TightPerson.base_class
    assert_equal TightPerson,     TightDescendant.base_class
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683

    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.
1684
    assert !SubStiPost.descends_from_active_record?
1685 1686 1687 1688 1689 1690
  end

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

1691
    descendant = old_class.create! :first_name => 'bob'
1692 1693 1694 1695 1696
    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
1697 1698
  end

1699 1700 1701 1702 1703 1704 1705
  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

1706
  def test_to_xml
1707
    xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
1708 1709
    bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
    written_on_in_current_timezone = topics(:first).written_on.xmlschema
1710
    last_read_in_current_timezone = topics(:first).last_read.xmlschema
1711 1712 1713 1714

    assert_equal "topic", xml.root.name
    assert_equal "The First Topic" , xml.elements["//title"].text
    assert_equal "David" , xml.elements["//author-name"].text
1715
    assert_match "Have a nice day", xml.elements["//content"].text
1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731

    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 "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']

1732
    if current_adapter?(:SybaseAdapter)
1733 1734
      assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
      assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
1735
    else
1736
      # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
1737 1738
      assert_equal "2004-04-15", xml.elements["//last-read"].text
      assert_equal "date" , xml.elements["//last-read"].attributes['type']
1739
    end
1740

1741
    # Oracle and DB2 don't have true boolean or time-only fields
1742
    unless current_adapter?(:OracleAdapter, :DB2Adapter)
1743 1744 1745 1746 1747
      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']
1748
    end
1749
  end
1750

1751
  def test_to_xml_skipping_attributes
1752
    xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
1753
    assert_equal "<topic>", xml.first(7)
1754
    assert !xml.include?(%(<title>The First Topic</title>))
J
Jeremy Kemper 已提交
1755
    assert xml.include?(%(<author-name>David</author-name>))
1756

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

1762
  def test_to_xml_including_has_many_association
1763
    xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
1764
    assert_equal "<topic>", xml.first(7)
1765
    assert xml.include?(%(<replies type="array"><reply>))
1766
    assert xml.include?(%(<title>The Second Topic of the day</title>))
1767
  end
1768 1769 1770

  def test_array_to_xml_including_has_many_association
    xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
1771
    assert xml.include?(%(<replies type="array"><reply>))
1772
  end
1773 1774 1775

  def test_array_to_xml_including_methods
    xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
1776 1777
    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
1778
  end
J
Jeremy Kemper 已提交
1779

1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791
  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
1792 1793 1794 1795 1796 1797 1798 1799

  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 已提交
1800

1801 1802 1803 1804
  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>))
1805
    assert xml.include?(%(<clients type="array"><client>))
1806
  end
1807 1808 1809

  def test_to_xml_including_multiple_associations_with_options
    xml = companies(:first_firm).to_xml(
J
Jeremy Kemper 已提交
1810
      :indent  => 0, :skip_instruct => true,
1811 1812
      :include => { :clients => { :only => :name } }
    )
J
Jeremy Kemper 已提交
1813

1814
    assert_equal "<firm>", xml.first(6)
1815
    assert xml.include?(%(<client><name>Summit</name></client>))
1816
    assert xml.include?(%(<clients type="array"><client>))
1817
  end
J
Jeremy Kemper 已提交
1818

1819 1820 1821 1822 1823
  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 已提交
1824

1825 1826
  def test_to_xml_with_block
    value = "Rockin' the block"
1827 1828
    xml = Company.new.to_xml(:skip_instruct => true) do |_xml|
      _xml.tag! "arbitrary-element", value
1829 1830 1831 1832
    end
    assert_equal "<company>", xml.first(9)
    assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
  end
J
Jeremy Kemper 已提交
1833

1834 1835 1836
  def test_to_param_should_return_string
    assert_kind_of String, Client.find(:first).to_param
  end
J
Jeremy Kemper 已提交
1837

1838 1839 1840 1841 1842 1843 1844
  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
1845
    topic = topics(:first)
1846
    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, parent_title: nil, type: nil, group: nil>), topic.inspect
1847
  end
1848

1849
  def test_inspect_new_instance
1850
    assert_match(/Topic id: nil/, Topic.new.inspect)
1851 1852
  end

1853 1854 1855 1856
  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 已提交
1857

1858 1859 1860
  def test_inspect_class_without_table
    assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
  end
1861

1862 1863
  def test_attribute_for_inspect
    t = topics(:first)
1864
    t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
1865 1866

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

1870 1871 1872 1873
  def test_becomes
    assert_kind_of Reply, topics(:first).becomes(Reply)
    assert_equal "The First Topic", topics(:first).becomes(Reply).title
  end
1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905

  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
J
José Valim 已提交
1906 1907 1908
    ActiveRecord::Base.benchmark("Debug Topic Count", :level => :debug) { Topic.count }
    ActiveRecord::Base.benchmark("Warn Topic Count",  :level => :warn)  { Topic.count }
    ActiveRecord::Base.benchmark("Error Topic Count", :level => :error) { Topic.count }
1909 1910 1911
    assert_no_match(/Debug Topic Count/, log.string)
    assert_match(/Warn Topic Count/, log.string)
    assert_match(/Error Topic Count/, log.string)
1912 1913 1914 1915 1916 1917 1918 1919
  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)
J
José Valim 已提交
1920 1921
    ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => true) { ActiveRecord::Base.logger.debug "Loud" }
    ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false)  { ActiveRecord::Base.logger.debug "Quiet" }
1922 1923
    assert_no_match(/Loud/, log.string)
    assert_match(/Quiet/, log.string)
1924 1925 1926
  ensure
    ActiveRecord::Base.logger = original_logger
  end
1927

1928 1929 1930
  def test_dup
    assert !Minimalistic.new.freeze.dup.frozen?
  end
1931

1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948
  def test_compute_type_success
    assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author')
  end

  def test_compute_type_nonexistent_constant
    assert_raises NameError do
      ActiveRecord::Base.send :compute_type, 'NonexistentModel'
    end
  end

  def test_compute_type_no_method_error
    String.any_instance.stubs(:constantize).raises(NoMethodError)
    assert_raises NoMethodError do
      ActiveRecord::Base.send :compute_type, 'InvalidModel'
    end
  end

1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962
  protected
    def with_env_tz(new_tz = 'US/Eastern')
      old_tz, ENV['TZ'] = ENV['TZ'], new_tz
      yield
    ensure
      old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
    end

    def with_active_record_default_timezone(zone)
      old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
      yield
    ensure
      ActiveRecord::Base.default_timezone = old_zone
    end
1963
end