aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/associations/has_one_through_associations_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/cases/associations/has_one_through_associations_test.rb')
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb404
1 files changed, 404 insertions, 0 deletions
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
new file mode 100644
index 0000000000..9964f084ac
--- /dev/null
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -0,0 +1,404 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/club"
+require "models/member_type"
+require "models/member"
+require "models/membership"
+require "models/sponsor"
+require "models/organization"
+require "models/member_detail"
+require "models/minivan"
+require "models/dashboard"
+require "models/speedometer"
+require "models/category"
+require "models/author"
+require "models/essay"
+require "models/owner"
+require "models/post"
+require "models/comment"
+require "models/categorization"
+require "models/customer"
+require "models/carrier"
+require "models/shop_account"
+require "models/customer_carrier"
+
+class HasOneThroughAssociationsTest < ActiveRecord::TestCase
+ fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans,
+ :dashboards, :speedometers, :authors, :author_addresses, :posts, :comments, :categories, :essays, :owners
+
+ def setup
+ @member = members(:groucho)
+ end
+
+ def test_has_one_through_with_has_one
+ assert_equal clubs(:boring_club), @member.club
+ end
+
+ def test_creating_association_creates_through_record
+ new_member = Member.create(name: "Chris")
+ new_member.club = Club.create(name: "LRUG")
+ assert_not_nil new_member.current_membership
+ assert_not_nil new_member.club
+ end
+
+ def test_creating_association_builds_through_record
+ new_member = Member.create(name: "Chris")
+ new_club = new_member.association(:club).build
+ assert new_member.current_membership
+ assert_equal new_club, new_member.club
+ assert_predicate new_club, :new_record?
+ assert_predicate new_member.current_membership, :new_record?
+ assert new_member.save
+ assert_predicate new_club, :persisted?
+ assert_predicate new_member.current_membership, :persisted?
+ end
+
+ def test_creating_association_builds_through_record_for_new
+ new_member = Member.new(name: "Jane")
+ new_member.club = clubs(:moustache_club)
+ assert new_member.current_membership
+ assert_equal clubs(:moustache_club), new_member.current_membership.club
+ assert_equal clubs(:moustache_club), new_member.club
+ assert new_member.save
+ assert_equal clubs(:moustache_club), new_member.club
+ end
+
+ def test_creating_association_sets_both_parent_ids_for_new
+ member = Member.new(name: "Sean Griffin")
+ club = Club.new(name: "Da Club")
+
+ member.club = club
+
+ member.save!
+
+ assert member.id
+ assert club.id
+ assert_equal member.id, member.current_membership.member_id
+ assert_equal club.id, member.current_membership.club_id
+ end
+
+ def test_replace_target_record
+ new_club = Club.create(name: "Marx Bros")
+ @member.club = new_club
+ @member.reload
+ assert_equal new_club, @member.club
+ end
+
+ def test_replacing_target_record_deletes_old_association
+ assert_no_difference "Membership.count" do
+ new_club = Club.create(name: "Bananarama")
+ @member.club = new_club
+ @member.reload
+ end
+ end
+
+ def test_set_record_to_nil_should_delete_association
+ @member.club = nil
+ @member.reload
+ assert_nil @member.current_membership
+ assert_nil @member.club
+ end
+
+ def test_set_record_after_delete_association
+ @member.club = nil
+ @member.club = clubs(:moustache_club)
+ @member.reload
+ assert_equal clubs(:moustache_club), @member.club
+ end
+
+ def test_has_one_through_polymorphic
+ assert_equal clubs(:moustache_club), @member.sponsor_club
+ end
+
+ def test_has_one_through_eager_loading
+ members = assert_queries(3) do # base table, through table, clubs table
+ Member.all.merge!(includes: :club, where: ["name = ?", "Groucho Marx"]).to_a
+ end
+ assert_equal 1, members.size
+ assert_not_nil assert_no_queries { members[0].club }
+ end
+
+ def test_has_one_through_eager_loading_through_polymorphic
+ members = assert_queries(3) do # base table, through table, clubs table
+ Member.all.merge!(includes: :sponsor_club, where: ["name = ?", "Groucho Marx"]).to_a
+ end
+ assert_equal 1, members.size
+ assert_not_nil assert_no_queries { members[0].sponsor_club }
+ end
+
+ def test_has_one_through_with_conditions_eager_loading
+ # conditions on the through table
+ assert_equal clubs(:moustache_club), Member.all.merge!(includes: :favourite_club).find(@member.id).favourite_club
+ memberships(:membership_of_favourite_club).update_columns(favourite: false)
+ assert_nil Member.all.merge!(includes: :favourite_club).find(@member.id).reload.favourite_club
+
+ # conditions on the source table
+ assert_equal clubs(:moustache_club), Member.all.merge!(includes: :hairy_club).find(@member.id).hairy_club
+ clubs(:moustache_club).update_columns(name: "Association of Clean-Shaven Persons")
+ assert_nil Member.all.merge!(includes: :hairy_club).find(@member.id).reload.hairy_club
+ end
+
+ def test_has_one_through_polymorphic_with_source_type
+ assert_equal members(:groucho), clubs(:moustache_club).sponsored_member
+ end
+
+ def test_eager_has_one_through_polymorphic_with_source_type
+ clubs = Club.all.merge!(includes: :sponsored_member, where: ["name = ?", "Moustache and Eyebrow Fancier Club"]).to_a
+ # Only the eyebrow fanciers club has a sponsored_member
+ assert_not_nil assert_no_queries { clubs[0].sponsored_member }
+ end
+
+ def test_has_one_through_nonpreload_eagerloading
+ members = assert_queries(1) do
+ Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a # force fallback
+ end
+ assert_equal 1, members.size
+ assert_not_nil assert_no_queries { members[0].club }
+ end
+
+ def test_has_one_through_nonpreload_eager_loading_through_polymorphic
+ members = assert_queries(1) do
+ Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a # force fallback
+ end
+ assert_equal 1, members.size
+ assert_not_nil assert_no_queries { members[0].sponsor_club }
+ end
+
+ def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
+ Sponsor.new(sponsor_club: clubs(:crazy_club), sponsorable: members(:groucho)).save!
+ members = assert_queries(1) do
+ Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name DESC").to_a # force fallback
+ end
+ assert_equal 1, members.size
+ assert_not_nil assert_no_queries { members[0].sponsor_club }
+ assert_equal clubs(:crazy_club), members[0].sponsor_club
+ end
+
+ def test_uninitialized_has_one_through_should_return_nil_for_unsaved_record
+ assert_nil Member.new.club
+ end
+
+ def test_assigning_association_correctly_assigns_target
+ new_member = Member.create(name: "Chris")
+ new_member.club = new_club = Club.create(name: "LRUG")
+ assert_equal new_club, new_member.association(:club).target
+ end
+
+ def test_has_one_through_proxy_should_not_respond_to_private_methods
+ assert_raise(NoMethodError) { clubs(:moustache_club).private_method }
+ assert_raise(NoMethodError) { @member.club.private_method }
+ end
+
+ def test_has_one_through_proxy_should_respond_to_private_methods_via_send
+ clubs(:moustache_club).send(:private_method)
+ @member.club.send(:private_method)
+ end
+
+ def test_assigning_to_has_one_through_preserves_decorated_join_record
+ @organization = organizations(:nsa)
+ assert_difference "MemberDetail.count", 1 do
+ @member_detail = MemberDetail.new(extra_data: "Extra")
+ @member.member_detail = @member_detail
+ @member.organization = @organization
+ end
+ assert_equal @organization, @member.organization
+ assert_includes @organization.members, @member
+ assert_equal "Extra", @member.member_detail.extra_data
+ end
+
+ def test_reassigning_has_one_through
+ @organization = organizations(:nsa)
+ @new_organization = organizations(:discordians)
+
+ assert_difference "MemberDetail.count", 1 do
+ @member_detail = MemberDetail.new(extra_data: "Extra")
+ @member.member_detail = @member_detail
+ @member.organization = @organization
+ end
+ assert_equal @organization, @member.organization
+ assert_equal "Extra", @member.member_detail.extra_data
+ assert_includes @organization.members, @member
+ assert_not_includes @new_organization.members, @member
+
+ assert_no_difference "MemberDetail.count" do
+ @member.organization = @new_organization
+ end
+ assert_equal @new_organization, @member.organization
+ assert_equal "Extra", @member.member_detail.extra_data
+ assert_not_includes @organization.members, @member
+ assert_includes @new_organization.members, @member
+ end
+
+ def test_preloading_has_one_through_on_belongs_to
+ MemberDetail.delete_all
+ assert_not_nil @member.member_type
+ @organization = organizations(:nsa)
+ @member_detail = MemberDetail.new
+ @member.member_detail = @member_detail
+ @member.organization = @organization
+ @member_details = assert_queries(3) do
+ MemberDetail.all.merge!(includes: :member_type).to_a
+ end
+ @new_detail = @member_details[0]
+ assert_predicate @new_detail.send(:association, :member_type), :loaded?
+ assert_no_queries { @new_detail.member_type }
+ end
+
+ def test_save_of_record_with_loaded_has_one_through
+ @club = @member.club
+ assert_not_nil @club.sponsored_member
+
+ assert_nothing_raised do
+ Club.find(@club.id).save!
+ Club.all.merge!(includes: :sponsored_member).find(@club.id).save!
+ end
+
+ @club.sponsor.destroy
+
+ assert_nothing_raised do
+ Club.find(@club.id).save!
+ Club.all.merge!(includes: :sponsored_member).find(@club.id).save!
+ end
+ end
+
+ def test_through_belongs_to_after_destroy
+ @member_detail = MemberDetail.new(extra_data: "Extra")
+ @member.member_detail = @member_detail
+ @member.save!
+
+ assert_not_nil @member_detail.member_type
+ @member_detail.destroy
+ assert_queries(1) do
+ @member_detail.association(:member_type).reload
+ assert_not_nil @member_detail.member_type
+ end
+
+ @member_detail.member.destroy
+ assert_queries(1) do
+ @member_detail.association(:member_type).reload
+ assert_nil @member_detail.member_type
+ end
+ end
+
+ def test_value_is_properly_quoted
+ minivan = Minivan.find("m1")
+ assert_nothing_raised do
+ minivan.dashboard
+ end
+ end
+
+ def test_has_one_through_polymorphic_with_primary_key_option
+ assert_equal categories(:general), authors(:david).essay_category
+
+ authors = Author.joins(:essay_category).where("categories.id" => categories(:general).id)
+ assert_equal authors(:david), authors.first
+
+ assert_equal owners(:blackbeard), authors(:david).essay_owner
+
+ authors = Author.joins(:essay_owner).where("owners.name = 'blackbeard'")
+ assert_equal authors(:david), authors.first
+ end
+
+ def test_has_one_through_with_primary_key_option
+ assert_equal categories(:general), authors(:david).essay_category_2
+
+ authors = Author.joins(:essay_category_2).where("categories.id" => categories(:general).id)
+ assert_equal authors(:david), authors.first
+ end
+
+ def test_has_one_through_with_default_scope_on_join_model
+ assert_equal posts(:welcome).comments.order("id").first, authors(:david).comment_on_first_post
+ end
+
+ def test_has_one_through_many_raises_exception
+ assert_raise(ActiveRecord::HasOneThroughCantAssociateThroughCollection) do
+ members(:groucho).club_through_many
+ end
+ end
+
+ def test_has_one_through_polymorphic_association
+ assert_raise(ActiveRecord::HasOneAssociationPolymorphicThroughError) do
+ @member.premium_club
+ end
+ end
+
+ def test_has_one_through_belongs_to_should_update_when_the_through_foreign_key_changes
+ minivan = minivans(:cool_first)
+
+ minivan.dashboard
+ proxy = minivan.send(:association_instance_get, :dashboard)
+
+ assert_not_predicate proxy, :stale_target?
+ assert_equal dashboards(:cool_first), minivan.dashboard
+
+ minivan.speedometer_id = speedometers(:second).id
+
+ assert_predicate proxy, :stale_target?
+ assert_equal dashboards(:second), minivan.dashboard
+ end
+
+ def test_has_one_through_belongs_to_setting_belongs_to_foreign_key_after_nil_target_loaded
+ minivan = Minivan.new
+
+ minivan.dashboard
+ proxy = minivan.send(:association_instance_get, :dashboard)
+
+ minivan.speedometer_id = speedometers(:second).id
+
+ assert_predicate proxy, :stale_target?
+ assert_equal dashboards(:second), minivan.dashboard
+ end
+
+ def test_assigning_has_one_through_belongs_to_with_new_record_owner
+ minivan = Minivan.new
+ dashboard = dashboards(:cool_first)
+
+ minivan.dashboard = dashboard
+
+ assert_equal dashboard, minivan.dashboard
+ assert_equal dashboard, minivan.speedometer.dashboard
+ end
+
+ def test_has_one_through_with_custom_select_on_join_model_default_scope
+ assert_equal clubs(:boring_club), members(:groucho).selected_club
+ end
+
+ def test_has_one_through_relationship_cannot_have_a_counter_cache
+ assert_raise(ArgumentError) do
+ Class.new(ActiveRecord::Base) do
+ has_one :thing, through: :other_thing, counter_cache: true
+ end
+ end
+ end
+
+ def test_has_one_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
+ customer = Customer.create!
+ carrier = Carrier.create!
+ customer_carrier = CustomerCarrier.create!(
+ customer: customer,
+ carrier: carrier,
+ )
+ account = ShopAccount.create!(customer_carrier: customer_carrier)
+
+ CustomerCarrier.current_customer = customer
+
+ account_carrier = account.carrier
+ assert_equal carrier, account_carrier
+
+ CustomerCarrier.current_customer = nil
+
+ other_carrier = Carrier.create!
+ other_customer = Customer.create!
+ other_customer_carrier = CustomerCarrier.create!(
+ customer: other_customer,
+ carrier: other_carrier,
+ )
+ other_account = ShopAccount.create!(customer_carrier: other_customer_carrier)
+
+ account_carrier = other_account.carrier
+ assert_equal other_carrier, account_carrier
+ ensure
+ CustomerCarrier.current_customer = nil
+ end
+end