persistence_test.rb 30.6 KB
Newer Older
1
require "cases/helper"
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
require "models/aircraft"
require "models/post"
require "models/comment"
require "models/author"
require "models/topic"
require "models/reply"
require "models/category"
require "models/company"
require "models/developer"
require "models/computer"
require "models/project"
require "models/minimalistic"
require "models/warehouse_thing"
require "models/parrot"
require "models/minivan"
require "models/owner"
require "models/person"
require "models/pet"
require "models/ship"
require "models/toy"
require "models/admin"
require "models/admin/user"
require "rexml/document"
25

V
Vipul A M 已提交
26
class PersistenceTest < ActiveRecord::TestCase
27
  fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, "warehouse-things", :authors, :author_addresses, :categorizations, :categories, :posts, :minivans, :pets, :toys
28

29 30 31 32 33 34 35 36 37
  # 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
    end

38 39 40 41
    def test_update_all_doesnt_ignore_order
      assert_equal authors(:david).id + 1, authors(:mary).id # make sure there is going to be a duplicate PK error
      test_update_with_order_succeeds = lambda do |order|
        begin
42
          Author.order(order).update_all("id = id + 1")
43 44 45 46 47
        rescue ActiveRecord::ActiveRecordError
          false
        end
      end

48 49
      if test_update_with_order_succeeds.call("id DESC")
        assert !test_update_with_order_succeeds.call("id ASC") # test that this wasn't a fluke and using an incorrect order results in an exception
50 51 52
      else
        # test that we're failing because the current Arel's engine doesn't support UPDATE ORDER BY queries is using subselects instead
        assert_sql(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC\)\Z/i) do
53
          test_update_with_order_succeeds.call("id DESC")
54 55 56 57
        end
      end
    end

58 59 60 61
    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
62
        assert_equal 2, author.posts_sorted_by_id_limited.limit(2).to_a.size
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
        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
    end
  end

  def test_update_many
    topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
    updated = Topic.update(topic_data.keys, topic_data.values)

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

  def test_delete_all
    assert Topic.count > 0

    assert_equal Topic.count, Topic.delete_all
  end

85
  def test_delete_all_with_joins_and_where_part_is_hash
86
    where_args = { toys: { name: "Bone" } }
87 88 89 90 91 92 93
    count = Pet.joins(:toys).where(where_args).count

    assert_equal count, 1
    assert_equal count, Pet.joins(:toys).where(where_args).delete_all
  end

  def test_delete_all_with_joins_and_where_part_is_not_hash
94
    where_args = ["toys.name = ?", "Bone"]
95 96 97 98 99 100
    count = Pet.joins(:toys).where(where_args).count

    assert_equal count, 1
    assert_equal count, Pet.joins(:toys).where(where_args).delete_all
  end

101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
  def test_increment_attribute
    assert_equal 50, accounts(:signals37).credit_limit
    accounts(:signals37).increment! :credit_limit
    assert_equal 51, accounts(:signals37, :reload).credit_limit

    accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
    assert_equal 53, accounts(:signals37, :reload).credit_limit
  end

  def test_increment_nil_attribute
    assert_nil topics(:first).parent_id
    topics(:first).increment! :parent_id
    assert_equal 1, topics(:first).parent_id
  end

  def test_increment_attribute_by
    assert_equal 50, accounts(:signals37).credit_limit
    accounts(:signals37).increment! :credit_limit, 5
    assert_equal 55, accounts(:signals37, :reload).credit_limit

    accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3)
    assert_equal 59, accounts(:signals37, :reload).credit_limit
  end

125 126 127 128 129 130 131 132 133
  def test_increment_updates_counter_in_db_using_offset
    a1 = accounts(:signals37)
    initial_credit = a1.credit_limit
    a2 = Account.find(accounts(:signals37).id)
    a1.increment!(:credit_limit)
    a2.increment!(:credit_limit)
    assert_equal initial_credit + 2, a1.reload.credit_limit
  end

134 135
  def test_destroy_all
    conditions = "author_name = 'Mary'"
136
    topics_by_mary = Topic.all.merge!(where: conditions, order: "id").to_a
137 138
    assert ! topics_by_mary.empty?

139
    assert_difference("Topic.count", -topics_by_mary.size) do
140
      destroyed = Topic.where(conditions).destroy_all.sort_by(&:id)
141
      assert_equal topics_by_mary, destroyed
142
      assert destroyed.all?(&:frozen?), "destroyed topics should be frozen"
143 144 145 146
    end
  end

  def test_destroy_many
147
    clients = Client.all.merge!(order: "id").find([2, 3])
148

149
    assert_difference("Client.count", -2) do
150 151
      destroyed = Client.destroy([2, 3]).sort_by(&:id)
      assert_equal clients, destroyed
152
      assert destroyed.all?(&:frozen?), "destroyed clients should be frozen"
153 154 155
    end
  end

156 157 158 159 160 161
  def test_becomes
    assert_kind_of Reply, topics(:first).becomes(Reply)
    assert_equal "The First Topic", topics(:first).becomes(Reply).title
  end

  def test_becomes_includes_errors
162
    company = Company.new(name: nil)
163 164 165
    assert !company.valid?
    original_errors = company.errors
    client = company.becomes(Client)
166 167 168 169 170 171 172
    assert_equal original_errors.keys, client.errors.keys
  end

  def test_becomes_errors_base
    child_class = Class.new(Admin::User) do
      store_accessor :settings, :foo

173
      def self.name; "Admin::ChildUser"; end
174 175 176 177 178 179 180 181 182 183
    end

    admin = Admin::User.new
    admin.errors.add :token, :invalid
    child = admin.becomes(child_class)

    assert_equal [:token], child.errors.keys
    assert_nothing_raised do
      child.errors.add :foo, :invalid
    end
184 185
  end

A
Akira Matsuda 已提交
186
  def test_duped_becomes_persists_changes_from_the_original
187 188 189 190 191 192 193 194 195 196 197 198 199
    original = topics(:first)
    copy = original.dup.becomes(Reply)
    copy.save!
    assert_equal "The First Topic", Topic.find(copy.id).title
  end

  def test_becomes_includes_changed_attributes
    company = Company.new(name: "37signals")
    client = company.becomes(Client)
    assert_equal "37signals", client.name
    assert_equal %w{name}, client.changed
  end

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
  def test_delete_many
    original_count = Topic.count
    Topic.delete(deleting = [1, 2])
    assert_equal original_count - deleting.size, Topic.count
  end

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

    accounts(:signals37).decrement!(:credit_limit)
    assert_equal 49, accounts(:signals37, :reload).credit_limit

    accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
    assert_equal 47, accounts(:signals37, :reload).credit_limit
  end

  def test_decrement_attribute_by
    assert_equal 50, accounts(:signals37).credit_limit
    accounts(:signals37).decrement! :credit_limit, 5
    assert_equal 45, accounts(:signals37, :reload).credit_limit

    accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3)
    assert_equal 41, accounts(:signals37, :reload).credit_limit
  end

225 226 227 228 229 230 231 232 233
  def test_create
    topic = Topic.new
    topic.title = "New Topic"
    topic.save
    topic_reloaded = Topic.find(topic.id)
    assert_equal("New Topic", topic_reloaded.title)
  end

  def test_save!
234
    topic = Topic.new(title: "New Topic")
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
    assert topic.save!

    reply = WrongReply.new
    assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
  end

  def test_save_null_string_attributes
    topic = Topic.find(1)
    topic.attributes = { "title" => "null", "author_name" => "null" }
    topic.save!
    topic.reload
    assert_equal("null", topic.title)
    assert_equal("null", topic.author_name)
  end

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

  def test_save_for_record_with_only_primary_key
    minimalistic = Minimalistic.new
    assert_nothing_raised { minimalistic.save }
  end

  def test_save_for_record_with_only_primary_key_that_is_provided
264
    assert_nothing_raised { Minimalistic.create!(id: 2) }
265 266
  end

267
  def test_save_with_duping_of_destroyed_object
268
    developer = Developer.first
269 270 271 272
    developer.destroy
    new_developer = developer.dup
    new_developer.save
    assert new_developer.persisted?
273
    assert_not new_developer.destroyed?
274 275
  end

276 277 278 279 280 281 282
  def test_create_many
    topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
    assert_equal 2, topics.size
    assert_equal "first", topics.first.title
  end

  def test_create_columns_not_equal_attributes
283
    topic = Topic.instantiate(
284 285 286
      "attributes" => {
        "title"          => "Another New Topic",
        "does_not_exist" => "test"
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
    assert_nothing_raised { topic.save }
  end

  def test_create_through_factory_with_block
    topic = Topic.create("title" => "New Topic") do |t|
      t.author_name = "David"
    end
    assert_equal("New Topic", topic.title)
    assert_equal("David", topic.author_name)
  end

  def test_create_many_through_factory_with_block
    topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t|
      t.author_name = "David"
    end
    assert_equal 2, topics.size
    topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id)
    assert_equal "first", topic1.title
    assert_equal "David", topic1.author_name
    assert_equal "second", topic2.title
    assert_equal "David", topic2.author_name
  end

312
  def test_update_object
313 314 315 316
    topic = Topic.new
    topic.title = "Another New Topic"
    topic.written_on = "2003-12-12 23:23:00"
    topic.save
317 318
    topic_reloaded = Topic.find(topic.id)
    assert_equal("Another New Topic", topic_reloaded.title)
319

320 321
    topic_reloaded.title = "Updated topic"
    topic_reloaded.save
322

323
    topic_reloaded_again = Topic.find(topic.id)
324

325
    assert_equal("Updated topic", topic_reloaded_again.title)
326 327 328 329 330 331 332
  end

  def test_update_columns_not_equal_attributes
    topic = Topic.new
    topic.title = "Still another topic"
    topic.save

333 334
    topic_reloaded = Topic.instantiate(topic.attributes.merge("does_not_exist" => "test"))
    topic_reloaded.title = "A New Topic"
335
    assert_nothing_raised { topic_reloaded.save }
336 337 338 339 340 341 342
  end

  def test_update_for_record_with_only_primary_key
    minimalistic = minimalistics(:first)
    assert_nothing_raised { minimalistic.save }
  end

343 344 345
  def test_update_sti_type
    assert_instance_of Reply, topics(:second)

346
    topic = topics(:second).becomes!(Topic)
347 348 349 350 351
    assert_instance_of Topic, topic
    topic.save!
    assert_instance_of Topic, Topic.find(topic.id)
  end

352 353 354 355 356 357 358 359 360 361 362
  def test_preserve_original_sti_type
    reply = topics(:second)
    assert_equal "Reply", reply.type

    topic = reply.becomes(Topic)
    assert_equal "Reply", reply.type

    assert_instance_of Topic, topic
    assert_equal "Reply", topic.type
  end

E
Edo Balvers 已提交
363 364 365 366 367 368 369 370 371
  def test_update_sti_subclass_type
    assert_instance_of Topic, topics(:first)

    reply = topics(:first).becomes!(Reply)
    assert_instance_of Reply, reply
    reply.save!
    assert_instance_of Reply, Reply.find(reply.id)
  end

372 373
  def test_update_after_create
    klass = Class.new(Topic) do
374
      def self.name; "Topic"; end
375 376 377 378 379 380 381 382
      after_create do
        update_attribute("author_name", "David")
      end
    end
    topic = klass.new
    topic.title = "Another New Topic"
    topic.save

383 384 385
    topic_reloaded = Topic.find(topic.id)
    assert_equal("Another New Topic", topic_reloaded.title)
    assert_equal("David", topic_reloaded.author_name)
386 387
  end

388 389
  def test_update_attribute_does_not_run_sql_if_attribute_is_not_changed
    klass = Class.new(Topic) do
390
      def self.name; "Topic"; end
391
    end
392
    topic = klass.create(title: "Another New Topic")
393
    assert_queries(0) do
394
      assert topic.update_attribute(:title, "Another New Topic")
395 396 397
    end
  end

Y
Yves Senn 已提交
398
  def test_update_does_not_run_sql_if_record_has_not_changed
399
    topic = Topic.create(title: "Another New Topic")
400 401
    assert_queries(0) { assert topic.update(title: "Another New Topic") }
    assert_queries(0) { assert topic.update_attributes(title: "Another New Topic") }
Y
Yves Senn 已提交
402 403
  end

404 405
  def test_delete
    topic = Topic.find(1)
406 407
    assert_equal topic, topic.delete, "topic.delete did not return self"
    assert topic.frozen?, "topic not frozen after delete"
408 409 410 411 412 413 414 415 416 417
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
  end

  def test_delete_doesnt_run_callbacks
    Topic.find(1).delete
    assert_not_nil Topic.find(2)
  end

  def test_destroy
    topic = Topic.find(1)
418 419
    assert_equal topic, topic.destroy, "topic.destroy did not return self"
    assert topic.frozen?, "topic not frozen after destroy"
420 421 422
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
  end

423 424
  def test_destroy!
    topic = Topic.find(1)
425 426
    assert_equal topic, topic.destroy!, "topic.destroy! did not return self"
    assert topic.frozen?, "topic not frozen after destroy!"
427 428 429
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
  end

430
  def test_record_not_found_exception
A
Aaron Patterson 已提交
431
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(99999) }
432 433 434 435 436 437 438
  end

  def test_update_all
    assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'")
    assert_equal "bulk updated!", Topic.find(1).content
    assert_equal "bulk updated!", Topic.find(2).content

439
    assert_equal Topic.count, Topic.update_all(["content = ?", "bulk updated again!"])
440 441 442
    assert_equal "bulk updated again!", Topic.find(1).content
    assert_equal "bulk updated again!", Topic.find(2).content

443
    assert_equal Topic.count, Topic.update_all(["content = ?", nil])
444 445 446 447 448
    assert_nil Topic.find(1).content
  end

  def test_update_all_with_hash
    assert_not_nil Topic.find(1).last_read
449
    assert_equal Topic.count, Topic.update_all(content: "bulk updated with hash!", last_read: nil)
450 451 452 453 454 455 456
    assert_equal "bulk updated with hash!", Topic.find(1).content
    assert_equal "bulk updated with hash!", Topic.find(2).content
    assert_nil Topic.find(1).last_read
    assert_nil Topic.find(2).last_read
  end

  def test_update_all_with_non_standard_table_name
457
    assert_equal 1, WarehouseThing.where(id: 1).update_all(["value = ?", 0])
458 459 460 461 462 463 464 465 466 467 468 469 470 471
    assert_equal 0, WarehouseThing.find(1).value
  end

  def test_delete_new_record
    client = Client.new
    client.delete
    assert client.frozen?
  end

  def test_delete_record_with_associations
    client = Client.find(3)
    client.delete
    assert client.frozen?
    assert_kind_of Firm, client.firm
472
    assert_raise(RuntimeError) { client.name = "something else" }
473 474 475 476 477 478 479 480 481 482 483 484 485
  end

  def test_destroy_new_record
    client = Client.new
    client.destroy
    assert client.frozen?
  end

  def test_destroy_record_with_associations
    client = Client.find(3)
    client.destroy
    assert client.frozen?
    assert_kind_of Firm, client.firm
486
    assert_raise(RuntimeError) { client.name = "something else" }
487 488
  end

489 490 491 492 493 494 495 496 497 498
  def test_update_attribute
    assert !Topic.find(1).approved?
    Topic.find(1).update_attribute("approved", true)
    assert Topic.find(1).approved?

    Topic.find(1).update_attribute(:approved, false)
    assert !Topic.find(1).approved?
  end

  def test_update_attribute_for_readonly_attribute
499 500
    minivan = Minivan.find("m1")
    assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, "black") }
501 502 503 504
  end

  def test_update_attribute_with_one_updated
    t = Topic.first
505 506
    t.update_attribute(:title, "super_title")
    assert_equal "super_title", t.title
507 508
    assert !t.changed?, "topic should not have changed"
    assert !t.title_changed?, "title should not have changed"
509
    assert_nil t.title_change, "title change should be nil"
510 511

    t.reload
512
    assert_equal "super_title", t.title
513 514 515 516
  end

  def test_update_attribute_for_updated_at_on
    developer = Developer.find(1)
517
    prev_month = Time.now.prev_month.change(usec: 0)
518 519 520 521 522 523 524 525 526 527 528

    developer.update_attribute(:updated_at, prev_month)
    assert_equal prev_month, developer.updated_at

    developer.update_attribute(:salary, 80001)
    assert_not_equal prev_month, developer.updated_at

    developer.reload
    assert_not_equal prev_month, developer.updated_at
  end

529 530
  def test_update_column
    topic = Topic.find(1)
531
    topic.update_column("approved", true)
532 533 534
    assert topic.approved?
    topic.reload
    assert topic.approved?
535 536 537 538 539

    topic.update_column(:approved, false)
    assert !topic.approved?
    topic.reload
    assert !topic.approved?
540 541 542 543 544 545
  end

  def test_update_column_should_not_use_setter_method
    dev = Developer.find(1)
    dev.instance_eval { def salary=(value); write_attribute(:salary, value * 2); end }

546
    dev.update_column(:salary, 80000)
547 548 549 550 551 552 553 554
    assert_equal 80000, dev.salary

    dev.reload
    assert_equal 80000, dev.salary
  end

  def test_update_column_should_raise_exception_if_new_record
    topic = Topic.new
555
    assert_raises(ActiveRecord::ActiveRecordError) { topic.update_column("approved", false) }
556 557 558 559
  end

  def test_update_column_should_not_leave_the_object_dirty
    topic = Topic.find(1)
560
    topic.update_column("content", "--- Have a nice day\n...\n")
561 562

    topic.reload
563
    topic.update_column(:content, "--- You too\n...\n")
564 565 566
    assert_equal [], topic.changed

    topic.reload
567
    topic.update_column("content", "--- Have a nice day\n...\n")
568 569 570 571
    assert_equal [], topic.changed
  end

  def test_update_column_with_model_having_primary_key_other_than_id
572 573
    minivan = Minivan.find("m1")
    new_name = "sebavan"
574

575
    minivan.update_column(:name, new_name)
576 577 578 579
    assert_equal new_name, minivan.name
  end

  def test_update_column_for_readonly_attribute
580
    minivan = Minivan.find("m1")
581
    prev_color = minivan.color
582
    assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_column(:color, "black") }
583 584 585 586 587
    assert_equal prev_color, minivan.color
  end

  def test_update_column_should_not_modify_updated_at
    developer = Developer.find(1)
588
    prev_month = Time.now.prev_month.change(usec: 0)
589

590
    developer.update_column(:updated_at, prev_month)
591 592
    assert_equal prev_month, developer.updated_at

593
    developer.update_column(:salary, 80001)
594 595 596
    assert_equal prev_month, developer.updated_at

    developer.reload
597
    assert_equal prev_month.to_i, developer.updated_at.to_i
598 599 600
  end

  def test_update_column_with_one_changed_and_one_updated
601
    t = Topic.order("id").limit(1).first
602
    author_name = t.author_name
603 604 605 606
    t.author_name = "John"
    t.update_column(:title, "super_title")
    assert_equal "John", t.author_name
    assert_equal "super_title", t.title
607 608 609 610 611
    assert t.changed?, "topic should have changed"
    assert t.author_name_changed?, "author_name should have changed"

    t.reload
    assert_equal author_name, t.author_name
612
    assert_equal "super_title", t.title
613 614
  end

615 616
  def test_update_column_with_default_scope
    developer = DeveloperCalledDavid.first
617
    developer.name = "John"
618 619
    developer.save!

620
    assert developer.update_column(:name, "Will"), "did not update record due to default scope"
621 622
  end

S
Sebastian Martinez 已提交
623 624
  def test_update_columns
    topic = Topic.find(1)
625
    topic.update_columns("approved" => true, title: "Sebastian Topic")
S
Sebastian Martinez 已提交
626 627 628 629 630 631 632
    assert topic.approved?
    assert_equal "Sebastian Topic", topic.title
    topic.reload
    assert topic.approved?
    assert_equal "Sebastian Topic", topic.title
  end

633 634 635 636 637 638 639 640 641 642 643
  def test_update_columns_should_not_use_setter_method
    dev = Developer.find(1)
    dev.instance_eval { def salary=(value); write_attribute(:salary, value * 2); end }

    dev.update_columns(salary: 80000)
    assert_equal 80000, dev.salary

    dev.reload
    assert_equal 80000, dev.salary
  end

S
Sebastian Martinez 已提交
644 645
  def test_update_columns_should_raise_exception_if_new_record
    topic = Topic.new
646
    assert_raises(ActiveRecord::ActiveRecordError) { topic.update_columns(approved: false) }
S
Sebastian Martinez 已提交
647 648 649 650
  end

  def test_update_columns_should_not_leave_the_object_dirty
    topic = Topic.find(1)
651
    topic.update("content" => "--- Have a nice day\n...\n", :author_name => "Jose")
S
Sebastian Martinez 已提交
652 653

    topic.reload
654
    topic.update_columns(content: "--- You too\n...\n", "author_name" => "Sebastian")
S
Sebastian Martinez 已提交
655 656 657
    assert_equal [], topic.changed

    topic.reload
658
    topic.update_columns(content: "--- Have a nice day\n...\n", author_name: "Jose")
S
Sebastian Martinez 已提交
659 660 661
    assert_equal [], topic.changed
  end

662
  def test_update_columns_with_model_having_primary_key_other_than_id
663 664
    minivan = Minivan.find("m1")
    new_name = "sebavan"
665 666 667 668 669

    minivan.update_columns(name: new_name)
    assert_equal new_name, minivan.name
  end

S
Sebastian Martinez 已提交
670
  def test_update_columns_with_one_readonly_attribute
671
    minivan = Minivan.find("m1")
S
Sebastian Martinez 已提交
672 673
    prev_color = minivan.color
    prev_name = minivan.name
674
    assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_columns(name: "My old minivan", color: "black") }
S
Sebastian Martinez 已提交
675 676 677 678 679 680 681 682 683 684
    assert_equal prev_color, minivan.color
    assert_equal prev_name, minivan.name

    minivan.reload
    assert_equal prev_color, minivan.color
    assert_equal prev_name, minivan.name
  end

  def test_update_columns_should_not_modify_updated_at
    developer = Developer.find(1)
685
    prev_month = Time.now.prev_month.change(usec: 0)
S
Sebastian Martinez 已提交
686

687
    developer.update_columns(updated_at: prev_month)
S
Sebastian Martinez 已提交
688 689
    assert_equal prev_month, developer.updated_at

690
    developer.update_columns(salary: 80000)
S
Sebastian Martinez 已提交
691 692 693 694 695 696 697 698
    assert_equal prev_month, developer.updated_at
    assert_equal 80000, developer.salary

    developer.reload
    assert_equal prev_month.to_i, developer.updated_at.to_i
    assert_equal 80000, developer.salary
  end

699
  def test_update_columns_with_one_changed_and_one_updated
700
    t = Topic.order("id").limit(1).first
701
    author_name = t.author_name
702 703 704 705
    t.author_name = "John"
    t.update_columns(title: "super_title")
    assert_equal "John", t.author_name
    assert_equal "super_title", t.title
706 707 708 709 710
    assert t.changed?, "topic should have changed"
    assert t.author_name_changed?, "author_name should have changed"

    t.reload
    assert_equal author_name, t.author_name
711
    assert_equal "super_title", t.title
712 713
  end

714 715 716 717 718 719 720 721 722 723 724 725 726
  def test_update_columns_changing_id
    topic = Topic.find(1)
    topic.update_columns(id: 123)
    assert_equal 123, topic.id
    topic.reload
    assert_equal 123, topic.id
  end

  def test_update_columns_returns_boolean
    topic = Topic.find(1)
    assert_equal true, topic.update_columns(title: "New title")
  end

727 728
  def test_update_columns_with_default_scope
    developer = DeveloperCalledDavid.first
729
    developer.name = "John"
730 731
    developer.save!

732
    assert developer.update_columns(name: "Will"), "did not update record due to default scope"
733 734
  end

735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
  def test_update
    topic = Topic.find(1)
    assert !topic.approved?
    assert_equal "The First Topic", topic.title

    topic.update("approved" => true, "title" => "The First Topic Updated")
    topic.reload
    assert topic.approved?
    assert_equal "The First Topic Updated", topic.title

    topic.update(approved: false, title: "The First Topic")
    topic.reload
    assert !topic.approved?
    assert_equal "The First Topic", topic.title
  end
750

751 752 753 754 755 756 757 758 759 760
  def test_update_attributes
    topic = Topic.find(1)
    assert !topic.approved?
    assert_equal "The First Topic", topic.title

    topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
    topic.reload
    assert topic.approved?
    assert_equal "The First Topic Updated", topic.title

761
    topic.update_attributes(approved: false, title: "The First Topic")
762 763 764
    topic.reload
    assert !topic.approved?
    assert_equal "The First Topic", topic.title
765

766
    error = assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do
767 768
      topic.update_attributes(id: 3, title: "Hm is it possible?")
    end
769
    assert_not_nil error.cause
770 771 772 773 774
    assert_not_equal "Hm is it possible?", Topic.find(3).title

    topic.update_attributes(id: 1234)
    assert_nothing_raised { topic.reload }
    assert_equal topic.title, Topic.find(1234).title
775 776
  end

777 778 779 780 781 782 783 784 785 786 787
  def test_update_attributes_parameters
    topic = Topic.find(1)
    assert_nothing_raised do
      topic.update_attributes({})
    end

    assert_raises(ArgumentError) do
      topic.update_attributes(nil)
    end
  end

788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
  def test_update!
    Reply.validates_presence_of(:title)
    reply = Reply.find(2)
    assert_equal "The Second Topic of the day", reply.title
    assert_equal "Have a nice day", reply.content

    reply.update!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening")
    reply.reload
    assert_equal "The Second Topic of the day updated", reply.title
    assert_equal "Have a nice evening", reply.content

    reply.update!(title: "The Second Topic of the day", content: "Have a nice day")
    reply.reload
    assert_equal "The Second Topic of the day", reply.title
    assert_equal "Have a nice day", reply.content

    assert_raise(ActiveRecord::RecordInvalid) { reply.update!(title: nil, content: "Have a nice evening") }
  ensure
806
    Reply.clear_validators!
807 808
  end

809 810 811 812 813 814 815 816 817 818 819
  def test_update_attributes!
    Reply.validates_presence_of(:title)
    reply = Reply.find(2)
    assert_equal "The Second Topic of the day", reply.title
    assert_equal "Have a nice day", reply.content

    reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening")
    reply.reload
    assert_equal "The Second Topic of the day updated", reply.title
    assert_equal "Have a nice evening", reply.content

820
    reply.update_attributes!(title: "The Second Topic of the day", content: "Have a nice day")
821 822 823 824
    reply.reload
    assert_equal "The Second Topic of the day", reply.title
    assert_equal "Have a nice day", reply.content

825
    assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(title: nil, content: "Have a nice evening") }
826
  ensure
827
    Reply.clear_validators!
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
  end

  def test_destroyed_returns_boolean
    developer = Developer.first
    assert_equal false, developer.destroyed?
    developer.destroy
    assert_equal true, developer.destroyed?

    developer = Developer.last
    assert_equal false, developer.destroyed?
    developer.delete
    assert_equal true, developer.destroyed?
  end

  def test_persisted_returns_boolean
843
    developer = Developer.new(name: "Jose")
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
    assert_equal false, developer.persisted?
    developer.save!
    assert_equal true, developer.persisted?

    developer = Developer.first
    assert_equal true, developer.persisted?
    developer.destroy
    assert_equal false, developer.persisted?

    developer = Developer.last
    assert_equal true, developer.persisted?
    developer.delete
    assert_equal false, developer.persisted?
  end

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

    Topic.destroy(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
    assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
  end

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

    Topic.delete(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
    assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
  end

  def test_create_with_custom_timestamps
    custom_datetime = 1.hour.ago.beginning_of_day

    %w(created_at created_on updated_at updated_on).each do |attribute|
      parrot = LiveParrot.create(:name => "colombian", attribute => custom_datetime)
      assert_equal custom_datetime, parrot[attribute]
    end
  end

886 887 888 889 890 891 892 893 894 895 896 897 898
  def test_persist_inherited_class_with_different_table_name
    minimalistic_aircrafts = Class.new(Minimalistic) do
      self.table_name = "aircraft"
    end

    assert_difference "Aircraft.count", 1 do
      aircraft = minimalistic_aircrafts.create(name: "Wright Flyer")
      aircraft.name = "Wright Glider"
      aircraft.save
    end

    assert_equal "Wright Glider", Aircraft.last.name
  end
899 900 901 902 903 904 905 906 907 908 909

  def test_instantiate_creates_a_new_instance
    post = Post.instantiate("title" => "appropriate documentation", "type" => "SpecialPost")
    assert_equal "appropriate documentation", post.title
    assert_instance_of SpecialPost, post

    # body was not initialized
    assert_raises ActiveModel::MissingAttributeError do
      post.body
    end
  end
910 911

  def test_reload_removes_custom_selects
912
    post = Post.select("posts.*, 1 as wibble").last!
913 914 915 916

    assert_equal 1, post[:wibble]
    assert_nil post.reload[:wibble]
  end
917 918 919 920 921 922 923 924 925 926 927 928

  def test_find_via_reload
    post = Post.new

    assert post.new_record?

    post.id = 1
    post.reload

    assert_equal "Welcome to the weblog", post.title
    assert_not post.new_record?
  end
929

930 931 932
  def test_reload_via_querycache
    ActiveRecord::Base.connection.enable_query_cache!
    ActiveRecord::Base.connection.clear_query_cache
933
    assert ActiveRecord::Base.connection.query_cache_enabled, "cache should be on"
934
    parrot = Parrot.create(name: "Shane")
935 936 937 938 939 940 941 942

    # populate the cache with the SELECT result
    found_parrot = Parrot.find(parrot.id)
    assert_equal parrot.id, found_parrot.id

    # Manually update the 'name' attribute in the DB directly
    assert_equal 1, ActiveRecord::Base.connection.query_cache.length
    ActiveRecord::Base.uncached do
943
      found_parrot.name = "Mary"
944 945 946 947 948
      found_parrot.save
    end

    # Now reload, and verify that it gets the DB version, and not the querycache version
    found_parrot.reload
949
    assert_equal "Mary", found_parrot.name
950 951

    found_parrot = Parrot.find(parrot.id)
952
    assert_equal "Mary", found_parrot.name
953 954 955 956
  ensure
    ActiveRecord::Base.connection.disable_query_cache!
  end

957
  class SaveTest < ActiveRecord::TestCase
958
    self.use_transactional_tests = false
959 960 961 962 963 964 965 966 967 968 969

    def test_save_touch_false
      widget = Class.new(ActiveRecord::Base) do
        connection.create_table :widgets, force: true do |t|
          t.string :name
          t.timestamps null: false
        end

        self.table_name = :widgets
      end

970 971
      instance  = widget.create!(
        name: "Bob",
972
        created_at: 1.day.ago,
973
        updated_at: 1.day.ago)
974 975 976 977

      created_at = instance.created_at
      updated_at = instance.updated_at

978
      instance.name = "Barb"
979 980 981 982
      instance.save!(touch: false)
      assert_equal instance.created_at, created_at
      assert_equal instance.updated_at, updated_at
    ensure
983 984
      ActiveRecord::Base.connection.drop_table widget.table_name
      widget.reset_column_information
985 986
    end
  end
987 988 989 990 991 992 993 994 995 996 997

  def test_reset_column_information_resets_children
    child = Class.new(Topic)
    child.new # force schema to load

    ActiveRecord::Base.connection.add_column(:topics, :foo, :string)
    Topic.reset_column_information

    assert_equal "bar", child.new(foo: :bar).foo
  ensure
    ActiveRecord::Base.connection.remove_column(:topics, :foo)
S
Sean Griffin 已提交
998
    Topic.reset_column_information
999
  end
1000
end