base_test.rb 53.7 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
require 'models/company'
require 'models/customer'
require 'models/developer'
require 'models/project'
require 'models/default'
require 'models/auto_id'
13
require 'models/boolean'
J
Jeremy Kemper 已提交
14 15 16
require 'models/column_name'
require 'models/subscriber'
require 'models/keyboard'
17
require 'models/comment'
J
Jeremy Kemper 已提交
18 19
require 'models/minimalistic'
require 'models/warehouse_thing'
20
require 'models/parrot'
21
require 'models/loose_person'
22
require 'models/edge'
23
require 'rexml/document'
J
Jeremy Kemper 已提交
24
require 'active_support/core_ext/exception'
D
Initial  
David Heinemeier Hansson 已提交
25 26

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

43 44 45 46
class ReadonlyTitlePost < Post
  attr_readonly :title
end

47
class Boolean < ActiveRecord::Base; end
D
Initial  
David Heinemeier Hansson 已提交
48

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

52 53 54 55
  def test_primary_key_with_no_id
    assert_nil Edge.primary_key
  end

56 57
  def test_select_symbol
    topic_ids = Topic.select(:id).map(&:id).sort
58
    assert_equal Topic.find(:all).map(&:id).sort, topic_ids
59 60
  end

61 62 63 64
  def test_table_exists
    assert !NonExistentTable.table_exists?
    assert Topic.table_exists?
  end
J
Jeremy Kemper 已提交
65

D
Initial  
David Heinemeier Hansson 已提交
66
  def test_preserving_date_objects
67
    if current_adapter?(:SybaseAdapter)
68 69
      # Sybase ctlib does not (yet?) support the date type; use datetime instead.
      assert_kind_of(
J
Jeremy Kemper 已提交
70
        Time, Topic.find(1).last_read,
71 72 73
        "The last_read attribute should be of the Time class"
      )
    else
74
      # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
75
      assert_kind_of(
J
Jeremy Kemper 已提交
76
        Date, Topic.find(1).last_read,
77 78 79
        "The last_read attribute should be of the Date class"
      )
    end
80
  end
81

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
  def test_use_table_engine_for_quoting_where
    relation = Topic.where(Topic.arel_table[:id].eq(1))
    engine = relation.table.engine

    fakepool = Class.new(Struct.new(:spec)) {
      def with_connection; yield self; end
      def connection_pool; self; end
      def quote_table_name(*args); raise "lol quote_table_name"; end
    }

    relation.table.engine = fakepool.new(engine.connection_pool.spec)

    error = assert_raises(RuntimeError) { relation.to_a }
    assert_match('lol', error.message)
  ensure
    relation.table.engine = engine
  end

100
  def test_preserving_time_objects
101 102 103 104
    assert_kind_of(
      Time, Topic.find(1).bonus_time,
      "The bonus_time attribute should be of the Time class"
    )
D
Initial  
David Heinemeier Hansson 已提交
105 106 107 108 109

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

    # For adapters which support microsecond resolution.
112
    if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLiteAdapter)
113 114
      assert_equal 11, Topic.find(1).written_on.sec
      assert_equal 223300, Topic.find(1).written_on.usec
115
      assert_equal 9900, Topic.find(2).written_on.usec
116
    end
D
Initial  
David Heinemeier Hansson 已提交
117
  end
J
Jeremy Kemper 已提交
118

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
  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

173 174 175 176 177 178
  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 已提交
179

D
Initial  
David Heinemeier Hansson 已提交
180
  def test_initialize_with_attributes
J
Jeremy Kemper 已提交
181
    topic = Topic.new({
D
Initial  
David Heinemeier Hansson 已提交
182 183
      "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
    })
J
Jeremy Kemper 已提交
184

D
Initial  
David Heinemeier Hansson 已提交
185 186
    assert_equal("initialized from attributes", topic.title)
  end
J
Jeremy Kemper 已提交
187

188 189
  def test_initialize_with_invalid_attribute
    begin
A
Aaron Patterson 已提交
190
      Topic.new({ "title" => "test",
191 192 193 194 195 196
        "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 已提交
197

D
Initial  
David Heinemeier Hansson 已提交
198
  def test_load
J
Jeremy Kemper 已提交
199
    topics = Topic.find(:all, :order => 'id')
200
    assert_equal(4, topics.size)
201
    assert_equal(topics(:first).title, topics.first.title)
D
Initial  
David Heinemeier Hansson 已提交
202
  end
J
Jeremy Kemper 已提交
203

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

D
Initial  
David Heinemeier Hansson 已提交
207
    assert_equal(1, topics.size)
208
    assert_equal(topics(:second).title, topics.first.title)
D
Initial  
David Heinemeier Hansson 已提交
209 210
  end

211
  GUESSED_CLASSES = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
212

213
  def test_table_name_guesses
D
Initial  
David Heinemeier Hansson 已提交
214
    assert_equal "topics", Topic.table_name
215

D
Initial  
David Heinemeier Hansson 已提交
216 217 218
    assert_equal "categories", Category.table_name
    assert_equal "smarts", Smarts.table_name
    assert_equal "credit_cards", CreditCard.table_name
219
    assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
220 221 222
    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 已提交
223
    assert_equal "master_credit_cards", MasterCreditCard.table_name
224 225 226
  ensure
    GUESSED_CLASSES.each(&:reset_table_name)
  end
D
Initial  
David Heinemeier Hansson 已提交
227

228
  def test_singular_table_name_guesses
D
Initial  
David Heinemeier Hansson 已提交
229
    ActiveRecord::Base.pluralize_table_names = false
230
    GUESSED_CLASSES.each(&:reset_table_name)
231

D
Initial  
David Heinemeier Hansson 已提交
232 233 234
    assert_equal "category", Category.table_name
    assert_equal "smarts", Smarts.table_name
    assert_equal "credit_card", CreditCard.table_name
235
    assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
236 237 238
    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 已提交
239
    assert_equal "master_credit_card", MasterCreditCard.table_name
240
  ensure
D
Initial  
David Heinemeier Hansson 已提交
241
    ActiveRecord::Base.pluralize_table_names = true
242 243
    GUESSED_CLASSES.each(&:reset_table_name)
  end
D
Initial  
David Heinemeier Hansson 已提交
244

245
  def test_table_name_guesses_with_prefixes_and_suffixes
D
Initial  
David Heinemeier Hansson 已提交
246
    ActiveRecord::Base.table_name_prefix = "test_"
247
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
248 249
    assert_equal "test_categories", Category.table_name
    ActiveRecord::Base.table_name_suffix = "_test"
250
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
251 252
    assert_equal "test_categories_test", Category.table_name
    ActiveRecord::Base.table_name_prefix = ""
253
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
254 255
    assert_equal "categories_test", Category.table_name
    ActiveRecord::Base.table_name_suffix = ""
256
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
257
    assert_equal "categories", Category.table_name
258 259 260 261 262
  ensure
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""
    GUESSED_CLASSES.each(&:reset_table_name)
  end
D
Initial  
David Heinemeier Hansson 已提交
263

264
  def test_singular_table_name_guesses_with_prefixes_and_suffixes
D
Initial  
David Heinemeier Hansson 已提交
265
    ActiveRecord::Base.pluralize_table_names = false
266

D
Initial  
David Heinemeier Hansson 已提交
267
    ActiveRecord::Base.table_name_prefix = "test_"
268
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
269 270
    assert_equal "test_category", Category.table_name
    ActiveRecord::Base.table_name_suffix = "_test"
271
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
272 273
    assert_equal "test_category_test", Category.table_name
    ActiveRecord::Base.table_name_prefix = ""
274
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
275 276
    assert_equal "category_test", Category.table_name
    ActiveRecord::Base.table_name_suffix = ""
277
    Category.reset_table_name
D
Initial  
David Heinemeier Hansson 已提交
278
    assert_equal "category", Category.table_name
279
  ensure
D
Initial  
David Heinemeier Hansson 已提交
280
    ActiveRecord::Base.pluralize_table_names = true
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
    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 已提交
313
  end
J
Jeremy Kemper 已提交
314 315


B
Brian Lopez 已提交
316
  if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
317 318 319 320 321
    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

D
Initial  
David Heinemeier Hansson 已提交
322 323 324 325
  def test_null_fields
    assert_nil Topic.find(1).parent_id
    assert_nil Topic.create("title" => "Hey you").parent_id
  end
J
Jeremy Kemper 已提交
326

D
Initial  
David Heinemeier Hansson 已提交
327 328
  def test_default_values
    topic = Topic.new
J
Jeremy Kemper 已提交
329
    assert topic.approved?
D
Initial  
David Heinemeier Hansson 已提交
330
    assert_nil topic.written_on
331
    assert_nil topic.bonus_time
D
Initial  
David Heinemeier Hansson 已提交
332
    assert_nil topic.last_read
J
Jeremy Kemper 已提交
333

D
Initial  
David Heinemeier Hansson 已提交
334 335 336
    topic.save

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

J
Jeremy Kemper 已提交
340
    # Oracle has some funky default handling, so it requires a bit of
341
    # extra testing. See ticket #2788.
342 343
    if current_adapter?(:OracleAdapter)
      test = TestOracleDefault.new
344 345 346 347
      assert_equal "X", test.test_char
      assert_equal "hello", test.test_string
      assert_equal 3, test.test_int
    end
D
Initial  
David Heinemeier Hansson 已提交
348
  end
349

350 351
  # Oracle, and Sybase do not have a TIME datatype.
  unless current_adapter?(:OracleAdapter, :SybaseAdapter)
352 353 354 355 356 357 358 359
    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
360

361 362 363 364 365 366 367 368 369 370 371 372
    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
373 374
  end

D
Initial  
David Heinemeier Hansson 已提交
375 376 377 378 379 380 381 382 383
  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
384 385 386 387 388 389 390

    # 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 已提交
391
  end
392

D
Initial  
David Heinemeier Hansson 已提交
393
  def test_equality
394
    assert_equal Topic.find(1), Topic.find(2).topic
D
Initial  
David Heinemeier Hansson 已提交
395
  end
J
Jeremy Kemper 已提交
396

397 398 399 400
  def test_find_by_slug
    assert_equal Topic.find('1-meowmeow'), Topic.find(1)
  end

401 402 403
  def test_equality_of_new_records
    assert_not_equal Topic.new, Topic.new
  end
J
Jeremy Kemper 已提交
404

405 406 407 408 409 410 411 412 413
  def test_equality_of_destroyed_records
    topic_1 = Topic.new(:title => 'test_1')
    topic_1.save
    topic_2 = Topic.find(topic_1.id)
    topic_1.destroy
    assert_equal topic_1, topic_2
    assert_equal topic_2, topic_1
  end

D
Initial  
David Heinemeier Hansson 已提交
414
  def test_hashing
415
    assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
D
Initial  
David Heinemeier Hansson 已提交
416
  end
J
Jeremy Kemper 已提交
417

418
  def test_readonly_attributes
419
    assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
J
Jeremy Kemper 已提交
420

421 422 423
    post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
    post.reload
    assert_equal "cannot change this", post.title
J
Jeremy Kemper 已提交
424

425 426 427 428 429
    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 已提交
430 431 432 433 434

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

440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
  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 已提交
459 460 461
    attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
    topic = Topic.find(1)
    topic.attributes = attributes
J
Jeremy Kemper 已提交
462
    # note that extra #to_date call allows test to pass for Oracle, which
463
    # treats dates/times the same
464
    assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
D
Initial  
David Heinemeier Hansson 已提交
465 466
  end

467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
  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 已提交
494 495 496 497 498 499 500 501
  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 已提交
502 503
    attributes = {
      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
D
Initial  
David Heinemeier Hansson 已提交
504 505 506 507 508 509
      "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
510

511 512 513 514 515 516 517 518 519 520
  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 已提交
521

522 523 524 525 526 527 528 529 530 531 532 533 534
  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

535 536 537
  def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
    ActiveRecord::Base.time_zone_aware_attributes = true
    ActiveRecord::Base.default_timezone = :utc
538
    Time.zone = ActiveSupport::TimeZone[-28800]
539 540 541 542 543 544 545 546 547 548 549 550 551
    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
552
  end
553

554 555
  def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
    ActiveRecord::Base.time_zone_aware_attributes = false
556
    Time.zone = ActiveSupport::TimeZone[-28800]
557 558 559 560 561 562 563 564 565 566 567 568
    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

569 570 571
  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
572
    Time.zone = ActiveSupport::TimeZone[-28800]
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
    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
588

589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
  # 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
608
  end
609

D
Initial  
David Heinemeier Hansson 已提交
610
  def test_multiparameter_attributes_on_time_with_empty_seconds
J
Jeremy Kemper 已提交
611 612
    attributes = {
      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
D
Initial  
David Heinemeier Hansson 已提交
613 614 615 616 617 618 619
      "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

620 621 622 623 624 625 626
  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
627

628
  def test_attributes_on_dummy_time
629 630
    # Oracle, and Sybase do not have a TIME datatype.
    return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
631

632 633 634 635 636 637 638 639
    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 已提交
640
  def test_boolean
641
    b_nil = Boolean.create({ "value" => nil })
642
    nil_id = b_nil.id
643
    b_false = Boolean.create({ "value" => false })
D
Initial  
David Heinemeier Hansson 已提交
644
    false_id = b_false.id
645
    b_true = Boolean.create({ "value" => true })
D
Initial  
David Heinemeier Hansson 已提交
646 647
    true_id = b_true.id

648
    b_nil = Boolean.find(nil_id)
649
    assert_nil b_nil.value
650
    b_false = Boolean.find(false_id)
D
Initial  
David Heinemeier Hansson 已提交
651
    assert !b_false.value?
652
    b_true = Boolean.find(true_id)
D
Initial  
David Heinemeier Hansson 已提交
653 654
    assert b_true.value?
  end
655 656

  def test_boolean_cast_from_string
657
    b_blank = Boolean.create({ "value" => "" })
658
    blank_id = b_blank.id
659
    b_false = Boolean.create({ "value" => "0" })
660
    false_id = b_false.id
661
    b_true = Boolean.create({ "value" => "1" })
662 663
    true_id = b_true.id

664
    b_blank = Boolean.find(blank_id)
665
    assert_nil b_blank.value
666
    b_false = Boolean.find(false_id)
667
    assert !b_false.value?
668
    b_true = Boolean.find(true_id)
J
Jeremy Kemper 已提交
669
    assert b_true.value?
670
  end
J
Jeremy Kemper 已提交
671

672
  def test_new_record_returns_boolean
673 674
    assert_equal false, Topic.new.persisted?
    assert_equal true, Topic.find(1).persisted?
675 676
  end

A
Aaron Patterson 已提交
677
  def test_dup
D
Initial  
David Heinemeier Hansson 已提交
678
    topic = Topic.find(1)
A
Aaron Patterson 已提交
679 680 681 682
    duped_topic = nil
    assert_nothing_raised { duped_topic = topic.dup }
    assert_equal topic.title, duped_topic.title
    assert !duped_topic.persisted?
D
Initial  
David Heinemeier Hansson 已提交
683

A
Aaron Patterson 已提交
684
    # test if the attributes have been duped
J
Jeremy Kemper 已提交
685
    topic.title = "a"
A
Aaron Patterson 已提交
686
    duped_topic.title = "b"
D
Initial  
David Heinemeier Hansson 已提交
687
    assert_equal "a", topic.title
A
Aaron Patterson 已提交
688
    assert_equal "b", duped_topic.title
D
Initial  
David Heinemeier Hansson 已提交
689

A
Aaron Patterson 已提交
690
    # test if the attribute values have been duped
D
Initial  
David Heinemeier Hansson 已提交
691
    topic.title = {"a" => "b"}
A
Aaron Patterson 已提交
692 693
    duped_topic = topic.dup
    duped_topic.title["a"] = "c"
D
Initial  
David Heinemeier Hansson 已提交
694
    assert_equal "b", topic.title["a"]
695

A
Aaron Patterson 已提交
696 697
    # test if attributes set as part of after_initialize are duped correctly
    assert_equal topic.author_email_address, duped_topic.author_email_address
698 699

    # test if saved clone object differs from original
A
Aaron Patterson 已提交
700 701 702
    duped_topic.save
    assert duped_topic.persisted?
    assert_not_equal duped_topic.id, topic.id
703

A
Aaron Patterson 已提交
704
    duped_topic.reload
705
    # FIXME: I think this is poor behavior, and will fix it with #5686
P
Pratik Naik 已提交
706
    assert_equal({'a' => 'c'}.to_yaml, duped_topic.title)
D
Initial  
David Heinemeier Hansson 已提交
707
  end
708

A
Aaron Patterson 已提交
709
  def test_dup_with_aggregate_of_same_name_as_attribute
710 711 712
    dev = DeveloperWithAggregate.find(1)
    assert_kind_of DeveloperSalary, dev.salary

A
Aaron Patterson 已提交
713 714 715 716 717
    dup = nil
    assert_nothing_raised { dup = dev.dup }
    assert_kind_of DeveloperSalary, dup.salary
    assert_equal dev.salary.amount, dup.salary.amount
    assert !dup.persisted?
718

A
Aaron Patterson 已提交
719 720
    # test if the attributes have been dupd
    original_amount = dup.salary.amount
721
    dev.salary.amount = 1
A
Aaron Patterson 已提交
722
    assert_equal original_amount, dup.salary.amount
723

A
Aaron Patterson 已提交
724 725 726
    assert dup.save
    assert dup.persisted?
    assert_not_equal dup.id, dev.id
727 728
  end

A
Aaron Patterson 已提交
729
  def test_dup_does_not_copy_associations
730 731
    author = authors(:david)
    assert_not_equal [], author.posts
A
Aaron Patterson 已提交
732
    author.send(:clear_association_cache)
733

A
Aaron Patterson 已提交
734 735
    author_dup = author.dup
    assert_equal [], author_dup.posts
736 737
  end

738 739 740 741 742 743
  def test_clone_preserves_subtype
    clone = nil
    assert_nothing_raised { clone = Company.find(3).clone }
    assert_kind_of Client, clone
  end

744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
  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

A
Aaron Patterson 已提交
774
  def test_dup_of_saved_object_marks_attributes_as_dirty
775 776 777 778
    developer = Developer.create! :name => 'Bjorn', :salary => 100000
    assert !developer.name_changed?
    assert !developer.salary_changed?

A
Aaron Patterson 已提交
779
    cloned_developer = developer.dup
780 781 782 783
    assert cloned_developer.name_changed?     # both attributes differ from defaults
    assert cloned_developer.salary_changed?
  end

A
Aaron Patterson 已提交
784
  def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes
785 786 787 788
    developer = Developer.create! :name => 'Bjorn'
    assert !developer.name_changed?           # both attributes of saved object should be threated as not changed
    assert !developer.salary_changed?

A
Aaron Patterson 已提交
789
    cloned_developer = developer.dup
790 791 792 793
    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 已提交
794 795 796 797 798 799 800 801
  def test_bignum
    company = Company.find(1)
    company.rating = 2147483647
    company.save
    company = Company.find(1)
    assert_equal 2147483647, company.rating
  end

802
  # TODO: extend defaults tests to other databases!
803
  if current_adapter?(:PostgreSQLAdapter)
804
    def test_default
D
Initial  
David Heinemeier Hansson 已提交
805
      default = Default.new
J
Jeremy Kemper 已提交
806

D
Initial  
David Heinemeier Hansson 已提交
807 808 809
      # 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 已提交
810

D
Initial  
David Heinemeier Hansson 已提交
811 812 813 814 815
      # char types
      assert_equal 'Y', default.char1
      assert_equal 'a varchar field', default.char2
      assert_equal 'a text field', default.char3
    end
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832

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

834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
      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 已提交
851

852 853 854 855 856 857 858 859 860 861 862 863 864 865
      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 已提交
866

867 868 869 870 871 872 873 874 875 876 877
      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 已提交
878 879
  end

880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
  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 已提交
912 913 914
  def test_auto_id
    auto = AutoId.new
    auto.save
915
    assert(auto.id > 0)
D
Initial  
David Heinemeier Hansson 已提交
916
  end
917

D
Initial  
David Heinemeier Hansson 已提交
918 919 920 921 922 923 924 925 926 927 928 929 930
  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

931
  def test_sql_injection_via_find
932
    assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
933 934 935 936
      Topic.find("123456 OR id > 0")
    end
  end

D
Initial  
David Heinemeier Hansson 已提交
937 938 939
  def test_column_name_properly_quoted
    col_record = ColumnName.new
    col_record.references = 40
940
    assert col_record.save
D
Initial  
David Heinemeier Hansson 已提交
941
    col_record.references = 41
942 943
    assert col_record.save
    assert_not_nil c2 = ColumnName.find(col_record.id)
D
Initial  
David Heinemeier Hansson 已提交
944 945 946
    assert_equal(41, c2.references)
  end

947
  def test_quoting_arrays
948
    replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
949 950
    assert_equal topics(:first).replies.size, replies.size

951
    replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
952 953 954
    assert_equal 0, replies.size
  end

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

D
Initial  
David Heinemeier Hansson 已提交
957
  def test_serialized_attribute
958 959
    Topic.serialize("content", MyObject)

D
Initial  
David Heinemeier Hansson 已提交
960
    myobj = MyObject.new('value1', 'value2')
J
Jeremy Kemper 已提交
961
    topic = Topic.create("content" => myobj)
962 963 964
    assert_equal(myobj, topic.content)

    topic.reload
D
Initial  
David Heinemeier Hansson 已提交
965 966 967
    assert_equal(myobj, topic.content)
  end

968 969 970 971 972
  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
973

974 975 976 977 978
  def test_serialized_string_attribute
    myobj = "Yes"
    topic = Topic.create("content" => myobj).reload
    assert_equal(myobj, topic.content)
  end
979

980 981 982 983
  def test_nil_serialized_attribute_with_class_constraint
    topic = Topic.new
    assert_nil topic.content
  end
D
Initial  
David Heinemeier Hansson 已提交
984

985 986 987 988 989
  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)
990
    assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
991 992 993
  ensure
    Topic.serialize(:content)
  end
D
Initial  
David Heinemeier Hansson 已提交
994

995
  def test_serialized_attribute_with_class_constraint
D
Initial  
David Heinemeier Hansson 已提交
996
    settings = { "color" => "blue" }
997 998 999
    Topic.serialize(:content, Hash)
    topic = Topic.new(:content => settings)
    assert topic.save
D
Initial  
David Heinemeier Hansson 已提交
1000
    assert_equal(settings, Topic.find(topic.id).content)
1001
  ensure
D
Initial  
David Heinemeier Hansson 已提交
1002 1003 1004
    Topic.serialize(:content)
  end

1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
  def test_serialized_default_class
    Topic.serialize(:content, Hash)
    topic = Topic.new
    assert_equal Hash, topic.content.class
    assert_equal Hash, topic.read_attribute(:content).class
    topic.content["beer"] = "MadridRb"
    assert topic.save
    topic.reload
    assert_equal Hash, topic.content.class
    assert_equal "MadridRb", topic.content["beer"]
  ensure
    Topic.serialize(:content)
  end

  def test_serialized_no_default_class_for_object
    topic = Topic.new
    assert_nil topic.content
  end

1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
  def test_serialized_boolean_value_true
    Topic.serialize(:content)
    topic = Topic.new(:content => true)
    assert topic.save
    topic = topic.reload
    assert_equal topic.content, true
  end

  def test_serialized_boolean_value_false
    Topic.serialize(:content)
    topic = Topic.new(:content => false)
    assert topic.save
    topic = topic.reload
    assert_equal topic.content, false
  end

1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
  def test_serialize_with_coder
    coder = Class.new {
      # Identity
      def load(thing)
        thing
      end

      # base 64
      def dump(thing)
        [thing].pack('m')
      end
    }.new

    Topic.serialize(:content, coder)
    s = 'hello world'
    topic = Topic.new(:content => s)
    assert topic.save
    topic = topic.reload
    assert_equal [s].pack('m'), topic.content
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
  ensure
    Topic.serialize(:content)
  end

  def test_serialize_with_bcrypt_coder
    crypt_coder = Class.new {
      def load(thing)
        return unless thing
        BCrypt::Password.new thing
      end

      def dump(thing)
        BCrypt::Password.create(thing).to_s
      end
    }.new

    Topic.serialize(:content, crypt_coder)
    password = 'password'
    topic = Topic.new(:content => password)
    assert topic.save
    topic = topic.reload
    assert_kind_of BCrypt::Password, topic.content
    assert_equal(true, topic.content == password, 'password should equal')
  ensure
    Topic.serialize(:content)
1084 1085
  end

D
Initial  
David Heinemeier Hansson 已提交
1086
  def test_quote
1087 1088 1089
    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 已提交
1090
  end
1091 1092 1093

  if RUBY_VERSION < '1.9'
    def test_quote_chars
1094 1095 1096 1097
      with_kcode('UTF8') do
        str = 'The Narrator'
        topic = Topic.create(:author_name => str)
        assert_equal str, topic.author_name
1098

1099 1100
        assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
        topic = Topic.find_by_author_name(str.mb_chars)
1101

1102 1103 1104
        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
1105
    end
1106
  end
1107

1108
  def test_toggle_attribute
1109 1110 1111
    assert !topics(:first).approved?
    topics(:first).toggle!(:approved)
    assert topics(:first).approved?
1112 1113 1114 1115 1116
    topic = topics(:first)
    topic.toggle(:approved)
    assert !topic.approved?
    topic.reload
    assert topic.approved?
1117
  end
1118 1119 1120 1121 1122 1123 1124 1125 1126

  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
1127

1128 1129 1130 1131 1132 1133
  def test_reload_with_exclusive_scope
    dev = DeveloperCalledDavid.first
    dev.update_attributes!( :name => "NotDavid" )
    assert_equal dev, dev.reload
  end

1134 1135
  def test_define_attr_method_with_value
    k = Class.new( ActiveRecord::Base )
1136
    k.send(:define_attr_method, :table_name, "foo")
1137 1138 1139 1140
    assert_equal "foo", k.table_name
  end

  def test_define_attr_method_with_block
1141 1142 1143 1144 1145 1146 1147 1148
    k = Class.new( ActiveRecord::Base ) do
      class << self
        attr_accessor :foo_key
      end
    end
    k.foo_key = "id"
    k.send(:define_attr_method, :foo_key) { "sys_" + original_foo_key }
    assert_equal "sys_id", k.foo_key
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
  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

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

1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
  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 )
1187
    k.primary_key = 'id'
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
    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
1205 1206

  def test_count_with_join
1207
    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 已提交
1208

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

1212
    res3 = nil
1213 1214 1215 1216 1217
    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 已提交
1218

1219
    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"
1220 1221
    res5 = nil
    assert_nothing_raised do
1222 1223
      res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
                        :joins => "p, comments co",
1224 1225 1226
                        :select => "p.id")
    end

J
Jeremy Kemper 已提交
1227
    assert_equal res4, res5
1228

P
Pratik Naik 已提交
1229 1230 1231 1232 1233 1234 1235
    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)
1236
    end
P
Pratik Naik 已提交
1237
    assert_equal res6, res7
1238
  end
J
Jeremy Kemper 已提交
1239

1240 1241 1242 1243 1244 1245
  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

1246
  def test_scoped_find_conditions
1247
    scoped_developers = Developer.send(:with_scope, :find => { :conditions => 'salary > 90000' }) do
1248 1249
      Developer.find(:all, :conditions => 'id < 5')
    end
M
Marcel Molina 已提交
1250 1251
    assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
    assert_equal 3, scoped_developers.size
1252
  end
J
Jeremy Kemper 已提交
1253

1254 1255 1256 1257 1258 1259
  def test_no_limit_offset
    assert_nothing_raised do
      Developer.find(:all, :offset => 2)
    end
  end

1260
  def test_scoped_find_limit_offset
1261
    scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :offset => 2 }) do
1262
      Developer.find(:all, :order => 'id')
J
Jeremy Kemper 已提交
1263
    end
M
Marcel Molina 已提交
1264 1265 1266
    assert !scoped_developers.include?(developers(:david))
    assert !scoped_developers.include?(developers(:jamis))
    assert_equal 3, scoped_developers.size
J
Jeremy Kemper 已提交
1267

1268
    # Test without scoped find conditions to ensure we get the whole thing
1269
    developers = Developer.find(:all, :order => 'id')
M
Marcel Molina 已提交
1270
    assert_equal Developer.count, developers.size
1271
  end
1272

J
Jeremy Kemper 已提交
1273 1274
  def test_scoped_find_order
    # Test order in scope
1275
    scoped_developers = Developer.send(:with_scope, :find => { :limit => 1, :order => 'salary DESC' }) do
1276
      Developer.find(:all)
J
Jeremy Kemper 已提交
1277
    end
1278 1279 1280
    assert_equal 'Jamis', scoped_developers.first.name
    assert scoped_developers.include?(developers(:jamis))
    # Test scope without order and order in find
1281
    scoped_developers = Developer.send(:with_scope, :find => { :limit => 1 }) do
1282
      Developer.find(:all, :order => 'salary DESC')
J
Jeremy Kemper 已提交
1283
    end
1284
    # Test scope order + find order, order has priority
1285
    scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :order => 'id DESC' }) do
1286 1287 1288
      Developer.find(:all, :order => 'salary ASC')
    end
    assert scoped_developers.include?(developers(:poor_jamis))
1289
    assert ! scoped_developers.include?(developers(:david))
1290 1291 1292
    assert ! scoped_developers.include?(developers(:jamis))
    assert_equal 3, scoped_developers.size

1293
    # Test without scoped find conditions to ensure we get the right thing
1294 1295
    assert ! scoped_developers.include?(Developer.find(1))
    assert scoped_developers.include?(Developer.find(11))
1296 1297
  end

1298
  def test_scoped_find_limit_offset_including_has_many_association
1299
    topics = Topic.send(:with_scope, :find => {:limit => 1, :offset => 1, :include => :replies}) do
1300 1301 1302 1303 1304 1305
      Topic.find(:all, :order => "topics.id")
    end
    assert_equal 1, topics.size
    assert_equal 2, topics.first.id
  end

1306
  def test_scoped_find_order_including_has_many_association
1307
    developers = Developer.send(:with_scope, :find => { :order => 'developers.salary DESC', :include => :projects }) do
1308 1309 1310 1311 1312 1313 1314 1315
      Developer.find(:all)
    end
    assert developers.size >= 2
    for i in 1...developers.size
      assert developers[i-1].salary >= developers[i].salary
    end
  end

1316
  def test_scoped_find_with_group_and_having
1317
    developers = Developer.send(:with_scope, :find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
1318 1319 1320 1321 1322
      Developer.find(:all)
    end
    assert_equal 3, developers.size
  end

1323 1324 1325 1326
  def test_find_last
    last  = Developer.find :last
    assert_equal last, Developer.find(:first, :order => 'id desc')
  end
1327

1328 1329 1330
  def test_last
    assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
  end
1331

1332 1333 1334 1335 1336 1337
  def test_all
    developers = Developer.all
    assert_kind_of Array, developers
    assert_equal Developer.find(:all), developers
  end

1338
  def test_all_with_conditions
1339
    assert_equal Developer.find(:all, :order => 'id desc'), Developer.order('id desc').all
1340
  end
1341

1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
  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
1356

1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
  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

1367 1368 1369 1370 1371
  def test_find_symbol_ordered_last
    last  = Developer.find :last, :order => :salary
    assert_equal last, Developer.find(:all, :order => :salary).last
  end

1372
  def test_find_scoped_ordered_last
1373
    last_developer = Developer.send(:with_scope, :find => { :order => 'developers.salary ASC' }) do
1374 1375 1376 1377
      Developer.find(:last)
    end
    assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
  end
1378

1379
  def test_abstract_class
1380
    assert !ActiveRecord::Base.abstract_class?
1381 1382
    assert LoosePerson.abstract_class?
    assert !LooseDescendant.abstract_class?
1383 1384 1385
  end

  def test_base_class
1386 1387 1388 1389
    assert_equal LoosePerson,     LoosePerson.base_class
    assert_equal LooseDescendant, LooseDescendant.base_class
    assert_equal TightPerson,     TightPerson.base_class
    assert_equal TightPerson,     TightDescendant.base_class
1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422

    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.
1423
    assert !SubStiPost.descends_from_active_record?
1424 1425 1426 1427 1428 1429
  end

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

1430
    descendant = old_class.create! :first_name => 'bob'
1431 1432 1433 1434 1435
    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
1436 1437
  end

1438 1439 1440 1441 1442 1443 1444
  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

1445 1446 1447
  def test_to_param_should_return_string
    assert_kind_of String, Client.find(:first).to_param
  end
J
Jeremy Kemper 已提交
1448

1449 1450 1451 1452 1453 1454 1455
  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
1456
    topic = topics(:first)
P
Pratik Naik 已提交
1457
    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, created_at: "#{topic.created_at.to_s(:db)}", updated_at: "#{topic.updated_at.to_s(:db)}">), topic.inspect
1458
  end
1459

1460
  def test_inspect_new_instance
1461
    assert_match(/Topic id: nil/, Topic.new.inspect)
1462 1463
  end

1464 1465 1466 1467
  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 已提交
1468

1469 1470 1471
  def test_inspect_class_without_table
    assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
  end
1472

1473 1474
  def test_attribute_for_inspect
    t = topics(:first)
1475
    t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
1476 1477

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

1481 1482 1483 1484
  def test_becomes
    assert_kind_of Reply, topics(:first).becomes(Reply)
    assert_equal "The First Topic", topics(:first).becomes(Reply).title
  end
1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516

  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 已提交
1517 1518 1519
    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 }
1520 1521 1522
    assert_no_match(/Debug Topic Count/, log.string)
    assert_match(/Warn Topic Count/, log.string)
    assert_match(/Error Topic Count/, log.string)
1523 1524 1525 1526 1527 1528 1529 1530
  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 已提交
1531 1532
    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" }
1533 1534
    assert_no_match(/Loud/, log.string)
    assert_match(/Quiet/, log.string)
1535 1536 1537
  ensure
    ActiveRecord::Base.logger = original_logger
  end
1538

1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549
  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
1550
    ActiveSupport::Dependencies.stubs(:constantize).raises(NoMethodError)
1551 1552 1553 1554 1555
    assert_raises NoMethodError do
      ActiveRecord::Base.send :compute_type, 'InvalidModel'
    end
  end

1556 1557 1558 1559 1560 1561
  def test_default_scope_is_reset
    Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
    UnloadablePost.table_name = 'posts'
    UnloadablePost.class_eval do
      default_scope order('posts.comments_count ASC')
    end
1562
    UnloadablePost.scoped_methods # make Thread.current[:UnloadablePost_scoped_methods] not nil
1563 1564 1565 1566 1567 1568 1569 1570

    UnloadablePost.unloadable
    assert_not_nil Thread.current[:UnloadablePost_scoped_methods]
    ActiveSupport::Dependencies.remove_unloadable_constants!
    assert_nil Thread.current[:UnloadablePost_scoped_methods]
  ensure
    Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
  end
J
Jon Leighton 已提交
1571 1572 1573 1574 1575 1576 1577

  def test_marshal_round_trip
    expected = posts(:welcome)
    actual   = Marshal.load(Marshal.dump(expected))

    assert_equal expected.attributes, actual.attributes
  end
1578
end