aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/associations
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2007-11-07 15:07:39 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2007-11-07 15:07:39 +0000
commit37adea6ff1fba85c29406bbe2af4831f4c6beeec (patch)
tree3bfbd9e8b81f554bd9e6893e0ed93040065aa0d5 /activerecord/test/associations
parent31e2a2d9bbe3a375912223c133ba793b72600a9f (diff)
downloadrails-37adea6ff1fba85c29406bbe2af4831f4c6beeec.tar.gz
rails-37adea6ff1fba85c29406bbe2af4831f4c6beeec.tar.bz2
rails-37adea6ff1fba85c29406bbe2af4831f4c6beeec.zip
Address shortcomings of changeset [8054] [protocool]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8109 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activerecord/test/associations')
-rw-r--r--activerecord/test/associations/ar_joins_test.rb158
-rw-r--r--activerecord/test/associations/inner_join_association_test.rb88
2 files changed, 88 insertions, 158 deletions
diff --git a/activerecord/test/associations/ar_joins_test.rb b/activerecord/test/associations/ar_joins_test.rb
index e6a9180fae..e69de29bb2 100644
--- a/activerecord/test/associations/ar_joins_test.rb
+++ b/activerecord/test/associations/ar_joins_test.rb
@@ -1,158 +0,0 @@
-require 'abstract_unit'
-require 'fixtures/post'
-require 'fixtures/comment'
-require 'fixtures/author'
-require 'fixtures/category'
-require 'fixtures/categorization'
-require 'fixtures/company'
-require 'fixtures/topic'
-require 'fixtures/reply'
-require 'fixtures/developer'
-require 'fixtures/project'
-
-class ArJoinsTest < Test::Unit::TestCase
- fixtures :authors, :posts, :comments, :categories, :categories_posts, :people,
- :developers, :projects, :developers_projects,
- :categorizations, :companies, :accounts, :topics
-
- def test_ar_joins
- authors = Author.find(:all, :joins => :posts, :conditions => ['posts.type = ?', "Post"])
- assert_not_equal(0 , authors.length)
- authors.each do |author|
- assert !(author.send(:instance_variables).include? "@posts")
- assert(!author.readonly?, "non-string join value produced read-only result.")
- end
- end
-
- def test_ar_joins_with_cascaded_two_levels
- authors = Author.find(:all, :joins=>{:posts=>:comments})
- assert_equal(2, authors.length)
- authors.each do |author|
- assert !(author.send(:instance_variables).include? "@posts")
- assert(!author.readonly?, "non-string join value produced read-only result.")
- end
- authors = Author.find(:all, :joins=>{:posts=>:comments}, :conditions => ["comments.body = ?", "go crazy" ])
- assert_equal(1, authors.length)
- authors.each do |author|
- assert !(author.send(:instance_variables).include? "@posts")
- assert(!author.readonly?, "non-string join value produced read-only result.")
- end
- end
-
-
- def test_ar_joins_with_complex_conditions
- authors = Author.find(:all, :joins=>{:posts=>[:comments, :categories]},
- :conditions => ["categories.name = ? AND posts.title = ?", "General", "So I was thinking"]
- )
- assert_equal(1, authors.length)
- authors.each do |author|
- assert !(author.send(:instance_variables).include? "@posts")
- assert(!author.readonly?, "non-string join value produced read-only result.")
- end
- assert_equal("David", authors.first.name)
- end
-
- def test_ar_join_with_has_many_and_limit_and_scoped_and_explicit_conditions
- Post.with_scope(:find => { :conditions => "1=1" }) do
- posts = authors(:david).posts.find(:all,
- :joins => :comments,
- :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
- :limit => 2
- )
- assert_equal 2, posts.size
-
- count = Post.count(
- :joins => [ :comments, :author ],
- :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
- :limit => 2
- )
- assert_equal count, posts.size
- end
- end
-
- def test_ar_join_with_scoped_order_using_association_limiting_without_explicit_scope
- posts_with_explicit_order = Post.find(:all, :conditions => 'comments.id is not null', :joins => :comments, :order => 'posts.id DESC', :limit => 2)
- posts_with_scoped_order = Post.with_scope(:find => {:order => 'posts.id DESC'}) do
- Post.find(:all, :conditions => 'comments.id is not null', :joins => :comments, :limit => 2)
- end
- assert_equal posts_with_explicit_order, posts_with_scoped_order
- end
-
- def test_scoped_find_include
- # with the include, will retrieve only developers for the given project
- scoped_developers = Developer.with_scope(:find => { :joins => :projects }) do
- Developer.find(:all, :conditions => 'projects.id = 2')
- end
- assert scoped_developers.include?(developers(:david))
- assert !scoped_developers.include?(developers(:jamis))
- assert_equal 1, scoped_developers.size
- end
-
-
- def test_nested_scoped_find_ar_join
- Developer.with_scope(:find => { :joins => :projects }) do
- Developer.with_scope(:find => { :conditions => "projects.id = 2" }) do
- assert_equal('David', Developer.find(:first).name)
- end
- end
- end
-
- def test_nested_scoped_find_merged_ar_join
- # :include's remain unique and don't "double up" when merging
- Developer.with_scope(:find => { :joins => :projects, :conditions => "projects.id = 2" }) do
- Developer.with_scope(:find => { :joins => :projects }) do
- assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:ar_joins].length
- assert_equal('David', Developer.find(:first).name)
- end
- end
- # the nested scope doesn't remove the first :include
- Developer.with_scope(:find => { :joins => :projects, :conditions => "projects.id = 2" }) do
- Developer.with_scope(:find => { :joins => [] }) do
- assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:ar_joins].length
- assert_equal('David', Developer.find(:first).name)
- end
- end
- # mixing array and symbol include's will merge correctly
- Developer.with_scope(:find => { :joins => [:projects], :conditions => "projects.id = 2" }) do
- Developer.with_scope(:find => { :joins => :projects }) do
- assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:ar_joins].length
- assert_equal('David', Developer.find(:first).name)
- end
- end
- end
-
- def test_nested_scoped_find_replace_include
- Developer.with_scope(:find => { :joins => :projects }) do
- Developer.with_exclusive_scope(:find => { :joins => [] }) do
- assert_equal 0, Developer.instance_eval('current_scoped_methods')[:find][:ar_joins].length
- end
- end
- end
-
-#
-# Calculations
-#
- def test_count_with_ar_joins
- assert_equal(2, Author.count(:joins => :posts, :conditions => ['posts.type = ?', "Post"]))
- assert_equal(1, Author.count(:joins => :posts, :conditions => ['posts.type = ?', "SpecialPost"]))
- end
-
- def test_should_get_maximum_of_field_with_joins
- assert_equal 50, Account.maximum(:credit_limit, :joins=> :firm, :conditions => "companies.name != 'Summit'")
- end
-
- def test_should_get_maximum_of_field_with_scoped_include
- Account.with_scope :find => { :joins => :firm, :conditions => "companies.name != 'Summit'" } do
- assert_equal 50, Account.maximum(:credit_limit)
- end
- end
-
- def test_should_not_modify_options_when_using_ar_joins_on_count
- options = {:conditions => 'companies.id > 1', :joins => :firm}
- options_copy = options.dup
-
- Account.count(:all, options)
- assert_equal options_copy, options
- end
-
-end
diff --git a/activerecord/test/associations/inner_join_association_test.rb b/activerecord/test/associations/inner_join_association_test.rb
new file mode 100644
index 0000000000..983f269e87
--- /dev/null
+++ b/activerecord/test/associations/inner_join_association_test.rb
@@ -0,0 +1,88 @@
+require 'abstract_unit'
+require 'fixtures/post'
+require 'fixtures/comment'
+require 'fixtures/author'
+require 'fixtures/category'
+require 'fixtures/categorization'
+
+class InnerJoinAssociationTest < Test::Unit::TestCase
+ fixtures :authors, :posts, :comments, :categories, :categories_posts, :categorizations
+
+ def test_construct_finder_sql_creates_inner_joins
+ sql = Author.send(:construct_finder_sql, :joins => :posts)
+ assert_match /INNER JOIN posts ON posts.author_id = authors.id/, sql
+ end
+
+ def test_construct_finder_sql_cascades_inner_joins
+ sql = Author.send(:construct_finder_sql, :joins => {:posts => :comments})
+ assert_match /INNER JOIN posts ON posts.author_id = authors.id/, sql
+ assert_match /INNER JOIN comments ON comments.post_id = posts.id/, sql
+ end
+
+ def test_construct_finder_sql_inner_joins_through_associations
+ sql = Author.send(:construct_finder_sql, :joins => :categorized_posts)
+ assert_match /INNER JOIN categorizations.*INNER JOIN posts/, sql
+ end
+
+ def test_construct_finder_sql_applies_association_conditions
+ sql = Author.send(:construct_finder_sql, :joins => :categories_like_general, :conditions => "TERMINATING_MARKER")
+ assert_match /INNER JOIN categories ON.*AND.*'General'.*TERMINATING_MARKER/, sql
+ end
+
+ def test_construct_finder_sql_unpacks_nested_joins
+ sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]})
+ assert_no_match /inner join.*inner join.*inner join/i, sql, "only two join clauses should be present"
+ assert_match /INNER JOIN posts ON posts.author_id = authors.id/, sql
+ assert_match /INNER JOIN comments ON comments.post_id = posts.id/, sql
+ end
+
+ def test_construct_finder_sql_ignores_empty_joins_hash
+ sql = Author.send(:construct_finder_sql, :joins => {})
+ assert_no_match /JOIN/i, sql
+ end
+
+ def test_construct_finder_sql_ignores_empty_joins_array
+ sql = Author.send(:construct_finder_sql, :joins => [])
+ assert_no_match /JOIN/i, sql
+ end
+
+ def test_find_with_implicit_inner_joins_honors_readonly_without_select
+ authors = Author.find(:all, :joins => :posts)
+ assert !authors.empty?, "expected authors to be non-empty"
+ assert authors.all? {|a| a.readonly? }, "expected all authors to be readonly"
+ end
+
+ def test_find_with_implicit_inner_joins_honors_readonly_with_select
+ authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
+ assert !authors.empty?, "expected authors to be non-empty"
+ assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+ end
+
+ def test_find_with_implicit_inner_joins_honors_readonly_false
+ authors = Author.find(:all, :joins => :posts, :readonly => false)
+ assert !authors.empty?, "expected authors to be non-empty"
+ assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+ end
+
+ def test_find_with_implicit_inner_joins_does_not_set_associations
+ authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
+ assert !authors.empty?, "expected authors to be non-empty"
+ assert authors.all? {|a| !a.send(:instance_variables).include?("@posts")}, "expected no authors to have the @posts association loaded"
+ end
+
+ def test_count_honors_implicit_inner_joins
+ real_count = Author.find(:all).sum{|a| a.posts.count }
+ assert_equal real_count, Author.count(:joins => :posts), "plain inner join count should match the number of referenced posts records"
+ end
+
+ def test_calculate_honors_implicit_inner_joins
+ real_count = Author.find(:all).sum{|a| a.posts.count }
+ assert_equal real_count, Author.calculate(:count, 'authors.id', :joins => :posts), "plain inner join count should match the number of referenced posts records"
+ end
+
+ def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
+ real_count = Author.find(:all).select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
+ authors_with_welcoming_post_titles = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true, :conditions => "posts.title like 'Welcome%'")
+ assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
+ end
+end