has_many_associations_test.rb 84.7 KB
Newer Older
1 2
# frozen_string_literal: true

3
require "cases/helper"
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
require "models/developer"
require "models/computer"
require "models/project"
require "models/company"
require "models/contract"
require "models/topic"
require "models/reply"
require "models/category"
require "models/image"
require "models/post"
require "models/author"
require "models/essay"
require "models/comment"
require "models/person"
require "models/reader"
require "models/tagging"
require "models/tag"
require "models/invoice"
require "models/line_item"
require "models/car"
require "models/bulb"
require "models/engine"
require "models/categorization"
require "models/minivan"
require "models/speedometer"
require "models/reference"
require "models/job"
require "models/college"
require "models/student"
require "models/pirate"
require "models/ship"
require "models/ship_part"
require "models/treasure"
require "models/parrot"
require "models/tyre"
require "models/subscriber"
require "models/subscription"
require "models/zine"
require "models/interest"
43

44
class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase
45
  fixtures :authors, :author_addresses, :posts, :comments
46 47 48 49 50

  def test_should_generate_valid_sql
    author = authors(:david)
    # this can fail on adapters which require ORDER BY expressions to be included in the SELECT expression
    # if the reorder clauses are not correctly handled
51
    assert author.posts_with_comments_sorted_by_comment_id.where("comments.id > 0").reorder("posts.comments_count DESC", "posts.tags_count DESC").last
52 53
  end
end
54

55
class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
56
  fixtures :authors, :author_addresses, :essays, :subscribers, :subscriptions, :people
57 58

  def test_custom_primary_key_on_new_record_should_fetch_with_query
59
    subscriber = Subscriber.new(nick: "webster132")
60
    assert_not_predicate subscriber.subscriptions, :loaded?
61 62 63 64 65

    assert_queries 1 do
      assert_equal 2, subscriber.subscriptions.size
    end

66
    assert_equal Subscription.where(subscriber_id: "webster132"), subscriber.subscriptions
67 68 69
  end

  def test_association_primary_key_on_new_record_should_fetch_with_query
70
    author = Author.new(name: "David")
71
    assert_not_predicate author.essays, :loaded?
72 73 74 75 76

    assert_queries 1 do
      assert_equal 1, author.essays.size
    end

77
    assert_equal Essay.where(writer_id: "David"), author.essays
78 79 80 81
  end

  def test_has_many_custom_primary_key
    david = authors(:david)
82 83 84 85 86 87 88 89 90 91 92 93
    assert_equal Essay.where(writer_id: "David"), david.essays
  end

  def test_ids_on_unloaded_association_with_custom_primary_key
    david = people(:david)
    assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids
  end

  def test_ids_on_loaded_association_with_custom_primary_key
    david = people(:david)
    david.essays.load
    assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids
94 95 96 97 98 99
  end

  def test_has_many_assignment_with_custom_primary_key
    david = people(:david)

    assert_equal ["A Modest Proposal"], david.essays.map(&:name)
100
    david.essays = [Essay.create!(name: "Remote Work")]
101 102 103 104 105
    assert_equal ["Remote Work"], david.essays.map(&:name)
  end

  def test_blank_custom_primary_key_on_new_record_should_not_run_queries
    author = Author.new
106
    assert_not_predicate author.essays, :loaded?
107 108 109 110 111 112

    assert_queries 0 do
      assert_equal 0, author.essays.size
    end
  end
end
113 114 115

class HasManyAssociationsTest < ActiveRecord::TestCase
  fixtures :accounts, :categories, :companies, :developers, :projects,
116
           :developers_projects, :topics, :authors, :author_addresses, :comments,
117
           :posts, :readers, :taggings, :cars, :jobs, :tags,
118
           :categorizations, :zines, :interests
119 120 121 122 123

  def setup
    Client.destroyed_client_ids.clear
  end

124 125 126 127 128 129
  def test_sti_subselect_count
    tag = Tag.first
    len = Post.tagged_with(tag.id).limit(10).size
    assert_operator len, :>, 0
  end

130 131
  def test_anonymous_has_many
    developer = Class.new(ActiveRecord::Base) {
132
      self.table_name = "developers"
133 134 135
      dev = self

      developer_project = Class.new(ActiveRecord::Base) {
136
        self.table_name = "developers_projects"
137
        belongs_to :developer, anonymous_class: dev
138
      }
139
      has_many :developer_projects, anonymous_class: developer_project, foreign_key: "developer_id"
140 141 142 143 144 145 146 147
    }
    dev = developer.first
    named = Developer.find(dev.id)
    assert_operator dev.developer_projects.count, :>, 0
    assert_equal named.projects.map(&:id).sort,
                 dev.developer_projects.map(&:project_id).sort
  end

148 149 150
  def test_default_scope_on_relations_is_not_cached
    counter = 0
    posts = Class.new(ActiveRecord::Base) {
151 152
      self.table_name = "posts"
      self.inheritance_column = "not_there"
153 154 155
      post = self

      comments = Class.new(ActiveRecord::Base) {
156 157
        self.table_name = "comments"
        self.inheritance_column = "not_there"
158
        belongs_to :post, anonymous_class: post
159 160
        default_scope -> {
          counter += 1
161
          where("id = :inc", inc: counter)
162 163
        }
      }
164
      has_many :comments, anonymous_class: comments, foreign_key: "post_id"
165 166 167 168 169 170 171 172 173
    }
    assert_equal 0, counter
    post = posts.first
    assert_equal 0, counter
    sql = capture_sql { post.comments.to_a }
    post.comments.reset
    assert_not_equal sql, capture_sql { post.comments.to_a }
  end

174
  def test_has_many_build_with_options
175 176
    college = College.create(name: "UFMT")
    Student.create(active: true, college_id: college.id, name: "Sarah")
177 178 179 180

    assert_equal college.students, Student.where(active: true, college_id: college.id)
  end

181
  def test_add_record_to_collection_should_change_its_updated_at
182 183
    ship = Ship.create(name: "dauntless")
    part = ShipPart.create(name: "cockpit")
184 185
    updated_at = part.updated_at

186 187 188
    travel(1.second) do
      ship.parts << part
    end
189 190 191 192 193 194 195 196

    assert_equal part.ship, ship
    assert_not_equal part.updated_at, updated_at
  end

  def test_clear_collection_should_not_change_updated_at
    # GH#17161: .clear calls delete_all (and returns the association),
    # which is intended to not touch associated objects's updated_at field
197 198
    ship = Ship.create(name: "dauntless")
    part = ShipPart.create(name: "cockpit", ship_id: ship.id)
199 200 201 202

    ship.parts.clear
    part.reload

203
    assert_nil part.ship
204
    assert_not_predicate part, :updated_at_changed?
205 206
  end

207
  def test_create_from_association_should_respect_default_scope
208
    car = Car.create(name: "honda")
209
    assert_equal "honda", car.name
210 211

    bulb = Bulb.create
212
    assert_equal "defaulty", bulb.name
213 214

    bulb = car.bulbs.build
215
    assert_equal "defaulty", bulb.name
216 217

    bulb = car.bulbs.create
218
    assert_equal "defaulty", bulb.name
219 220 221
  end

  def test_build_and_create_from_association_should_respect_passed_attributes_over_default_scope
222
    car = Car.create(name: "honda")
223

224 225
    bulb = car.bulbs.build(name: "exotic")
    assert_equal "exotic", bulb.name
226

227 228
    bulb = car.bulbs.create(name: "exotic")
    assert_equal "exotic", bulb.name
229 230 231 232 233 234

    bulb = car.awesome_bulbs.build(frickinawesome: false)
    assert_equal false, bulb.frickinawesome

    bulb = car.awesome_bulbs.create(frickinawesome: false)
    assert_equal false, bulb.frickinawesome
235 236
  end

237 238 239 240
  def test_build_from_association_should_respect_scope
    author = Author.new

    post = author.thinking_posts.build
241
    assert_equal "So I was thinking", post.title
242 243
  end

244
  def test_create_from_association_with_nil_values_should_work
245
    car = Car.create(name: "honda")
246 247

    bulb = car.bulbs.new(nil)
248
    assert_equal "defaulty", bulb.name
249 250

    bulb = car.bulbs.build(nil)
251
    assert_equal "defaulty", bulb.name
252 253

    bulb = car.bulbs.create(nil)
254
    assert_equal "defaulty", bulb.name
255 256
  end

257 258 259 260 261 262 263
  def test_build_from_association_sets_inverse_instance
    car = Car.new(name: "honda")

    bulb = car.bulbs.build
    assert_equal car, bulb.car
  end

264
  def test_do_not_call_callbacks_for_delete_all
265
    car = Car.create(name: "honda")
266
    car.funky_bulbs.create!
267
    assert_equal 1, car.funky_bulbs.count
268
    assert_nothing_raised { car.reload.funky_bulbs.delete_all }
269
    assert_equal 0, car.funky_bulbs.count, "bulbs should have been deleted using :delete_all strategy"
270 271
  end

272 273
  def test_delete_all_on_association_is_the_same_as_not_loaded
    author = authors :david
274
    author.thinking_posts.create!(body: "test")
275 276 277
    author.reload
    expected_sql = capture_sql { author.thinking_posts.delete_all }

278
    author.thinking_posts.create!(body: "test")
279 280 281 282 283 284
    author.reload
    author.thinking_posts.inspect
    loaded_sql = capture_sql { author.thinking_posts.delete_all }
    assert_equal(expected_sql, loaded_sql)
  end

285 286
  def test_delete_all_on_association_with_nil_dependency_is_the_same_as_not_loaded
    author = authors :david
287
    author.posts.create!(title: "test", body: "body")
288 289 290
    author.reload
    expected_sql = capture_sql { author.posts.delete_all }

291
    author.posts.create!(title: "test", body: "body")
292 293 294 295 296 297
    author.reload
    author.posts.to_a
    loaded_sql = capture_sql { author.posts.delete_all }
    assert_equal(expected_sql, loaded_sql)
  end

298 299 300
  def test_building_the_associated_object_with_implicit_sti_base_class
    firm = DependentFirm.new
    company = firm.companies.build
301
    assert_kind_of Company, company, "Expected #{company.class} to be a Company"
302 303 304 305
  end

  def test_building_the_associated_object_with_explicit_sti_base_class
    firm = DependentFirm.new
306
    company = firm.companies.build(type: "Company")
307
    assert_kind_of Company, company, "Expected #{company.class} to be a Company"
308 309 310 311
  end

  def test_building_the_associated_object_with_sti_subclass
    firm = DependentFirm.new
312
    company = firm.companies.build(type: "Client")
313
    assert_kind_of Client, company, "Expected #{company.class} to be a Client"
314 315 316 317
  end

  def test_building_the_associated_object_with_an_invalid_type
    firm = DependentFirm.new
318
    assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(type: "Invalid") }
319 320 321 322
  end

  def test_building_the_associated_object_with_an_unrelated_type
    firm = DependentFirm.new
323
    assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(type: "Account") }
324 325
  end

326 327
  test "building the association with an array" do
    speedometer = Speedometer.new(speedometer_id: "a")
328
    data = [{ name: "first" }, { name: "second" }]
329 330 331 332 333 334 335
    speedometer.minivans.build(data)

    assert_equal 2, speedometer.minivans.size
    assert speedometer.save
    assert_equal ["first", "second"], speedometer.reload.minivans.map(&:name)
  end

336
  def test_association_keys_bypass_attribute_protection
337
    car = Car.create(name: "honda")
338 339 340 341

    bulb = car.bulbs.new
    assert_equal car.id, bulb.car_id

342
    bulb = car.bulbs.new car_id: car.id + 1
343 344
    assert_equal car.id, bulb.car_id

345 346 347
    bulb = car.bulbs.build
    assert_equal car.id, bulb.car_id

348
    bulb = car.bulbs.build car_id: car.id + 1
349 350
    assert_equal car.id, bulb.car_id

351 352
    bulb = car.bulbs.create
    assert_equal car.id, bulb.car_id
353

354
    bulb = car.bulbs.create car_id: car.id + 1
355
    assert_equal car.id, bulb.car_id
356 357
  end

358 359 360 361 362 363
  def test_association_protect_foreign_key
    invoice = Invoice.create

    line_item = invoice.line_items.new
    assert_equal invoice.id, line_item.invoice_id

364
    line_item = invoice.line_items.new invoice_id: invoice.id + 1
365 366 367 368 369
    assert_equal invoice.id, line_item.invoice_id

    line_item = invoice.line_items.build
    assert_equal invoice.id, line_item.invoice_id

370
    line_item = invoice.line_items.build invoice_id: invoice.id + 1
371 372 373 374 375
    assert_equal invoice.id, line_item.invoice_id

    line_item = invoice.line_items.create
    assert_equal invoice.id, line_item.invoice_id

376
    line_item = invoice.line_items.create invoice_id: invoice.id + 1
377 378 379
    assert_equal invoice.id, line_item.invoice_id
  end

380 381 382 383
  # When creating objects on the association, we must not do it within a scope (even though it
  # would be convenient), because this would cause that scope to be applied to any callbacks etc.
  def test_build_and_create_should_not_happen_within_scope
    car = cars(:honda)
384
    scope = car.foo_bulbs.where_values_hash
385

386
    bulb = car.foo_bulbs.build
387
    assert_not_equal scope, bulb.scope_after_initialize.where_values_hash
388

389
    bulb = car.foo_bulbs.create
390
    assert_not_equal scope, bulb.scope_after_initialize.where_values_hash
391

392
    bulb = car.foo_bulbs.create!
393
    assert_not_equal scope, bulb.scope_after_initialize.where_values_hash
394 395
  end

396
  def test_no_sql_should_be_fired_if_association_already_loaded
397
    Car.create(name: "honda")
398
    bulbs = Car.first.bulbs
B
Brian Cardarella 已提交
399
    bulbs.to_a # to load all instances of bulbs
400

401 402 403
    assert_no_queries do
      bulbs.first()
    end
404

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
    assert_no_queries do
      bulbs.second()
    end

    assert_no_queries do
      bulbs.third()
    end

    assert_no_queries do
      bulbs.fourth()
    end

    assert_no_queries do
      bulbs.fifth()
    end

    assert_no_queries do
      bulbs.forty_two()
    end

425
    assert_no_queries do
426
      bulbs.third_to_last()
427 428 429
    end

    assert_no_queries do
430
      bulbs.second_to_last()
431 432
    end

433 434 435
    assert_no_queries do
      bulbs.last()
    end
436 437
  end

438 439 440 441 442 443 444 445 446
  def test_finder_method_with_dirty_target
    company = companies(:first_firm)
    new_clients = []
    assert_no_queries(ignore_none: false) do
      new_clients << company.clients_of_firm.build(name: "Another Client")
      new_clients << company.clients_of_firm.build(name: "Another Client II")
      new_clients << company.clients_of_firm.build(name: "Another Client III")
    end

447
    assert_not_predicate company.clients_of_firm, :loaded?
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
    assert_queries(1) do
      assert_same new_clients[0], company.clients_of_firm.third
      assert_same new_clients[1], company.clients_of_firm.fourth
      assert_same new_clients[2], company.clients_of_firm.fifth
      assert_same new_clients[0], company.clients_of_firm.third_to_last
      assert_same new_clients[1], company.clients_of_firm.second_to_last
      assert_same new_clients[2], company.clients_of_firm.last
    end
  end

  def test_finder_bang_method_with_dirty_target
    company = companies(:first_firm)
    new_clients = []
    assert_no_queries(ignore_none: false) do
      new_clients << company.clients_of_firm.build(name: "Another Client")
      new_clients << company.clients_of_firm.build(name: "Another Client II")
      new_clients << company.clients_of_firm.build(name: "Another Client III")
    end

467
    assert_not_predicate company.clients_of_firm, :loaded?
468 469 470 471 472 473 474 475 476 477
    assert_queries(1) do
      assert_same new_clients[0], company.clients_of_firm.third!
      assert_same new_clients[1], company.clients_of_firm.fourth!
      assert_same new_clients[2], company.clients_of_firm.fifth!
      assert_same new_clients[0], company.clients_of_firm.third_to_last!
      assert_same new_clients[1], company.clients_of_firm.second_to_last!
      assert_same new_clients[2], company.clients_of_firm.last!
    end
  end

478
  def test_create_resets_cached_counters
479 480
    Reader.delete_all

481
    person = Person.create!(first_name: "tenderlove")
482

483
    post   = Post.first
484 485

    assert_equal [], person.readers
486
    assert_nil person.readers.find_by_post_id(post.id)
487

488
    person.readers.create(post_id: post.id)
489 490 491 492 493 494 495

    assert_equal 1, person.readers.count
    assert_equal 1, person.readers.length
    assert_equal post, person.readers.first.post
    assert_equal person, person.readers.first.person
  end

496 497
  def test_update_all_respects_association_scope
    person = Person.new
498
    person.first_name = "Naruto"
499 500 501 502 503 504 505 506 507
    person.references << Reference.new
    person.id = 10
    person.references
    person.save!
    assert_equal 1, person.references.update_all(favourite: true)
  end

  def test_exists_respects_association_scope
    person = Person.new
508
    person.first_name = "Sasuke"
509 510 511 512 513 514 515
    person.references << Reference.new
    person.id = 10
    person.references
    person.save!
    assert_predicate person.references, :exists?
  end

516
  def test_counting_with_counter_sql
517
    assert_equal 3, Firm.first.clients.count
518 519 520
  end

  def test_counting
521
    assert_equal 3, Firm.first.plain_clients.count
522 523 524
  end

  def test_counting_with_single_hash
525
    assert_equal 1, Firm.first.plain_clients.where(name: "Microsoft").count
526 527 528
  end

  def test_counting_with_column_name_and_hash
529
    assert_equal 3, Firm.first.plain_clients.count(:name)
530 531
  end

532 533 534 535 536 537
  def test_counting_with_association_limit
    firm = companies(:first_firm)
    assert_equal firm.limited_clients.length, firm.limited_clients.size
    assert_equal firm.limited_clients.length, firm.limited_clients.count
  end

538
  def test_finding
539
    assert_equal 3, Firm.first.clients.length
540 541
  end

542
  def test_finding_array_compatibility
543
    assert_equal 3, Firm.order(:id).find { |f| f.id > 0 }.clients.length
544 545
  end

546 547
  def test_find_many_with_merged_options
    assert_equal 1, companies(:first_firm).limited_clients.size
548
    assert_equal 1, companies(:first_firm).limited_clients.to_a.size
549
    assert_equal 3, companies(:first_firm).limited_clients.limit(nil).to_a.size
550 551
  end

552
  def test_find_should_append_to_association_order
553
    ordered_clients = companies(:first_firm).clients_sorted_desc.order("companies.id")
554
    assert_equal ["id DESC", "companies.id"], ordered_clients.order_values
555 556
  end

557
  def test_dynamic_find_should_respect_association_order
558
    assert_equal companies(:another_first_firm_client), companies(:first_firm).clients_sorted_desc.where("type = 'Client'").first
559
    assert_equal companies(:another_first_firm_client), companies(:first_firm).clients_sorted_desc.find_by_type("Client")
560 561
  end

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
  def test_taking
    posts(:other_by_bob).destroy
    assert_equal posts(:misc_by_bob), authors(:bob).posts.take
    assert_equal posts(:misc_by_bob), authors(:bob).posts.take!
    authors(:bob).posts.to_a
    assert_equal posts(:misc_by_bob), authors(:bob).posts.take
    assert_equal posts(:misc_by_bob), authors(:bob).posts.take!
  end

  def test_taking_not_found
    authors(:bob).posts.delete_all
    assert_raise(ActiveRecord::RecordNotFound) { authors(:bob).posts.take! }
    authors(:bob).posts.to_a
    assert_raise(ActiveRecord::RecordNotFound) { authors(:bob).posts.take! }
  end

578
  def test_taking_with_a_number
579 580 581 582 583 584 585 586
    klass = Class.new(Author) do
      has_many :posts, -> { order(:id) }

      def self.name
        "Author"
      end
    end

587
    # taking from unloaded Relation
588
    bob = klass.find(authors(:bob).id)
589
    new_post = bob.posts.build
590
    assert_not_predicate bob.posts, :loaded?
591 592
    assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
    assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], bob.posts.take(2)
593
    assert_equal [posts(:misc_by_bob), posts(:other_by_bob), new_post], bob.posts.take(3)
594 595

    # taking from loaded Relation
596
    bob.posts.load
597
    assert_predicate bob.posts, :loaded?
598 599 600
    assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
    assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], bob.posts.take(2)
    assert_equal [posts(:misc_by_bob), posts(:other_by_bob), new_post], bob.posts.take(3)
601 602
  end

603 604 605 606 607 608 609 610 611 612
  def test_taking_with_inverse_of
    interests(:woodsmanship).destroy
    interests(:survival).destroy

    zine = zines(:going_out)
    interest = zine.interests.take
    assert_equal interests(:hunting), interest
    assert_same zine, interest.zine
  end

613 614 615 616 617 618
  def test_cant_save_has_many_readonly_association
    authors(:david).readonly_comments.each { |c| assert_raise(ActiveRecord::ReadOnlyRecord) { c.save! } }
    authors(:david).readonly_comments.each { |c| assert c.readonly? }
  end

  def test_finding_default_orders
619
    assert_equal "Summit", Firm.first.clients.first.name
620 621 622
  end

  def test_finding_with_different_class_name_and_order
623
    assert_equal "Apex", Firm.first.clients_sorted_desc.first.name
624 625 626
  end

  def test_finding_with_foreign_key
627
    assert_equal "Microsoft", Firm.first.clients_of_firm.first.name
628 629 630
  end

  def test_finding_with_condition
631
    assert_equal "Microsoft", Firm.first.clients_like_ms.first.name
632 633 634
  end

  def test_finding_with_condition_hash
635
    assert_equal "Microsoft", Firm.first.clients_like_ms_with_hash_conditions.first.name
636 637
  end

638
  def test_finding_using_primary_key
639
    assert_equal "Summit", Firm.first.clients_using_primary_key.first.name
640 641
  end

642
  def test_update_all_on_association_accessed_before_save
643
    firm = Firm.new(name: "Firm")
644 645
    firm.clients << Client.first
    firm.save!
646
    assert_equal firm.clients.count, firm.clients.update_all(description: "Great!")
647 648 649
  end

  def test_update_all_on_association_accessed_before_save_with_explicit_foreign_key
650
    firm = Firm.new(name: "Firm", id: 100)
651 652
    firm.clients << Client.first
    firm.save!
653
    assert_equal firm.clients.count, firm.clients.update_all(description: "Great!")
654 655
  end

656 657
  def test_belongs_to_sanity
    c = Client.new
658
    assert_nil c.firm, "belongs_to failed sanity check on new object"
659 660 661
  end

  def test_find_ids
662
    firm = Firm.first
663

664
    assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find }
665 666 667 668 669 670 671 672 673 674 675 676 677

    client = firm.clients.find(2)
    assert_kind_of Client, client

    client_ary = firm.clients.find([2])
    assert_kind_of Array, client_ary
    assert_equal client, client_ary.first

    client_ary = firm.clients.find(2, 3)
    assert_kind_of Array, client_ary
    assert_equal 2, client_ary.size
    assert_equal client, client_ary.first

678
    assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
679 680
  end

681
  def test_find_one_message_on_primary_key
682
    firm = Firm.first
683 684 685 686 687 688 689 690 691 692

    e = assert_raises(ActiveRecord::RecordNotFound) do
      firm.clients.find(0)
    end
    assert_equal 0, e.id
    assert_equal "id", e.primary_key
    assert_equal "Client", e.model
    assert_match (/\ACouldn't find Client with 'id'=0/), e.message
  end

A
Arthur Neves 已提交
693 694 695
  def test_find_ids_and_inverse_of
    force_signal37_to_load_all_clients_of_firm

696 697
    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

A
Arthur Neves 已提交
698 699 700 701 702 703 704 705 706
    firm = companies(:first_firm)
    client = firm.clients_of_firm.find(3)
    assert_kind_of Client, client

    client_ary = firm.clients_of_firm.find([3])
    assert_kind_of Array, client_ary
    assert_equal client, client_ary.first
  end

707
  def test_find_all
708
    firm = Firm.first
709
    assert_equal 3, firm.clients.where("#{QUOTED_TYPE} = 'Client'").to_a.length
710
    assert_equal 1, firm.clients.where("name = 'Summit'").to_a.length
711 712
  end

713 714 715
  def test_find_each
    firm = companies(:first_firm)

716
    assert_not_predicate  firm.clients, :loaded?
717

718
    assert_queries(4) do
719
      firm.clients.find_each(batch_size: 1) { |c| assert_equal firm.id, c.firm_id }
720 721
    end

722
    assert_not_predicate  firm.clients, :loaded?
723 724 725 726 727 728
  end

  def test_find_each_with_conditions
    firm = companies(:first_firm)

    assert_queries(2) do
729
      firm.clients.where(name: "Microsoft").find_each(batch_size: 1) do |c|
730 731 732 733 734
        assert_equal firm.id, c.firm_id
        assert_equal "Microsoft", c.name
      end
    end

735
    assert_not_predicate  firm.clients, :loaded?
736 737 738 739 740
  end

  def test_find_in_batches
    firm = companies(:first_firm)

741
    assert_not_predicate  firm.clients, :loaded?
742 743

    assert_queries(2) do
744
      firm.clients.find_in_batches(batch_size: 2) do |clients|
745
        clients.each { |c| assert_equal firm.id, c.firm_id }
746 747 748
      end
    end

749
    assert_not_predicate  firm.clients, :loaded?
750 751
  end

752
  def test_find_all_sanitized
753
    firm = Firm.first
754 755
    summit = firm.clients.where("name = 'Summit'").to_a
    assert_equal summit, firm.clients.where("name = ?", "Summit").to_a
756
    assert_equal summit, firm.clients.where("name = :name", name: "Summit").to_a
757 758 759
  end

  def test_find_first
760
    firm = Firm.first
761
    client2 = Client.find(2)
762 763
    assert_equal firm.clients.first, firm.clients.order("id").first
    assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = 'Client'").order("id").first
764 765 766
  end

  def test_find_first_sanitized
767
    firm = Firm.first
768
    client2 = Client.find(2)
769 770
    assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = ?", "Client").first
    assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = :type", type: "Client").first
771 772
  end

773
  def test_find_first_after_reset_scope
774
    firm = Firm.first
775 776
    collection = firm.clients

777 778
    original_object = collection.first
    assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
779 780

    # It should return a different object, since the association has been reloaded
781
    assert_not_same original_object, firm.clients.first, "Expected #first to return a new object"
782
  end
783

784
  def test_find_first_after_reset
785
    firm = Firm.first
786
    collection = firm.clients
787

788 789
    original_object = collection.first
    assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
790
    collection.reset
791 792

    # It should return a different object, since the association has been reloaded
793
    assert_not_same original_object, collection.first, "Expected #first after #reset to return a new object"
794 795 796
  end

  def test_find_first_after_reload
797
    firm = Firm.first
798
    collection = firm.clients
799

800 801 802
    original_object = collection.first
    assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
    collection.reload
803 804

    # It should return a different object, since the association has been reloaded
805
    assert_not_same original_object, collection.first, "Expected #first after #reload to return a new object"
806 807
  end

808 809
  def test_find_all_with_include_and_conditions
    assert_nothing_raised do
810
      Developer.all.merge!(joins: :audit_logs, where: { "audit_logs.message" => nil, :name => "Smith" }).to_a
811 812 813
    end
  end

814 815
  def test_find_in_collection
    assert_equal Client.find(2).name, companies(:first_firm).clients.find(2).name
816
    assert_raise(ActiveRecord::RecordNotFound) { companies(:first_firm).clients.find(6) }
817 818 819
  end

  def test_find_grouped
820 821
    all_clients_of_firm1 = Client.all.merge!(where: "firm_id = 1").to_a
    grouped_clients_of_firm1 = Client.all.merge!(where: "firm_id = 1", group: "firm_id", select: "firm_id, count(id) as clients_count").to_a
822
    assert_equal 3, all_clients_of_firm1.size
823 824 825
    assert_equal 1, grouped_clients_of_firm1.size
  end

826
  def test_find_scoped_grouped
827
    assert_equal 1, companies(:first_firm).clients_grouped_by_firm_id.size
828
    assert_equal 1, companies(:first_firm).clients_grouped_by_firm_id.length
829 830
    assert_equal 3, companies(:first_firm).clients_grouped_by_name.size
    assert_equal 3, companies(:first_firm).clients_grouped_by_name.length
831 832
  end

833
  def test_find_scoped_grouped_having
834
    assert_equal 2, authors(:david).popular_grouped_posts.length
835 836 837
    assert_equal 0, authors(:mary).popular_grouped_posts.length
  end

838
  def test_default_select
839
    assert_equal Comment.column_names.sort, posts(:welcome).comments.first.attributes.keys.sort
840 841 842
  end

  def test_select_query_method
843
    assert_equal ["id", "body"], posts(:welcome).comments.select(:id, :body).first.attributes.keys
844 845 846 847
  end

  def test_select_with_block
    assert_equal [1], posts(:welcome).comments.select { |c| c.id == 1 }.map(&:id)
848 849
  end

850 851 852 853 854 855
  def test_select_with_block_and_dirty_target
    assert_equal 2, posts(:welcome).comments.select { true }.size
    posts(:welcome).comments.build
    assert_equal 3, posts(:welcome).comments.select { true }.size
  end

856
  def test_select_without_foreign_key
857
    assert_equal companies(:first_firm).accounts.first.credit_limit, companies(:first_firm).accounts.select(:credit_limit).first.credit_limit
858
  end
859

860 861
  def test_adding
    force_signal37_to_load_all_clients_of_firm
862 863 864

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

865 866
    natural = Client.new("name" => "Natural Company")
    companies(:first_firm).clients_of_firm << natural
867
    assert_equal 3, companies(:first_firm).clients_of_firm.size # checking via the collection
868
    assert_equal 3, companies(:first_firm).clients_of_firm.reload.size # checking using the db
869 870 871 872 873 874
    assert_equal natural, companies(:first_firm).clients_of_firm.last
  end

  def test_adding_using_create
    first_firm = companies(:first_firm)
    assert_equal 3, first_firm.plain_clients.size
875
    first_firm.plain_clients.create(name: "Natural Company")
876 877
    assert_equal 4, first_firm.plain_clients.length
    assert_equal 4, first_firm.plain_clients.size
878 879 880
  end

  def test_create_with_bang_on_has_many_when_parent_is_new_raises
881
    error = assert_raise(ActiveRecord::RecordNotSaved) do
882
      firm = Firm.new
883
      firm.plain_clients.create! name: "Whoever"
884
    end
885 886

    assert_equal "You cannot call create unless the parent is saved", error.message
887 888 889
  end

  def test_regular_create_on_has_many_when_parent_is_new_raises
890
    error = assert_raise(ActiveRecord::RecordNotSaved) do
891
      firm = Firm.new
892
      firm.plain_clients.create name: "Whoever"
893
    end
894 895

    assert_equal "You cannot call create unless the parent is saved", error.message
896 897 898
  end

  def test_create_with_bang_on_has_many_raises_when_record_not_saved
899
    assert_raise(ActiveRecord::RecordInvalid) do
900
      firm = Firm.first
901 902 903 904 905
      firm.plain_clients.create!
    end
  end

  def test_create_with_bang_on_habtm_when_parent_is_new_raises
906
    error = assert_raise(ActiveRecord::RecordNotSaved) do
907 908
      Developer.new("name" => "Aredridel").projects.create!
    end
909 910

    assert_equal "You cannot call create unless the parent is saved", error.message
911 912 913
  end

  def test_adding_a_mismatch_class
914 915 916
    assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << nil }
    assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << 1 }
    assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << Topic.find(1) }
917 918 919 920
  end

  def test_adding_a_collection
    force_signal37_to_load_all_clients_of_firm
921 922 923

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

924
    companies(:first_firm).clients_of_firm.concat([Client.new("name" => "Natural Company"), Client.new("name" => "Apple")])
925
    assert_equal 4, companies(:first_firm).clients_of_firm.size
926
    assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
927 928
  end

929
  def test_transactions_when_adding_to_persisted
930 931
    good = Client.new(name: "Good")
    bad  = Client.new(name: "Bad", raise_on_save: true)
J
Jon Leighton 已提交
932 933 934 935 936 937

    begin
      companies(:first_firm).clients_of_firm.concat(good, bad)
    rescue Client::RaisedOnSave
    end

938
    assert_not_includes companies(:first_firm).clients_of_firm.reload, good
939 940 941
  end

  def test_transactions_when_adding_to_new_record
942
    assert_no_queries(ignore_none: false) do
J
Jon Leighton 已提交
943 944 945
      firm = Firm.new
      firm.clients_of_firm.concat(Client.new("name" => "Natural Company"))
    end
946 947
  end

948 949 950 951 952 953 954
  def test_inverse_on_before_validate
    firm = companies(:first_firm)
    assert_queries(1) do
      firm.clients_of_firm << Client.new("name" => "Natural Company")
    end
  end

955 956
  def test_new_aliased_to_build
    company = companies(:first_firm)
957
    new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.new("name" => "Another Client") }
958
    assert_not_predicate company.clients_of_firm, :loaded?
959 960

    assert_equal "Another Client", new_client.name
961
    assert_not_predicate new_client, :persisted?
962 963 964
    assert_equal new_client, company.clients_of_firm.last
  end

965 966
  def test_build
    company = companies(:first_firm)
967
    new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") }
968
    assert_not_predicate company.clients_of_firm, :loaded?
969

970
    assert_equal "Another Client", new_client.name
971
    assert_not_predicate new_client, :persisted?
972 973 974
    assert_equal new_client, company.clients_of_firm.last
  end

975 976 977 978
  def test_collection_size_after_building
    company = companies(:first_firm)  # company already has one client
    company.clients_of_firm.build("name" => "Another Client")
    company.clients_of_firm.build("name" => "Yet Another Client")
979
    assert_equal 4, company.clients_of_firm.size
980
    assert_equal 4, company.clients_of_firm.uniq.size
981 982
  end

983 984
  def test_collection_not_empty_after_building
    company = companies(:first_firm)
985
    assert_empty company.contracts
986
    company.contracts.build
987
    assert_not_empty company.contracts
988 989
  end

990 991 992 993 994 995 996 997 998 999 1000 1001
  def test_collection_size_twice_for_regressions
    post = posts(:thinking)
    assert_equal 0, post.readers.size
    # This test needs a post that has no readers, we assert it to ensure it holds,
    # but need to reload the post because the very call to #size hides the bug.
    post.reload
    post.readers.build
    size1 = post.readers.size
    size2 = post.readers.size
    assert_equal size1, size2
  end

1002 1003
  def test_build_many
    company = companies(:first_firm)
1004
    new_clients = assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
1005 1006 1007 1008
    assert_equal 2, new_clients.size
  end

  def test_build_followed_by_save_does_not_load_target
1009
    companies(:first_firm).clients_of_firm.build("name" => "Another Client")
1010
    assert companies(:first_firm).save
1011
    assert_not_predicate companies(:first_firm).clients_of_firm, :loaded?
1012 1013 1014 1015 1016 1017 1018 1019 1020
  end

  def test_build_without_loading_association
    first_topic = topics(:first)
    Reply.column_names

    assert_equal 1, first_topic.replies.length

    assert_no_queries do
1021
      first_topic.replies.build(title: "Not saved", content: "Superstars")
1022 1023 1024 1025 1026 1027
      assert_equal 2, first_topic.replies.size
    end

    assert_equal 2, first_topic.replies.to_ary.size
  end

1028 1029
  def test_build_via_block
    company = companies(:first_firm)
1030
    new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
1031
    assert_not_predicate company.clients_of_firm, :loaded?
1032 1033

    assert_equal "Another Client", new_client.name
1034
    assert_not_predicate new_client, :persisted?
1035 1036 1037 1038 1039
    assert_equal new_client, company.clients_of_firm.last
  end

  def test_build_many_via_block
    company = companies(:first_firm)
1040
    new_clients = assert_no_queries(ignore_none: false) do
1041
      company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
1042 1043 1044 1045 1046 1047 1048 1049 1050
        client.name = "changed"
      end
    end

    assert_equal 2, new_clients.size
    assert_equal "changed", new_clients.first.name
    assert_equal "changed", new_clients.last.name
  end

1051
  def test_create_without_loading_association
1052
    first_firm = companies(:first_firm)
1053 1054 1055
    Firm.column_names
    Client.column_names

1056
    assert_equal 2, first_firm.clients_of_firm.size
1057 1058 1059
    first_firm.clients_of_firm.reset

    assert_queries(1) do
1060
      first_firm.clients_of_firm.create(name: "Superstars")
1061 1062
    end

1063
    assert_equal 3, first_firm.clients_of_firm.size
1064 1065 1066 1067
  end

  def test_create
    force_signal37_to_load_all_clients_of_firm
1068 1069 1070

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1071
    new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
1072
    assert_predicate new_client, :persisted?
1073
    assert_equal new_client, companies(:first_firm).clients_of_firm.last
1074
    assert_equal new_client, companies(:first_firm).clients_of_firm.reload.last
1075 1076 1077
  end

  def test_create_many
1078
    companies(:first_firm).clients_of_firm.create([{ "name" => "Another Client" }, { "name" => "Another Client II" }])
1079
    assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
1080 1081 1082
  end

  def test_create_followed_by_save_does_not_load_target
1083
    companies(:first_firm).clients_of_firm.create("name" => "Another Client")
1084
    assert companies(:first_firm).save
1085
    assert_not_predicate companies(:first_firm).clients_of_firm, :loaded?
1086 1087 1088 1089
  end

  def test_deleting
    force_signal37_to_load_all_clients_of_firm
1090 1091 1092

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1093
    companies(:first_firm).clients_of_firm.delete(companies(:first_firm).clients_of_firm.first)
1094
    assert_equal 1, companies(:first_firm).clients_of_firm.size
1095
    assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
  end

  def test_deleting_before_save
    new_firm = Firm.new("name" => "A New Firm, Inc.")
    new_client = new_firm.clients_of_firm.build("name" => "Another Client")
    assert_equal 1, new_firm.clients_of_firm.size
    new_firm.clients_of_firm.delete(new_client)
    assert_equal 0, new_firm.clients_of_firm.size
  end

1106 1107 1108
  def test_has_many_without_counter_cache_option
    # Ship has a conventionally named `treasures_count` column, but the counter_cache
    # option is not given on the association.
1109
    ship = Ship.create(name: "Countless", treasures_count: 10)
1110

1111
    assert_not_predicate Ship.reflect_on_association(:treasures), :has_cached_counter?
1112 1113 1114 1115 1116

    # Count should come from sql count() of treasures rather than treasures_count attribute
    assert_equal ship.treasures.size, 0

    assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
1117
      ship.treasures.create(name: "Gold")
1118 1119 1120 1121 1122 1123 1124
    end

    assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
      ship.treasures.destroy_all
    end
  end

1125
  def test_deleting_updates_counter_cache
J
Jon Leighton 已提交
1126
    topic = Topic.order("id ASC").first
1127 1128 1129 1130 1131 1132 1133
    assert_equal topic.replies.to_a.size, topic.replies_count

    topic.replies.delete(topic.replies.first)
    topic.reload
    assert_equal topic.replies.to_a.size, topic.replies_count
  end

1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
  def test_counter_cache_updates_in_memory_after_concat
    topic = Topic.create title: "Zoom-zoom-zoom"

    topic.replies << Reply.create(title: "re: zoom", content: "speedy quick!")
    assert_equal 1, topic.replies_count
    assert_equal 1, topic.replies.size
    assert_equal 1, topic.reload.replies.size
  end

  def test_counter_cache_updates_in_memory_after_create
    topic = Topic.create title: "Zoom-zoom-zoom"

    topic.replies.create!(title: "re: zoom", content: "speedy quick!")
    assert_equal 1, topic.replies_count
    assert_equal 1, topic.replies.size
    assert_equal 1, topic.reload.replies.size
  end

  def test_counter_cache_updates_in_memory_after_create_with_array
    topic = Topic.create title: "Zoom-zoom-zoom"

    topic.replies.create!([
      { title: "re: zoom", content: "speedy quick!" },
      { title: "re: zoom 2", content: "OMG lol!" },
    ])
    assert_equal 2, topic.replies_count
    assert_equal 2, topic.replies.size
    assert_equal 2, topic.reload.replies.size
  end

1164 1165 1166 1167 1168 1169 1170 1171 1172
  def test_pushing_association_updates_counter_cache
    topic = Topic.order("id ASC").first
    reply = Reply.create!

    assert_difference "topic.reload.replies_count", 1 do
      topic.replies << reply
    end
  end

1173
  def test_deleting_updates_counter_cache_without_dependent_option
1174 1175
    post = posts(:welcome)

1176
    assert_difference "post.reload.tags_count", -1 do
1177 1178 1179 1180
      post.taggings.delete(post.taggings.first)
    end
  end

1181 1182
  def test_deleting_updates_counter_cache_with_dependent_delete_all
    post = posts(:welcome)
1183
    post.update_columns(taggings_with_delete_all_count: post.tags_count)
1184 1185 1186 1187 1188 1189

    assert_difference "post.reload.taggings_with_delete_all_count", -1 do
      post.taggings_with_delete_all.delete(post.taggings_with_delete_all.first)
    end
  end

1190 1191
  def test_deleting_updates_counter_cache_with_dependent_destroy
    post = posts(:welcome)
1192
    post.update_columns(taggings_with_destroy_count: post.tags_count)
1193 1194 1195 1196 1197 1198

    assert_difference "post.reload.taggings_with_destroy_count", -1 do
      post.taggings_with_destroy.delete(post.taggings_with_destroy.first)
    end
  end

1199 1200 1201
  def test_calling_empty_with_counter_cache
    post = posts(:welcome)
    assert_queries(0) do
1202
      assert_not_empty post.comments
1203 1204 1205
    end
  end

1206 1207 1208 1209 1210 1211 1212 1213
  def test_custom_named_counter_cache
    topic = topics(:first)

    assert_difference "topic.reload.replies_count", -1 do
      topic.approved_replies.clear
    end
  end

1214 1215 1216 1217 1218 1219
  def test_calling_update_attributes_on_id_changes_the_counter_cache
    topic = Topic.order("id ASC").first
    original_count = topic.replies.to_a.size
    assert_equal original_count, topic.replies_count

    first_reply = topic.replies.first
1220
    first_reply.update_attributes(parent_id: nil)
1221 1222
    assert_equal original_count - 1, topic.reload.replies_count

1223
    first_reply.update_attributes(parent_id: topic.id)
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
    assert_equal original_count, topic.reload.replies_count
  end

  def test_calling_update_attributes_changing_ids_doesnt_change_counter_cache
    topic1 = Topic.find(1)
    topic2 = Topic.find(3)
    original_count1 = topic1.replies.to_a.size
    original_count2 = topic2.replies.to_a.size

    reply1 = topic1.replies.first
    reply2 = topic2.replies.first

1236
    reply1.update_attributes(parent_id: topic2.id)
1237 1238 1239
    assert_equal original_count1 - 1, topic1.reload.replies_count
    assert_equal original_count2 + 1, topic2.reload.replies_count

1240
    reply2.update_attributes(parent_id: topic1.id)
1241 1242 1243 1244
    assert_equal original_count1, topic1.reload.replies_count
    assert_equal original_count2, topic2.reload.replies_count
  end

1245 1246
  def test_deleting_a_collection
    force_signal37_to_load_all_clients_of_firm
1247 1248 1249

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1250
    companies(:first_firm).clients_of_firm.create("name" => "Another Client")
1251 1252
    assert_equal 3, companies(:first_firm).clients_of_firm.size
    companies(:first_firm).clients_of_firm.delete([companies(:first_firm).clients_of_firm[0], companies(:first_firm).clients_of_firm[1], companies(:first_firm).clients_of_firm[2]])
1253
    assert_equal 0, companies(:first_firm).clients_of_firm.size
1254
    assert_equal 0, companies(:first_firm).clients_of_firm.reload.size
1255 1256 1257 1258
  end

  def test_delete_all
    force_signal37_to_load_all_clients_of_firm
1259 1260 1261

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1262 1263
    companies(:first_firm).dependent_clients_of_firm.create("name" => "Another Client")
    clients = companies(:first_firm).dependent_clients_of_firm.to_a
1264
    assert_equal 3, clients.count
1265 1266

    assert_difference "Client.count", -(clients.count) do
1267
      companies(:first_firm).dependent_clients_of_firm.delete_all
1268
    end
1269 1270 1271 1272
  end

  def test_delete_all_with_not_yet_loaded_association_collection
    force_signal37_to_load_all_clients_of_firm
1273 1274 1275

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1276
    companies(:first_firm).clients_of_firm.create("name" => "Another Client")
1277
    assert_equal 3, companies(:first_firm).clients_of_firm.size
1278 1279 1280
    companies(:first_firm).clients_of_firm.reset
    companies(:first_firm).clients_of_firm.delete_all
    assert_equal 0, companies(:first_firm).clients_of_firm.size
1281
    assert_equal 0, companies(:first_firm).clients_of_firm.reload.size
1282 1283
  end

1284
  def test_transaction_when_deleting_persisted
1285 1286
    good = Client.new(name: "Good")
    bad  = Client.new(name: "Bad", raise_on_destroy: true)
J
Jon Leighton 已提交
1287 1288 1289 1290 1291 1292 1293 1294

    companies(:first_firm).clients_of_firm = [good, bad]

    begin
      companies(:first_firm).clients_of_firm.destroy(good, bad)
    rescue Client::RaisedOnDestroy
    end

1295
    assert_equal [good, bad], companies(:first_firm).clients_of_firm.reload
1296 1297 1298
  end

  def test_transaction_when_deleting_new_record
1299
    assert_no_queries(ignore_none: false) do
J
Jon Leighton 已提交
1300 1301 1302 1303 1304
      firm = Firm.new
      client = Client.new("name" => "New Client")
      firm.clients_of_firm << client
      firm.clients_of_firm.destroy(client)
    end
1305 1306
  end

1307 1308 1309
  def test_clearing_an_association_collection
    firm = companies(:first_firm)
    client_id = firm.clients_of_firm.first.id
1310
    assert_equal 2, firm.clients_of_firm.size
1311

J
Jon Leighton 已提交
1312
    firm.clients_of_firm.clear
1313 1314

    assert_equal 0, firm.clients_of_firm.size
1315
    assert_equal 0, firm.clients_of_firm.reload.size
1316 1317 1318 1319
    assert_equal [], Client.destroyed_client_ids[firm.id]

    # Should not be destroyed since the association is not dependent.
    assert_nothing_raised do
1320
      assert_nil Client.find(client_id).firm
1321 1322 1323
    end
  end

1324 1325 1326
  def test_clearing_updates_counter_cache
    topic = Topic.first

1327
    assert_difference "topic.reload.replies_count", -1 do
1328 1329
      topic.replies.clear
    end
1330 1331
  end

1332 1333 1334 1335
  def test_clearing_updates_counter_cache_when_inverse_counter_cache_is_a_symbol_with_dependent_destroy
    car = Car.first
    car.engines.create!

1336
    assert_difference "car.reload.engines_count", -1 do
1337 1338 1339 1340
      car.engines.clear
    end
  end

1341 1342 1343
  def test_clearing_a_dependent_association_collection
    firm = companies(:first_firm)
    client_id = firm.dependent_clients_of_firm.first.id
1344
    assert_equal 2, firm.dependent_clients_of_firm.size
1345
    assert_equal 1, Client.find_by_id(client_id).client_of
1346

1347
    # :delete_all is called on each client since the dependent options is :destroy
1348 1349 1350
    firm.dependent_clients_of_firm.clear

    assert_equal 0, firm.dependent_clients_of_firm.size
1351
    assert_equal 0, firm.dependent_clients_of_firm.reload.size
1352
    assert_equal [], Client.destroyed_client_ids[firm.id]
1353 1354

    # Should be destroyed since the association is dependent.
1355
    assert_nil Client.find_by_id(client_id)
1356 1357 1358 1359 1360 1361
  end

  def test_delete_all_with_option_delete_all
    firm = companies(:first_firm)
    client_id = firm.dependent_clients_of_firm.first.id
    firm.dependent_clients_of_firm.delete_all(:delete_all)
1362
    assert_nil Client.find_by_id(client_id)
1363 1364
  end

1365 1366 1367 1368 1369 1370 1371
  def test_delete_all_accepts_limited_parameters
    firm = companies(:first_firm)
    assert_raise(ArgumentError) do
      firm.dependent_clients_of_firm.delete_all(:destroy)
    end
  end

1372 1373 1374
  def test_clearing_an_exclusively_dependent_association_collection
    firm = companies(:first_firm)
    client_id = firm.exclusively_dependent_clients_of_firm.first.id
1375
    assert_equal 2, firm.exclusively_dependent_clients_of_firm.size
1376 1377 1378 1379 1380 1381 1382 1383

    assert_equal [], Client.destroyed_client_ids[firm.id]

    # :exclusively_dependent means each client is deleted directly from
    # the database without looping through them calling destroy.
    firm.exclusively_dependent_clients_of_firm.clear

    assert_equal 0, firm.exclusively_dependent_clients_of_firm.size
1384
    assert_equal 0, firm.exclusively_dependent_clients_of_firm.reload.size
1385 1386 1387 1388
    # no destroy-filters should have been called
    assert_equal [], Client.destroyed_client_ids[firm.id]

    # Should be destroyed since the association is exclusively dependent.
1389
    assert_nil Client.find_by_id(client_id)
1390 1391 1392 1393
  end

  def test_dependent_association_respects_optional_conditions_on_delete
    firm = companies(:odegy)
1394 1395
    Client.create(client_of: firm.id, name: "BigShot Inc.")
    Client.create(client_of: firm.id, name: "SmallTime Inc.")
1396
    # only one of two clients is included in the association due to the :conditions key
J
Jon Leighton 已提交
1397
    assert_equal 2, Client.where(client_of: firm.id).size
1398 1399 1400
    assert_equal 1, firm.dependent_conditional_clients_of_firm.size
    firm.destroy
    # only the correctly associated client should have been deleted
J
Jon Leighton 已提交
1401
    assert_equal 1, Client.where(client_of: firm.id).size
1402 1403 1404 1405
  end

  def test_dependent_association_respects_optional_sanitized_conditions_on_delete
    firm = companies(:odegy)
1406 1407
    Client.create(client_of: firm.id, name: "BigShot Inc.")
    Client.create(client_of: firm.id, name: "SmallTime Inc.")
1408
    # only one of two clients is included in the association due to the :conditions key
J
Jon Leighton 已提交
1409
    assert_equal 2, Client.where(client_of: firm.id).size
1410 1411 1412
    assert_equal 1, firm.dependent_sanitized_conditional_clients_of_firm.size
    firm.destroy
    # only the correctly associated client should have been deleted
J
Jon Leighton 已提交
1413
    assert_equal 1, Client.where(client_of: firm.id).size
1414 1415
  end

1416 1417
  def test_dependent_association_respects_optional_hash_conditions_on_delete
    firm = companies(:odegy)
1418 1419
    Client.create(client_of: firm.id, name: "BigShot Inc.")
    Client.create(client_of: firm.id, name: "SmallTime Inc.")
1420
    # only one of two clients is included in the association due to the :conditions key
J
Jon Leighton 已提交
1421
    assert_equal 2, Client.where(client_of: firm.id).size
K
Koichi ITO 已提交
1422
    assert_equal 1, firm.dependent_hash_conditional_clients_of_firm.size
1423 1424
    firm.destroy
    # only the correctly associated client should have been deleted
J
Jon Leighton 已提交
1425
    assert_equal 1, Client.where(client_of: firm.id).size
1426 1427
  end

1428
  def test_delete_all_association_with_primary_key_deletes_correct_records
1429
    firm = Firm.first
1430
    # break the vanilla firm_id foreign key
1431
    assert_equal 3, firm.clients.count
1432
    firm.clients.first.update_columns(firm_id: nil)
1433
    assert_equal 2, firm.clients.reload.count
1434
    assert_equal 2, firm.clients_using_primary_key_with_delete_all.count
1435
    old_record = firm.clients_using_primary_key_with_delete_all.first
1436
    firm = Firm.first
1437
    firm.destroy
1438
    assert_nil Client.find_by_id(old_record.id)
1439
  end
1440

1441 1442
  def test_creation_respects_hash_condition
    ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.build
1443

1444
    assert        ms_client.save
1445
    assert_equal  "Microsoft", ms_client.name
1446

1447 1448
    another_ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.create

1449
    assert_predicate        another_ms_client, :persisted?
1450
    assert_equal  "Microsoft", another_ms_client.name
1451 1452 1453 1454 1455 1456 1457 1458
  end

  def test_clearing_without_initial_access
    firm = companies(:first_firm)

    firm.clients_of_firm.clear

    assert_equal 0, firm.clients_of_firm.size
1459
    assert_equal 0, firm.clients_of_firm.reload.size
1460 1461 1462 1463
  end

  def test_deleting_a_item_which_is_not_in_the_collection
    force_signal37_to_load_all_clients_of_firm
1464 1465 1466

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1467
    summit = Client.find_by_name("Summit")
1468
    companies(:first_firm).clients_of_firm.delete(summit)
1469
    assert_equal 2, companies(:first_firm).clients_of_firm.size
1470
    assert_equal 2, companies(:first_firm).clients_of_firm.reload.size
1471 1472 1473
    assert_equal 2, summit.client_of
  end

1474
  def test_deleting_by_integer_id
1475
    david = Developer.find(1)
1476

1477
    assert_difference "david.projects.count", -1 do
1478 1479 1480 1481 1482 1483 1484 1485 1486
      assert_equal 1, david.projects.delete(1).size
    end

    assert_equal 1, david.projects.size
  end

  def test_deleting_by_string_id
    david = Developer.find(1)

1487 1488
    assert_difference "david.projects.count", -1 do
      assert_equal 1, david.projects.delete("1").size
1489 1490 1491
    end

    assert_equal 1, david.projects.size
1492 1493 1494 1495 1496
  end

  def test_deleting_self_type_mismatch
    david = Developer.find(1)
    david.projects.reload
1497
    assert_raise(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(Project.find(1).developers) }
1498 1499
  end

1500 1501 1502
  def test_destroying
    force_signal37_to_load_all_clients_of_firm

1503 1504
    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1505 1506 1507 1508
    assert_difference "Client.count", -1 do
      companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first)
    end

1509
    assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
1510
    assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
1511 1512
  end

1513
  def test_destroying_by_integer_id
1514 1515
    force_signal37_to_load_all_clients_of_firm

1516 1517
    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1518 1519 1520 1521
    assert_difference "Client.count", -1 do
      companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id)
    end

1522
    assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
1523
    assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
1524 1525 1526 1527 1528
  end

  def test_destroying_by_string_id
    force_signal37_to_load_all_clients_of_firm

1529 1530
    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1531 1532 1533 1534
    assert_difference "Client.count", -1 do
      companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id.to_s)
    end

1535
    assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
1536
    assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
1537 1538
  end

1539 1540
  def test_destroying_a_collection
    force_signal37_to_load_all_clients_of_firm
1541 1542 1543

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1544
    companies(:first_firm).clients_of_firm.create("name" => "Another Client")
1545
    assert_equal 3, companies(:first_firm).clients_of_firm.size
1546 1547 1548 1549 1550

    assert_difference "Client.count", -2 do
      companies(:first_firm).clients_of_firm.destroy([companies(:first_firm).clients_of_firm[0], companies(:first_firm).clients_of_firm[1]])
    end

1551
    assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
1552
    assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
1553 1554
  end

1555 1556
  def test_destroy_all
    force_signal37_to_load_all_clients_of_firm
1557 1558 1559

    assert_predicate companies(:first_firm).clients_of_firm, :loaded?

1560 1561 1562 1563
    clients = companies(:first_firm).clients_of_firm.to_a
    assert !clients.empty?, "37signals has clients after load"
    destroyed = companies(:first_firm).clients_of_firm.destroy_all
    assert_equal clients.sort_by(&:id), destroyed.sort_by(&:id)
1564
    assert destroyed.all?(&:frozen?), "destroyed clients should be frozen"
1565
    assert companies(:first_firm).clients_of_firm.empty?, "37signals has no clients after destroy all"
1566
    assert companies(:first_firm).clients_of_firm.reload.empty?, "37signals has no clients after destroy all and refresh"
1567 1568 1569 1570
  end

  def test_dependence
    firm = companies(:first_firm)
1571
    assert_equal 3, firm.clients.size
1572
    firm.destroy
1573
    assert_empty Client.all.merge!(where: "firm_id=#{firm.id}").to_a
1574 1575
  end

1576 1577
  def test_dependence_for_associations_with_hash_condition
    david = authors(:david)
1578
    assert_difference("Post.count", -1) { assert david.destroy }
1579 1580
  end

1581
  def test_destroy_dependent_when_deleted_from_association
1582
    firm = Firm.first
1583
    assert_equal 3, firm.clients.size
1584 1585 1586 1587 1588 1589

    client = firm.clients.first
    firm.clients.delete(client)

    assert_raise(ActiveRecord::RecordNotFound) { Client.find(client.id) }
    assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(client.id) }
1590
    assert_equal 2, firm.clients.size
1591 1592 1593 1594 1595
  end

  def test_three_levels_of_dependence
    topic = Topic.create "title" => "neat and simple"
    reply = topic.replies.create "title" => "neat and simple", "content" => "still digging it"
1596
    reply.replies.create "title" => "neat and simple", "content" => "ain't complaining"
1597 1598 1599 1600 1601 1602 1603

    assert_nothing_raised { topic.destroy }
  end

  def test_dependence_with_transaction_support_on_failure
    firm = companies(:first_firm)
    clients = firm.clients
1604
    assert_equal 3, clients.length
1605
    clients.last.instance_eval { def overwrite_to_raise() raise "Trigger rollback" end }
1606 1607 1608

    firm.destroy rescue "do nothing"

1609
    assert_equal 3, Client.all.merge!(where: "firm_id=#{firm.id}").to_a.size
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631
  end

  def test_dependence_on_account
    num_accounts = Account.count
    companies(:first_firm).destroy
    assert_equal num_accounts - 1, Account.count
  end

  def test_depends_and_nullify
    num_accounts = Account.count

    core = companies(:rails_core)
    assert_equal accounts(:rails_core_account), core.account
    assert_equal companies(:leetsoft, :jadedpixel), core.companies
    core.destroy
    assert_nil accounts(:rails_core_account).reload.firm_id
    assert_nil companies(:leetsoft).reload.client_of
    assert_nil companies(:jadedpixel).reload.client_of

    assert_equal num_accounts, Account.count
  end

1632
  def test_restrict_with_exception
1633 1634
    firm = RestrictedWithExceptionFirm.create!(name: "restrict")
    firm.companies.create(name: "child")
1635

1636
    assert_not_empty firm.companies
1637
    assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
1638 1639
    assert RestrictedWithExceptionFirm.exists?(name: "restrict")
    assert firm.companies.exists?(name: "child")
1640 1641 1642
  end

  def test_restrict_with_error
1643 1644
    firm = RestrictedWithErrorFirm.create!(name: "restrict")
    firm.companies.create(name: "child")
1645

1646
    assert_not_empty firm.companies
1647 1648 1649

    firm.destroy

1650
    assert_not_empty firm.errors
1651

1652
    assert_equal "Cannot delete record because dependent companies exist", firm.errors[:base].first
1653 1654
    assert RestrictedWithErrorFirm.exists?(name: "restrict")
    assert firm.companies.exists?(name: "child")
1655 1656
  end

1657 1658
  def test_restrict_with_error_with_locale
    I18n.backend = I18n::Backend::Simple.new
1659
    I18n.backend.store_translations "en", activerecord: { attributes: { restricted_with_error_firm: { companies: "client companies" } } }
1660 1661
    firm = RestrictedWithErrorFirm.create!(name: "restrict")
    firm.companies.create(name: "child")
1662

1663
    assert_not_empty firm.companies
1664 1665 1666

    firm.destroy

1667
    assert_not_empty firm.errors
1668 1669

    assert_equal "Cannot delete record because dependent client companies exist", firm.errors[:base].first
1670 1671
    assert RestrictedWithErrorFirm.exists?(name: "restrict")
    assert firm.companies.exists?(name: "child")
1672 1673 1674 1675
  ensure
    I18n.backend.reload!
  end

1676
  def test_included_in_collection
1677
    assert_equal true, companies(:first_firm).clients.include?(Client.find(2))
1678 1679
  end

1680
  def test_included_in_collection_for_new_records
1681
    client = Client.create(name: "Persisted")
1682
    assert_nil client.client_of
1683
    assert_equal false, Firm.new.clients_of_firm.include?(client),
1684
     "includes a client that does not belong to any firm"
1685 1686
  end

1687
  def test_adding_array_and_collection
J
Jon Leighton 已提交
1688
    assert_nothing_raised { Firm.first.clients + Firm.all.last.clients }
1689 1690 1691
  end

  def test_replace_with_less
1692
    firm = Firm.first
1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705
    firm.clients = [companies(:first_client)]
    assert firm.save, "Could not save firm"
    firm.reload
    assert_equal 1, firm.clients.length
  end

  def test_replace_with_less_and_dependent_nullify
    num_companies = Company.count
    companies(:rails_core).companies = []
    assert_equal num_companies, Company.count
  end

  def test_replace_with_new
1706
    firm = Firm.first
1707 1708 1709 1710
    firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
    firm.save
    firm.reload
    assert_equal 2, firm.clients.length
1711
    assert_equal false, firm.clients.include?(:first_client)
1712 1713
  end

1714 1715 1716 1717 1718
  def test_replace_failure
    firm = companies(:first_firm)
    account = Account.new
    orig_accounts = firm.accounts.to_a

1719
    assert_not_predicate account, :valid?
1720
    assert_not_empty orig_accounts
1721
    error = assert_raise ActiveRecord::RecordNotSaved do
1722 1723
      firm.accounts = [account]
    end
1724

1725
    assert_equal orig_accounts, firm.accounts
1726 1727
    assert_equal "Failed to replace accounts because one or more of the " \
                 "new records could not be saved.", error.message
1728 1729
  end

1730 1731 1732 1733 1734 1735 1736 1737
  def test_replace_with_same_content
    firm = Firm.first
    firm.clients = []
    firm.save

    assert_queries(0, ignore_none: true) do
      firm.clients = []
    end
1738

1739
    assert_equal [], firm.send("clients=", [])
1740 1741
  end

1742
  def test_transactions_when_replacing_on_persisted
1743 1744
    good = Client.new(name: "Good")
    bad  = Client.new(name: "Bad", raise_on_save: true)
1745

J
Jon Leighton 已提交
1746 1747 1748 1749 1750 1751 1752
    companies(:first_firm).clients_of_firm = [good]

    begin
      companies(:first_firm).clients_of_firm = [bad]
    rescue Client::RaisedOnSave
    end

1753
    assert_equal [good], companies(:first_firm).clients_of_firm.reload
1754 1755 1756
  end

  def test_transactions_when_replacing_on_new_record
1757
    assert_no_queries(ignore_none: false) do
J
Jon Leighton 已提交
1758 1759 1760
      firm = Firm.new
      firm.clients_of_firm = [Client.new("name" => "New Client")]
    end
1761 1762
  end

1763
  def test_get_ids
1764
    assert_equal [companies(:first_client).id, companies(:second_client).id, companies(:another_first_firm_client).id], companies(:first_firm).client_ids
1765 1766
  end

1767 1768
  def test_get_ids_for_loaded_associations
    company = companies(:first_firm)
1769
    company.clients.reload
1770 1771 1772 1773 1774 1775 1776 1777
    assert_queries(0) do
      company.client_ids
      company.client_ids
    end
  end

  def test_get_ids_for_unloaded_associations_does_not_load_them
    company = companies(:first_firm)
1778
    assert_not_predicate company.clients, :loaded?
1779
    assert_equal [companies(:first_client).id, companies(:second_client).id, companies(:another_first_firm_client).id], company.client_ids
1780
    assert_not_predicate company.clients, :loaded?
1781 1782
  end

1783 1784 1785 1786 1787
  def test_counter_cache_on_unloaded_association
    car = Car.create(name: "My AppliCar")
    assert_equal car.engines.size, 0
  end

1788 1789 1790 1791
  def test_get_ids_ignores_include_option
    assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids
  end

1792
  def test_get_ids_for_ordered_association
1793
    assert_equal [companies(:another_first_firm_client).id, companies(:second_client).id, companies(:first_client).id], companies(:first_firm).clients_ordered_by_name_ids
1794 1795
  end

1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810
  def test_get_ids_for_association_on_new_record_does_not_try_to_find_records
    Company.columns  # Load schema information so we don't query below
    Contract.columns # if running just this test.

    company = Company.new
    assert_queries(0) do
      company.contract_ids
    end

    assert_equal [], company.contract_ids
  end

  def test_set_ids_for_association_on_new_record_applies_association_correctly
    contract_a = Contract.create!
    contract_b = Contract.create!
1811
    Contract.create! # another contract
1812
    company = Company.new(name: "Some Company")
1813 1814 1815 1816 1817 1818 1819 1820 1821 1822

    company.contract_ids = [contract_a.id, contract_b.id]
    assert_equal [contract_a.id, contract_b.id], company.contract_ids
    assert_equal [contract_a, contract_b], company.contracts

    company.save!
    assert_equal company, contract_a.reload.company
    assert_equal company, contract_b.reload.company
  end

1823
  def test_assign_ids_ignoring_blanks
1824
    firm = Firm.create!(name: "Apple")
1825
    firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, ""]
1826 1827
    firm.save!

1828
    assert_equal 2, firm.clients.reload.size
1829
    assert_equal true, firm.clients.include?(companies(:second_client))
1830 1831 1832 1833 1834 1835 1836 1837 1838 1839
  end

  def test_get_ids_for_through
    assert_equal [comments(:eager_other_comment1).id], authors(:mary).comment_ids
  end

  def test_modifying_a_through_a_has_many_should_raise
    [
      lambda { authors(:mary).comment_ids = [comments(:greetings).id, comments(:more_greetings).id] },
      lambda { authors(:mary).comments = [comments(:greetings), comments(:more_greetings)] },
1840
      lambda { authors(:mary).comments << Comment.create!(body: "Yay", post_id: 424242) },
1841
      lambda { authors(:mary).comments.delete(authors(:mary).comments.first) },
1842
    ].each { |block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
1843 1844 1845
  end

  def test_dynamic_find_should_respect_association_order_for_through
1846
    assert_equal Comment.find(10), authors(:david).comments_desc.where("comments.type = 'SpecialComment'").first
1847
    assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type("SpecialComment")
1848 1849 1850 1851 1852 1853 1854 1855 1856
  end

  def test_has_many_through_respects_hash_conditions
    assert_equal authors(:david).hello_posts, authors(:david).hello_posts_with_hash_conditions
    assert_equal authors(:david).hello_post_comments, authors(:david).hello_post_comments_with_hash_conditions
  end

  def test_include_uses_array_include_after_loaded
    firm = companies(:first_firm)
J
Jon Leighton 已提交
1857
    firm.clients.load_target
1858

1859 1860 1861
    client = firm.clients.first

    assert_no_queries do
1862
      assert_predicate firm.clients, :loaded?
1863
      assert_equal true, firm.clients.include?(client)
1864 1865 1866 1867 1868 1869 1870 1871
    end
  end

  def test_include_checks_if_record_exists_if_target_not_loaded
    firm = companies(:first_firm)
    client = firm.clients.first

    firm.reload
1872
    assert_not_predicate  firm.clients, :loaded?
1873
    assert_queries(1) do
1874
      assert_equal true, firm.clients.include?(client)
1875
    end
1876
    assert_not_predicate  firm.clients, :loaded?
1877 1878 1879 1880
  end

  def test_include_returns_false_for_non_matching_record_to_verify_scoping
    firm = companies(:first_firm)
1881
    client = Client.create!(name: "Not Associated")
1882

1883
    assert_not_predicate  firm.clients, :loaded?
1884
    assert_equal false, firm.clients.include?(client)
1885 1886
  end

1887
  def test_calling_first_nth_or_last_on_association_should_not_load_association
1888 1889
    firm = companies(:first_firm)
    firm.clients.first
1890
    firm.clients.second
1891
    firm.clients.last
1892
    assert_not_predicate firm.clients, :loaded?
1893 1894 1895 1896
  end

  def test_calling_first_or_last_on_loaded_association_should_not_fetch_with_query
    firm = companies(:first_firm)
J
Jon Leighton 已提交
1897
    firm.clients.load_target
1898
    assert_predicate firm.clients, :loaded?
1899

1900
    assert_no_queries(ignore_none: false) do
1901 1902 1903 1904 1905 1906 1907 1908 1909
      firm.clients.first
      assert_equal 2, firm.clients.first(2).size
      firm.clients.last
      assert_equal 2, firm.clients.last(2).size
    end
  end

  def test_calling_first_or_last_on_existing_record_with_build_should_load_association
    firm = companies(:first_firm)
1910
    firm.clients.build(name: "Foo")
1911
    assert_not_predicate firm.clients, :loaded?
1912 1913 1914

    assert_queries 1 do
      firm.clients.first
1915
      firm.clients.second
1916 1917 1918
      firm.clients.last
    end

1919
    assert_predicate firm.clients, :loaded?
1920 1921
  end

1922
  def test_calling_first_nth_or_last_on_existing_record_with_create_should_not_load_association
1923
    firm = companies(:first_firm)
1924
    firm.clients.create(name: "Foo")
1925
    assert_not_predicate firm.clients, :loaded?
1926

1927
    assert_queries 3 do
1928
      firm.clients.first
1929
      firm.clients.second
1930 1931 1932
      firm.clients.last
    end

1933
    assert_not_predicate firm.clients, :loaded?
1934 1935
  end

1936
  def test_calling_first_nth_or_last_on_new_record_should_not_run_queries
1937 1938 1939 1940
    firm = Firm.new

    assert_no_queries do
      firm.clients.first
1941
      firm.clients.second
1942 1943 1944
      firm.clients.last
    end
  end
1945

1946
  def test_calling_first_or_last_with_integer_on_association_should_not_load_association
1947
    firm = companies(:first_firm)
1948
    firm.clients.create(name: "Foo")
1949
    assert_not_predicate firm.clients, :loaded?
1950

1951
    assert_queries 2 do
1952 1953 1954 1955
      firm.clients.first(2)
      firm.clients.last(2)
    end

1956
    assert_not_predicate firm.clients, :loaded?
1957 1958
  end

1959 1960 1961 1962 1963
  def test_calling_many_should_count_instead_of_loading_association
    firm = companies(:first_firm)
    assert_queries(1) do
      firm.clients.many?  # use count query
    end
1964
    assert_not_predicate firm.clients, :loaded?
1965 1966 1967 1968
  end

  def test_calling_many_on_loaded_association_should_not_use_query
    firm = companies(:first_firm)
1969
    firm.clients.load  # force load
1970 1971 1972 1973 1974 1975 1976 1977 1978
    assert_no_queries { assert firm.clients.many? }
  end

  def test_calling_many_should_defer_to_collection_if_using_a_block
    firm = companies(:first_firm)
    assert_queries(1) do
      firm.clients.expects(:size).never
      firm.clients.many? { true }
    end
1979
    assert_predicate firm.clients, :loaded?
1980 1981 1982 1983
  end

  def test_calling_many_should_return_false_if_none_or_one
    firm = companies(:another_firm)
1984
    assert_not_predicate firm.clients_like_ms, :many?
1985 1986 1987
    assert_equal 0, firm.clients_like_ms.size

    firm = companies(:first_firm)
1988
    assert_not_predicate firm.limited_clients, :many?
1989 1990 1991 1992 1993
    assert_equal 1, firm.limited_clients.size
  end

  def test_calling_many_should_return_true_if_more_than_one
    firm = companies(:first_firm)
1994
    assert_predicate firm.clients, :many?
1995
    assert_equal 3, firm.clients.size
1996 1997
  end

1998 1999 2000 2001 2002
  def test_calling_none_should_count_instead_of_loading_association
    firm = companies(:first_firm)
    assert_queries(1) do
      firm.clients.none?  # use count query
    end
2003
    assert_not_predicate firm.clients, :loaded?
2004 2005 2006 2007
  end

  def test_calling_none_on_loaded_association_should_not_use_query
    firm = companies(:first_firm)
2008
    firm.clients.load  # force load
2009 2010 2011 2012 2013 2014 2015 2016 2017
    assert_no_queries { assert ! firm.clients.none? }
  end

  def test_calling_none_should_defer_to_collection_if_using_a_block
    firm = companies(:first_firm)
    assert_queries(1) do
      firm.clients.expects(:size).never
      firm.clients.none? { true }
    end
2018
    assert_predicate firm.clients, :loaded?
2019 2020 2021 2022
  end

  def test_calling_none_should_return_true_if_none
    firm = companies(:another_firm)
2023
    assert_predicate firm.clients_like_ms, :none?
2024 2025 2026 2027 2028
    assert_equal 0, firm.clients_like_ms.size
  end

  def test_calling_none_should_return_false_if_any
    firm = companies(:first_firm)
2029
    assert_not_predicate firm.limited_clients, :none?
2030 2031 2032 2033 2034 2035 2036 2037
    assert_equal 1, firm.limited_clients.size
  end

  def test_calling_one_should_count_instead_of_loading_association
    firm = companies(:first_firm)
    assert_queries(1) do
      firm.clients.one?  # use count query
    end
2038
    assert_not_predicate firm.clients, :loaded?
2039 2040 2041 2042
  end

  def test_calling_one_on_loaded_association_should_not_use_query
    firm = companies(:first_firm)
2043
    firm.clients.load  # force load
2044 2045 2046 2047 2048 2049 2050 2051 2052
    assert_no_queries { assert ! firm.clients.one? }
  end

  def test_calling_one_should_defer_to_collection_if_using_a_block
    firm = companies(:first_firm)
    assert_queries(1) do
      firm.clients.expects(:size).never
      firm.clients.one? { true }
    end
2053
    assert_predicate firm.clients, :loaded?
2054 2055 2056 2057
  end

  def test_calling_one_should_return_false_if_zero
    firm = companies(:another_firm)
2058
    assert_not_predicate  firm.clients_like_ms, :one?
2059 2060 2061 2062 2063
    assert_equal 0, firm.clients_like_ms.size
  end

  def test_calling_one_should_return_true_if_one
    firm = companies(:first_firm)
2064
    assert_predicate firm.limited_clients, :one?
2065 2066 2067 2068 2069
    assert_equal 1, firm.limited_clients.size
  end

  def test_calling_one_should_return_false_if_more_than_one
    firm = companies(:first_firm)
2070
    assert_not_predicate  firm.clients, :one?
2071 2072 2073
    assert_equal 3, firm.clients.size
  end

2074 2075 2076 2077
  def test_joins_with_namespaced_model_should_use_correct_type
    old = ActiveRecord::Base.store_full_sti_class
    ActiveRecord::Base.store_full_sti_class = true

2078 2079
    firm = Namespaced::Firm.create(name: "Some Company")
    firm.clients.create(name: "Some Client")
2080

2081
    stats = Namespaced::Firm.all.merge!(
2082 2083 2084
      select: "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
      joins: :clients,
      group: "#{Namespaced::Firm.table_name}.id"
J
Jon Leighton 已提交
2085
    ).find firm.id
2086 2087 2088 2089 2090
    assert_equal 1, stats.num_clients.to_i
  ensure
    ActiveRecord::Base.store_full_sti_class = old
  end

2091 2092
  def test_association_proxy_transaction_method_starts_transaction_in_association_class
    Comment.expects(:transaction)
2093
    Post.first.comments.transaction do
2094
      # nothing
2095 2096 2097
    end
  end

2098
  def test_sending_new_to_association_proxy_should_have_same_effect_as_calling_new
2099 2100 2101 2102
    client_association = companies(:first_firm).clients
    assert_equal client_association.new.attributes, client_association.send(:new).attributes
  end

2103
  def test_creating_using_primary_key
2104
    firm = Firm.first
2105
    client = firm.clients_using_primary_key.create!(name: "test")
2106 2107
    assert_equal firm.name, client.firm_name
  end
2108 2109 2110

  def test_defining_has_many_association_with_delete_all_dependency_lazily_evaluates_target_class
    ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
2111
    class_eval(<<-EOF, __FILE__, __LINE__ + 1)
2112 2113 2114 2115 2116 2117 2118 2119
      class DeleteAllModel < ActiveRecord::Base
        has_many :nonentities, :dependent => :delete_all
      end
    EOF
  end

  def test_defining_has_many_association_with_nullify_dependency_lazily_evaluates_target_class
    ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
2120
    class_eval(<<-EOF, __FILE__, __LINE__ + 1)
2121 2122 2123 2124 2125
      class NullifyModel < ActiveRecord::Base
        has_many :nonentities, :dependent => :nullify
      end
    EOF
  end
2126 2127

  def test_attributes_are_being_set_when_initialized_from_has_many_association_with_where_clause
2128
    new_comment = posts(:welcome).comments.where(body: "Some content").build
2129 2130 2131 2132
    assert_equal new_comment.body, "Some content"
  end

  def test_attributes_are_being_set_when_initialized_from_has_many_association_with_multiple_where_clauses
2133
    new_comment = posts(:welcome).comments.where(body: "Some content").where(type: "SpecialComment").build
2134 2135 2136 2137
    assert_equal new_comment.body, "Some content"
    assert_equal new_comment.type, "SpecialComment"
    assert_equal new_comment.post_id, posts(:welcome).id
  end
2138 2139 2140 2141

  def test_include_method_in_has_many_association_should_return_true_for_instance_added_with_build
    post = Post.new
    comment = post.comments.build
2142
    assert_equal true, post.comments.include?(comment)
2143
  end
2144 2145 2146

  def test_load_target_respects_protected_attributes
    topic = Topic.create!
2147
    reply = topic.replies.create(title: "reply 1")
2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164
    reply.approved = false
    reply.save!

    # Save with a different object instance, so the instance that's still held
    # in topic.relies doesn't know about the changed attribute.
    reply2 = Reply.find(reply.id)
    reply2.approved = true
    reply2.save!

    # Force loading the collection from the db. This will merge the existing
    # object (reply) with what gets loaded from the db (which includes the
    # changed approved attribute). approved is a protected attribute, so if mass
    # assignment is used, it won't get updated and will still be false.
    first = topic.replies.to_a.first
    assert_equal reply.id, first.id
    assert_equal true, first.approved?
  end
2165 2166 2167 2168 2169 2170 2171

  def test_to_a_should_dup_target
    ary    = topics(:first).replies.to_a
    target = topics(:first).replies.target

    assert_not_equal target.object_id, ary.object_id
  end
2172 2173

  def test_merging_with_custom_attribute_writer
2174
    bulb = Bulb.new(color: "red")
2175 2176 2177 2178 2179 2180 2181
    assert_equal "RED!", bulb.color

    car = Car.create!
    car.bulbs << bulb

    assert_equal "RED!", car.bulbs.to_a.first.color
  end
2182

2183
  def test_abstract_class_with_polymorphic_has_many
2184 2185
    post = SubStiPost.create! title: "fooo", body: "baa"
    tagging = Tagging.create! taggable: post
2186 2187
    assert_equal [tagging], post.taggings
  end
F
Farley Knight 已提交
2188

2189
  def test_with_polymorphic_has_many_with_custom_columns_name
2190
    post = Post.create! title: "foo", body: "bar"
2191 2192 2193 2194 2195 2196 2197
    image = Image.create!

    post.images << image

    assert_equal [image], post.images
  end

A
Andy Lindeman 已提交
2198
  def test_build_with_polymorphic_has_many_does_not_allow_to_override_type_and_id
2199
    welcome = posts(:welcome)
2200
    tagging = welcome.taggings.build(taggable_id: 99, taggable_type: "ShouldNotChange")
2201 2202

    assert_equal welcome.id, tagging.taggable_id
2203
    assert_equal "Post", tagging.taggable_type
2204 2205
  end

2206 2207 2208 2209 2210 2211 2212
  def test_build_from_polymorphic_association_sets_inverse_instance
    post = Post.new
    tagging = post.taggings.build

    assert_equal post, tagging.taggable
  end

F
Farley Knight 已提交
2213 2214 2215 2216 2217 2218 2219
  def test_dont_call_save_callbacks_twice_on_has_many
    firm = companies(:first_firm)
    contract = firm.contracts.create!

    assert_equal 1, contract.hi_count
    assert_equal 1, contract.bye_count
  end
2220 2221

  def test_association_attributes_are_available_to_after_initialize
2222
    car = Car.create(name: "honda")
2223 2224
    bulb = car.bulbs.build

2225
    assert_equal car.id, bulb.attributes_after_initialize["car_id"]
2226
  end
2227

2228
  def test_attributes_are_set_when_initialized_from_has_many_null_relationship
2229 2230 2231
    car  = Car.new name: "honda"
    bulb = car.bulbs.where(name: "headlight").first_or_initialize
    assert_equal "headlight", bulb.name
2232 2233 2234
  end

  def test_attributes_are_set_when_initialized_from_polymorphic_has_many_null_relationship
2235 2236
    post    = Post.new title: "title", body: "bar"
    tag     = Tag.create!(name: "foo")
2237 2238 2239 2240

    tagging = post.taggings.where(tag: tag).first_or_initialize

    assert_equal tag.id, tagging.tag_id
2241
    assert_equal "Post", tagging.taggable_type
2242 2243
  end

2244
  def test_replace
2245
    car = Car.create(name: "honda")
2246 2247 2248 2249 2250 2251 2252 2253
    bulb1 = car.bulbs.create
    bulb2 = Bulb.create

    assert_equal [bulb1], car.bulbs
    car.bulbs.replace([bulb2])
    assert_equal [bulb2], car.bulbs
    assert_equal [bulb2], car.reload.bulbs
  end
2254

2255
  def test_replace_returns_target
2256
    car = Car.create(name: "honda")
2257 2258 2259 2260 2261
    bulb1 = car.bulbs.create
    bulb2 = car.bulbs.create
    bulb3 = Bulb.create

    assert_equal [bulb1, bulb2], car.bulbs
2262
    result = car.bulbs.replace([bulb3, bulb1])
2263 2264 2265 2266
    assert_equal [bulb1, bulb3], car.bulbs
    assert_equal [bulb1, bulb3], result
  end

2267 2268 2269 2270
  def test_collection_association_with_private_kernel_method
    firm = companies(:first_firm)
    assert_equal [accounts(:signals37)], firm.accounts.open
  end
J
Jon Leighton 已提交
2271 2272

  test "first_or_initialize adds the record to the association" do
2273
    firm = Firm.create! name: "omg"
J
Jon Leighton 已提交
2274 2275 2276 2277 2278
    client = firm.clients_of_firm.first_or_initialize
    assert_equal [client], firm.clients_of_firm
  end

  test "first_or_create adds the record to the association" do
2279
    firm = Firm.create! name: "omg"
J
Jon Leighton 已提交
2280
    firm.clients_of_firm.load_target
2281
    client = firm.clients_of_firm.first_or_create name: "lol"
J
Jon Leighton 已提交
2282 2283 2284
    assert_equal [client], firm.clients_of_firm
    assert_equal [client], firm.reload.clients_of_firm
  end
2285 2286 2287 2288 2289

  test "delete_all, when not loaded, doesn't load the records" do
    post = posts(:welcome)

    assert post.taggings_with_delete_all.count > 0
2290
    assert_not_predicate post.taggings_with_delete_all, :loaded?
2291 2292 2293 2294 2295 2296

    # 2 queries: one DELETE and another to update the counter cache
    assert_queries(2) do
      post.taggings_with_delete_all.delete_all
    end
  end
2297

2298 2299 2300
  test "has many associations on new records use null relations" do
    post = Post.new

2301
    assert_no_queries(ignore_none: false) do
2302
      assert_equal [], post.comments
2303
      assert_equal [], post.comments.where(body: "omg")
2304
      assert_equal [], post.comments.pluck(:body)
2305
      assert_equal 0,  post.comments.sum(:id)
V
Vipul A M 已提交
2306
      assert_equal 0,  post.comments.count
2307 2308
    end
  end
2309 2310 2311

  test "collection proxy respects default scope" do
    author = authors(:mary)
2312
    assert_not_predicate author.first_posts, :exists?
2313
  end
J
Jon Leighton 已提交
2314 2315 2316 2317 2318 2319 2320 2321 2322 2323

  test "association with extend option" do
    post = posts(:welcome)
    assert_equal "lifo",  post.comments_with_extend.author
    assert_equal "hello", post.comments_with_extend.greeting
  end

  test "association with extend option with multiple extensions" do
    post = posts(:welcome)
    assert_equal "lifo",  post.comments_with_extend_2.author
2324 2325 2326 2327 2328 2329 2330 2331 2332
    assert_equal "hullo", post.comments_with_extend_2.greeting
  end

  test "extend option affects per association" do
    post = posts(:welcome)
    assert_equal "lifo",  post.comments_with_extend.author
    assert_equal "lifo",  post.comments_with_extend_2.author
    assert_equal "hello", post.comments_with_extend.greeting
    assert_equal "hullo", post.comments_with_extend_2.greeting
J
Jon Leighton 已提交
2333
  end
2334 2335 2336 2337 2338

  test "delete record with complex joins" do
    david = authors(:david)

    post = david.posts.first
2339
    post.type = "PostWithSpecialCategorization"
2340 2341 2342 2343 2344 2345 2346 2347 2348 2349
    post.save

    categorization = post.categorizations.first
    categorization.special = true
    categorization.save

    assert_not_equal [], david.posts_with_special_categorizations
    david.posts_with_special_categorizations = []
    assert_equal [], david.posts_with_special_categorizations
  end
2350 2351

  test "does not duplicate associations when used with natural primary keys" do
2352
    speedometer = Speedometer.create!(id: "4")
2353
    speedometer.minivans.create!(minivan_id: "a-van-red", name: "a van", color: "red")
2354 2355 2356 2357

    assert_equal 1, speedometer.minivans.to_a.size, "Only one association should be present:\n#{speedometer.minivans.to_a}"
    assert_equal 1, speedometer.reload.minivans.to_a.size
  end
J
Jon Leighton 已提交
2358 2359 2360 2361 2362 2363 2364 2365 2366

  test "can unscope the default scope of the associated model" do
    car = Car.create!
    bulb1 = Bulb.create! name: "defaulty", car: car
    bulb2 = Bulb.create! name: "other",    car: car

    assert_equal [bulb1], car.bulbs
    assert_equal [bulb1, bulb2], car.all_bulbs.sort_by(&:id)
  end
2367

2368
  test "can unscope and where the default scope of the associated model" do
2369
    Car.has_many :other_bulbs, -> { unscope(where: [:name]).where(name: "other") }, class_name: "Bulb"
2370 2371 2372 2373 2374 2375 2376 2377 2378
    car = Car.create!
    bulb1 = Bulb.create! name: "defaulty", car: car
    bulb2 = Bulb.create! name: "other",    car: car

    assert_equal [bulb1], car.bulbs
    assert_equal [bulb2], car.other_bulbs
  end

  test "can rewhere the default scope of the associated model" do
2379
    Car.has_many :old_bulbs, -> { rewhere(name: "old") }, class_name: "Bulb"
2380 2381 2382 2383 2384 2385 2386 2387
    car = Car.create!
    bulb1 = Bulb.create! name: "defaulty", car: car
    bulb2 = Bulb.create! name: "old",      car: car

    assert_equal [bulb1], car.bulbs
    assert_equal [bulb2], car.old_bulbs
  end

2388
  test "unscopes the default scope of associated model when used with include" do
2389 2390 2391
    car = Car.create!
    bulb = Bulb.create! name: "other", car: car

2392 2393 2394
    assert_equal [bulb], Car.find(car.id).all_bulbs
    assert_equal [bulb], Car.includes(:all_bulbs).find(car.id).all_bulbs
    assert_equal [bulb], Car.eager_load(:all_bulbs).find(car.id).all_bulbs
2395 2396
  end

2397 2398 2399 2400
  test "raises RecordNotDestroyed when replaced child can't be destroyed" do
    car = Car.create!
    original_child = FailedBulb.create!(car: car)

2401
    error = assert_raise(ActiveRecord::RecordNotDestroyed) do
2402 2403 2404 2405
      car.failed_bulbs = [FailedBulb.create!]
    end

    assert_equal [original_child], car.reload.failed_bulbs
2406
    assert_equal "Failed to destroy the record", error.message
2407
  end
2408

2409
  test "updates counter cache when default scope is given" do
2410 2411 2412 2413 2414
    topic = DefaultRejectedTopic.create approved: true

    assert_difference "topic.reload.replies_count", 1 do
      topic.approved_replies.create!
    end
2415
  end
2416

2417 2418
  test "dangerous association name raises ArgumentError" do
    [:errors, "errors", :save, "save"].each do |name|
2419 2420 2421 2422 2423 2424 2425
      assert_raises(ArgumentError, "Association #{name} should not be allowed") do
        Class.new(ActiveRecord::Base) do
          has_many name
        end
      end
    end
  end
2426

2427
  test "passes custom context validation to validate children" do
2428 2429
    pirate = FamousPirate.new
    pirate.famous_ships << ship = FamousShip.new
2430

2431
    assert_predicate pirate, :valid?
2432
    assert_not pirate.valid?(:conference)
2433 2434
    assert_equal "can't be blank", ship.errors[:name].first
  end
2435

2436
  test "association with instance dependent scope" do
2437 2438 2439 2440 2441 2442 2443 2444
    bob = authors(:bob)
    Post.create!(title: "signed post by bob", body: "stuff", author: authors(:bob))
    Post.create!(title: "anonymous post", body: "more stuff", author: authors(:bob))
    assert_equal ["misc post by bob", "other post by bob",
                  "signed post by bob"], bob.posts_with_signature.map(&:title).sort

    assert_equal [], authors(:david).posts_with_signature.map(&:title)
  end
2445

2446
  test "associations autosaves when object is already persisted" do
2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457
    bulb = Bulb.create!
    tyre = Tyre.create!

    car = Car.create! do |c|
      c.bulbs << bulb
      c.tyres << tyre
    end

    assert_equal 1, car.bulbs.count
    assert_equal 1, car.tyres.count
  end
2458

2459
  test "associations replace in memory when records have the same id" do
2460 2461 2462 2463 2464 2465 2466 2467 2468 2469
    bulb = Bulb.create!
    car = Car.create!(bulbs: [bulb])

    new_bulb = Bulb.find(bulb.id)
    new_bulb.name = "foo"
    car.bulbs = [new_bulb]

    assert_equal "foo", car.bulbs.first.name
  end

2470
  test "in memory replacement executes no queries" do
2471 2472 2473 2474 2475 2476 2477 2478 2479 2480
    bulb = Bulb.create!
    car = Car.create!(bulbs: [bulb])

    new_bulb = Bulb.find(bulb.id)

    assert_no_queries do
      car.bulbs = [new_bulb]
    end
  end

2481
  test "in memory replacements do not execute callbacks" do
2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501
    raise_after_add = false
    klass = Class.new(ActiveRecord::Base) do
      self.table_name = :cars
      has_many :bulbs, after_add: proc { raise if raise_after_add }

      def self.name
        "Car"
      end
    end
    bulb = Bulb.create!
    car = klass.create!(bulbs: [bulb])

    new_bulb = Bulb.find(bulb.id)
    raise_after_add = true

    assert_nothing_raised do
      car.bulbs = [new_bulb]
    end
  end

2502
  test "in memory replacements sets inverse instance" do
2503 2504 2505 2506 2507 2508 2509 2510 2511
    bulb = Bulb.create!
    car = Car.create!(bulbs: [bulb])

    new_bulb = Bulb.find(bulb.id)
    car.bulbs = [new_bulb]

    assert_same car, new_bulb.car
  end

2512 2513 2514 2515 2516 2517 2518 2519 2520
  test "reattach to new objects replaces inverse association and foreign key" do
    bulb = Bulb.create!(car: Car.create!)
    assert bulb.car_id
    car = Car.new
    car.bulbs << bulb
    assert_equal car, bulb.car
    assert_nil bulb.car_id
  end

2521
  test "in memory replacement maintains order" do
2522 2523 2524 2525 2526 2527 2528 2529 2530
    first_bulb = Bulb.create!
    second_bulb = Bulb.create!
    car = Car.create!(bulbs: [first_bulb, second_bulb])

    same_bulb = Bulb.find(first_bulb.id)
    car.bulbs = [second_bulb, same_bulb]

    assert_equal [first_bulb, second_bulb], car.bulbs
  end
2531

2532 2533 2534 2535 2536 2537 2538 2539
  test "association size calculation works with default scoped selects when not previously fetched" do
    firm = Firm.create!(name: "Firm")
    5.times { firm.developers_with_select << Developer.create!(name: "Developer") }

    same_firm = Firm.find(firm.id)
    assert_equal 5, same_firm.developers_with_select.size
  end

2540
  test "prevent double insertion of new object when the parent association loaded in the after save callback" do
2541
    reset_callbacks(:save, Bulb) do
2542
      Bulb.after_save { |record| record.car.bulbs.load }
2543

2544 2545
      car = Car.create!
      car.bulbs << Bulb.new
2546

2547 2548
      assert_equal 1, car.bulbs.size
    end
2549 2550
  end

2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562
  test "prevent double firing the before save callback of new object when the parent association saved in the callback" do
    reset_callbacks(:save, Bulb) do
      count = 0
      Bulb.before_save { |record| record.car.save && count += 1 }

      car = Car.create!
      car.bulbs.create!

      assert_equal 1, count
    end
  end

2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591
  class AuthorWithErrorDestroyingAssociation < ActiveRecord::Base
    self.table_name = "authors"
    has_many :posts_with_error_destroying,
      class_name: "PostWithErrorDestroying",
      foreign_key: :author_id,
      dependent: :destroy
  end

  class PostWithErrorDestroying < ActiveRecord::Base
    self.table_name = "posts"
    self.inheritance_column = nil
    before_destroy -> { throw :abort }
  end

  def test_destroy_does_not_raise_when_association_errors_on_destroy
    assert_no_difference "AuthorWithErrorDestroyingAssociation.count" do
      author = AuthorWithErrorDestroyingAssociation.first

      assert_not author.destroy
    end
  end

  def test_destroy_with_bang_bubbles_errors_from_associations
    error = assert_raises ActiveRecord::RecordNotDestroyed do
      AuthorWithErrorDestroyingAssociation.first.destroy!
    end

    assert_instance_of PostWithErrorDestroying, error.record
  end
2592 2593

  def test_ids_reader_memoization
2594
    car = Car.create!(name: "Tofaş")
2595 2596 2597 2598
    bulb = Bulb.create!(car: car)

    assert_equal [bulb.id], car.bulb_ids
    assert_no_queries { car.bulb_ids }
2599 2600 2601 2602 2603

    bulb2 = car.bulbs.create!

    assert_equal [bulb.id, bulb2.id], car.bulb_ids
    assert_no_queries { car.bulb_ids }
2604
  end
2605

2606 2607
  def test_loading_association_in_validate_callback_doesnt_affect_persistence
    reset_callbacks(:validation, Bulb) do
2608
      Bulb.after_validation { |record| record.car.bulbs.load }
2609 2610 2611 2612 2613 2614 2615 2616

      car = Car.create!(name: "Car")
      bulb = car.bulbs.create!

      assert_equal [bulb], car.bulbs
    end
  end

2617 2618 2619 2620 2621
  private

    def force_signal37_to_load_all_clients_of_firm
      companies(:first_firm).clients_of_firm.load_target
    end
2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635

    def reset_callbacks(kind, klass)
      old_callbacks = {}
      old_callbacks[klass] = klass.send("_#{kind}_callbacks").dup
      klass.subclasses.each do |subclass|
        old_callbacks[subclass] = subclass.send("_#{kind}_callbacks").dup
      end
      yield
    ensure
      klass.send("_#{kind}_callbacks=", old_callbacks[klass])
      klass.subclasses.each do |subclass|
        subclass.send("_#{kind}_callbacks=", old_callbacks[subclass])
      end
    end
2636
end