aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/associations/has_many_through_associations_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/cases/associations/has_many_through_associations_test.rb')
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb265
1 files changed, 252 insertions, 13 deletions
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 77bc369ecc..73b1d6c500 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -19,10 +19,14 @@ require 'models/book'
require 'models/subscription'
require 'models/categorization'
require 'models/category'
+require 'models/essay'
+require 'models/member'
+require 'models/membership'
+require 'models/club'
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
- fixtures :posts, :readers, :people, :comments, :authors,
- :owners, :pets, :toys, :jobs, :references, :companies,
+ fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags,
+ :owners, :pets, :toys, :jobs, :references, :companies, :members, :author_addresses,
:subscribers, :books, :subscriptions, :developers, :categorizations
# Dummies to force column loads so query counts are clean.
@@ -31,6 +35,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
Reader.create :person_id => 0, :post_id => 0
end
+ def test_include?
+ person = Person.new
+ post = Post.new
+ person.posts << post
+ assert person.posts.include?(post)
+ end
+
def test_associate_existing
assert_queries(2) { posts(:thinking); people(:david) }
@@ -45,6 +56,16 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert posts(:thinking).reload.people(true).include?(people(:david))
end
+ def test_associate_existing_record_twice_should_add_to_target_twice
+ post = posts(:thinking)
+ person = people(:david)
+
+ assert_difference 'post.people.to_a.count', 2 do
+ post.people << person
+ post.people << person
+ end
+ end
+
def test_associating_new
assert_queries(1) { posts(:thinking) }
new_person = nil # so block binding catches it
@@ -107,8 +128,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_destroy_association
- assert_difference ["Person.count", "Reader.count"], -1 do
- posts(:welcome).people.destroy(people(:michael))
+ assert_no_difference "Person.count" do
+ assert_difference "Reader.count", -1 do
+ posts(:welcome).people.destroy(people(:michael))
+ end
end
assert posts(:welcome).reload.people.empty?
@@ -116,8 +139,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_destroy_all
- assert_difference ["Person.count", "Reader.count"], -1 do
- posts(:welcome).people.destroy_all
+ assert_no_difference "Person.count" do
+ assert_difference "Reader.count", -1 do
+ posts(:welcome).people.destroy_all
+ end
end
assert posts(:welcome).reload.people.empty?
@@ -130,6 +155,137 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_delete_through_belongs_to_with_dependent_nullify
+ Reference.make_comments = true
+
+ person = people(:michael)
+ job = jobs(:magician)
+ reference = Reference.where(:job_id => job.id, :person_id => person.id).first
+
+ assert_no_difference ['Job.count', 'Reference.count'] do
+ assert_difference 'person.jobs.count', -1 do
+ person.jobs_with_dependent_nullify.delete(job)
+ end
+ end
+
+ assert_equal nil, reference.reload.job_id
+ ensure
+ Reference.make_comments = false
+ end
+
+ def test_delete_through_belongs_to_with_dependent_delete_all
+ Reference.make_comments = true
+
+ person = people(:michael)
+ job = jobs(:magician)
+
+ # Make sure we're not deleting everything
+ assert person.jobs.count >= 2
+
+ assert_no_difference 'Job.count' do
+ assert_difference ['person.jobs.count', 'Reference.count'], -1 do
+ person.jobs_with_dependent_delete_all.delete(job)
+ end
+ end
+
+ # Check that the destroy callback on Reference did not run
+ assert_equal nil, person.reload.comments
+ ensure
+ Reference.make_comments = false
+ end
+
+ def test_delete_through_belongs_to_with_dependent_destroy
+ Reference.make_comments = true
+
+ person = people(:michael)
+ job = jobs(:magician)
+
+ # Make sure we're not deleting everything
+ assert person.jobs.count >= 2
+
+ assert_no_difference 'Job.count' do
+ assert_difference ['person.jobs.count', 'Reference.count'], -1 do
+ person.jobs_with_dependent_destroy.delete(job)
+ end
+ end
+
+ # Check that the destroy callback on Reference ran
+ assert_equal "Reference destroyed", person.reload.comments
+ ensure
+ Reference.make_comments = false
+ end
+
+ def test_belongs_to_with_dependent_destroy
+ person = PersonWithDependentDestroyJobs.find(1)
+
+ # Create a reference which is not linked to a job. This should not be destroyed.
+ person.references.create!
+
+ assert_no_difference 'Job.count' do
+ assert_difference 'Reference.count', -person.jobs.count do
+ person.destroy
+ end
+ end
+ end
+
+ def test_belongs_to_with_dependent_delete_all
+ person = PersonWithDependentDeleteAllJobs.find(1)
+
+ # Create a reference which is not linked to a job. This should not be destroyed.
+ person.references.create!
+
+ assert_no_difference 'Job.count' do
+ assert_difference 'Reference.count', -person.jobs.count do
+ person.destroy
+ end
+ end
+ end
+
+ def test_belongs_to_with_dependent_nullify
+ person = PersonWithDependentNullifyJobs.find(1)
+
+ references = person.references.to_a
+
+ assert_no_difference ['Reference.count', 'Job.count'] do
+ person.destroy
+ end
+
+ references.each do |reference|
+ assert_equal nil, reference.reload.job_id
+ end
+ end
+
+ def test_update_counter_caches_on_delete
+ post = posts(:welcome)
+ tag = post.tags.create!(:name => 'doomed')
+
+ assert_difference ['post.reload.taggings_count', 'post.reload.tags_count'], -1 do
+ posts(:welcome).tags.delete(tag)
+ end
+ end
+
+ def test_update_counter_caches_on_delete_with_dependent_destroy
+ post = posts(:welcome)
+ tag = post.tags.create!(:name => 'doomed')
+ post.update_attribute(:tags_with_destroy_count, post.tags.count)
+
+ assert_difference ['post.reload.taggings_count', 'post.reload.tags_with_destroy_count'], -1 do
+ posts(:welcome).tags_with_destroy.delete(tag)
+ end
+ end
+
+ def test_update_counter_caches_on_delete_with_dependent_nullify
+ post = posts(:welcome)
+ tag = post.tags.create!(:name => 'doomed')
+ post.update_attribute(:tags_with_nullify_count, post.tags.count)
+
+ assert_no_difference 'post.reload.taggings_count' do
+ assert_difference 'post.reload.tags_with_nullify_count', -1 do
+ posts(:welcome).tags_with_nullify.delete(tag)
+ end
+ end
+ end
+
def test_replace_association
assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
@@ -324,12 +480,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 2, people(:michael).jobs.size
end
- def test_get_ids_for_belongs_to_source
- assert_sql(/DISTINCT/) { assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort }
- end
-
- def test_get_ids_for_has_many_source
- assert_equal [comments(:eager_other_comment1).id], authors(:mary).comment_ids
+ def test_get_ids
+ assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
end
def test_get_ids_for_loaded_associations
@@ -397,6 +549,34 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal people(:susan).agents.map(&:agents).flatten, people(:susan).agents_of_agents
end
+ def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
+ Categorization.create(:author => authors(:mary), :named_category_name => categories(:general).name)
+ assert_equal categories(:general), authors(:mary).named_categories.first
+ end
+
+ def test_collection_build_with_nonstandard_primary_key_on_belongs_to
+ author = authors(:mary)
+ category = author.named_categories.build(:name => "Primary")
+ author.save
+ assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
+ assert author.named_categories(true).include?(category)
+ end
+
+ def test_collection_create_with_nonstandard_primary_key_on_belongs_to
+ author = authors(:mary)
+ category = author.named_categories.create(:name => "Primary")
+ assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
+ assert author.named_categories(true).include?(category)
+ end
+
+ def test_collection_delete_with_nonstandard_primary_key_on_belongs_to
+ author = authors(:mary)
+ category = author.named_categories.create(:name => "Primary")
+ author.named_categories.delete(category)
+ assert !Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
+ assert author.named_categories(true).empty?
+ end
+
def test_collection_singular_ids_getter_with_string_primary_keys
book = books(:awdr)
assert_equal 2, book.subscriber_ids.size
@@ -466,7 +646,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_with_default_scope_on_join_model
- assert_equal posts(:welcome).comments, authors(:david).comments_on_first_posts
+ assert_equal posts(:welcome).comments.order('id').all, authors(:david).comments_on_first_posts
end
def test_create_has_many_through_with_default_scope_on_join_model
@@ -479,4 +659,63 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 1, mary.unique_categorized_posts.length
assert_equal 1, mary.unique_categorized_post_ids.length
end
+
+ def test_joining_has_many_through_belongs_to
+ posts = Post.joins(:author_categorizations).
+ where('categorizations.id' => categorizations(:mary_thinking_sti).id)
+
+ assert_equal [posts(:eager_other)], posts
+ end
+
+ def test_select_chosen_fields_only
+ author = authors(:david)
+ assert_equal ['body'], author.comments.select('comments.body').first.attributes.keys
+ end
+
+ def test_get_has_many_through_belongs_to_ids_with_conditions
+ assert_equal [categories(:general).id], authors(:mary).categories_like_general_ids
+ end
+
+ def test_count_has_many_through_with_named_scope
+ assert_equal 2, authors(:mary).categories.count
+ assert_equal 1, authors(:mary).categories.general.count
+ end
+
+ def test_has_many_through_belongs_to_should_update_when_the_through_foreign_key_changes
+ post = posts(:eager_other)
+
+ post.author_categorizations
+ proxy = post.send(:association_instance_get, :author_categorizations)
+
+ assert !proxy.stale_target?
+ assert_equal authors(:mary).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
+
+ post.author_id = authors(:david).id
+
+ assert proxy.stale_target?
+ assert_equal authors(:david).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
+ end
+
+ def test_create_with_conditions_hash_on_through_association
+ member = members(:groucho)
+ club = member.clubs.create!
+
+ assert_equal true, club.reload.membership.favourite
+ end
+
+ def test_deleting_from_has_many_through_a_belongs_to_should_not_try_to_update_counter
+ post = posts(:welcome)
+ address = author_addresses(:david_address)
+
+ assert post.author_addresses.include?(address)
+ post.author_addresses.delete(address)
+ assert post[:author_count].nil?
+ end
+
+ def test_interpolated_conditions
+ post = posts(:welcome)
+ assert !post.tags.empty?
+ assert_equal post.tags, post.interpolated_tags
+ assert_equal post.tags, post.interpolated_tags_2
+ end
end