Upgraded Rails and RSpec
[monkeycharger.git] / vendor / rails / activerecord / test / associations / eager_test.rb
blob75948ab59f0d81bc83f220c474afe70376c58316
1 require 'abstract_unit'
2 require 'fixtures/post'
3 require 'fixtures/comment'
4 require 'fixtures/author'
5 require 'fixtures/category'
6 require 'fixtures/company'
7 require 'fixtures/person'
8 require 'fixtures/reader'
10 class EagerAssociationTest < Test::Unit::TestCase
11   fixtures :posts, :comments, :authors, :categories, :categories_posts,
12             :companies, :accounts, :tags, :people, :readers
14   def test_loading_with_one_association
15     posts = Post.find(:all, :include => :comments)
16     post = posts.find { |p| p.id == 1 }
17     assert_equal 2, post.comments.size
18     assert post.comments.include?(comments(:greetings))
20     post = Post.find(:first, :include => :comments, :conditions => "posts.title = 'Welcome to the weblog'")
21     assert_equal 2, post.comments.size
22     assert post.comments.include?(comments(:greetings))
23   end
25   def test_loading_conditions_with_or
26     posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
27     assert_nil posts.detect { |p| p.author_id != authors(:david).id },
28       "expected to find only david's posts"
29   end
31   def test_with_ordering
32     list = Post.find(:all, :include => :comments, :order => "posts.id DESC")
33     [:eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments,
34      :authorless, :thinking, :welcome
35     ].each_with_index do |post, index|
36       assert_equal posts(post), list[index]
37     end
38   end
40   def test_loading_with_multiple_associations
41     posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
42     assert_equal 2, posts.first.comments.size
43     assert_equal 2, posts.first.categories.size
44     assert posts.first.comments.include?(comments(:greetings))
45   end
47   def test_loading_from_an_association
48     posts = authors(:david).posts.find(:all, :include => :comments, :order => "posts.id")
49     assert_equal 2, posts.first.comments.size
50   end
52   def test_loading_with_no_associations
53     assert_nil Post.find(posts(:authorless).id, :include => :author).author
54   end
56   def test_eager_association_loading_with_belongs_to
57     comments = Comment.find(:all, :include => :post)
58     assert_equal 10, comments.length
59     titles = comments.map { |c| c.post.title }
60     assert titles.include?(posts(:welcome).title)
61     assert titles.include?(posts(:sti_post_and_comments).title)
62   end
63   
64   def test_eager_association_loading_with_belongs_to_and_limit
65     comments = Comment.find(:all, :include => :post, :limit => 5, :order => 'comments.id')
66     assert_equal 5, comments.length
67     assert_equal [1,2,3,5,6], comments.collect { |c| c.id }
68   end
70   def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
71     comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :order => 'comments.id')
72     assert_equal 3, comments.length
73     assert_equal [5,6,7], comments.collect { |c| c.id }
74   end
76   def test_eager_association_loading_with_belongs_to_and_limit_and_offset
77     comments = Comment.find(:all, :include => :post, :limit => 3, :offset => 2, :order => 'comments.id')
78     assert_equal 3, comments.length
79     assert_equal [3,5,6], comments.collect { |c| c.id }
80   end
82   def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
83     comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :offset => 1, :order => 'comments.id')
84     assert_equal 3, comments.length
85     assert_equal [6,7,8], comments.collect { |c| c.id }
86   end
87   
88   def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
89     comments = Comment.find(:all, :include => :post, :conditions => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id')
90     assert_equal 3, comments.length
91     assert_equal [6,7,8], comments.collect { |c| c.id }
92   end
94   def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
95     posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :order => 'posts.id')
96     assert_equal 1, posts.length
97     assert_equal [1], posts.collect { |p| p.id }
98   end
99   
100   def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
101     posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id')
102     assert_equal 1, posts.length
103     assert_equal [2], posts.collect { |p| p.id }
104   end
105   
106   def test_eager_association_loading_with_explicit_join
107     posts = Post.find(:all, :include => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id')
108     assert_equal 1, posts.length
109   end
110   
111   def test_eager_with_has_many_through
112     posts_with_comments = people(:michael).posts.find(:all, :include => :comments)
113     posts_with_author = people(:michael).posts.find(:all, :include => :author )
114     posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ])
115     assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size }
116     assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
117     assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
118   end
120   def test_eager_with_has_many_through_an_sti_join_model
121     author = Author.find(:first, :include => :special_post_comments, :order => 'authors.id')
122     assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
123   end
124   
125   def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both
126     author = Author.find(:first, :include => :special_nonexistant_post_comments, :order => 'authors.id')
127     assert_equal [], author.special_nonexistant_post_comments
128   end
130   def test_eager_with_has_many_through_join_model_with_conditions
131     assert_equal Author.find(:first, :include => :hello_post_comments,
132                              :order => 'authors.id').hello_post_comments.sort_by(&:id),
133                  Author.find(:first, :order => 'authors.id').hello_post_comments.sort_by(&:id)
134   end
136   def test_eager_with_has_many_and_limit
137     posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2)
138     assert_equal 2, posts.size
139     assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size }
140   end
142   def test_eager_with_has_many_and_limit_and_conditions
143     if current_adapter?(:OpenBaseAdapter)
144       posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "FETCHBLOB(posts.body) = 'hello'", :order => "posts.id")
145     else
146       posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
147     end
148     assert_equal 2, posts.size
149     assert_equal [4,5], posts.collect { |p| p.id }
150   end
152   def test_eager_with_has_many_and_limit_and_conditions_array
153     if current_adapter?(:OpenBaseAdapter)
154       posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id")
155     else
156       posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
157     end
158     assert_equal 2, posts.size
159     assert_equal [4,5], posts.collect { |p| p.id }    
160   end
162   def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
163     posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
164     assert_equal 2, posts.size
165     
166     count = Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
167     assert_equal count, posts.size
168   end
170   def test_eager_with_has_many_and_limit_ond_high_offset
171     posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
172     assert_equal 0, posts.size
173   end
175   def test_count_eager_with_has_many_and_limit_ond_high_offset
176     posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
177     assert_equal 0, posts
178   end
180   def test_eager_with_has_many_and_limit_with_no_results
181     posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'")
182     assert_equal 0, posts.size
183   end
184   
185   def test_eager_count_performed_on_a_has_many_association_with_multi_table_conditional
186     author = authors(:david)
187     author_posts_without_comments = author.posts.select { |post| post.comments.blank? }
188     assert_equal author_posts_without_comments.size, author.posts.count(:all, :include => :comments, :conditions => 'comments.id is null')
189   end
191   def test_eager_with_has_and_belongs_to_many_and_limit
192     posts = Post.find(:all, :include => :categories, :order => "posts.id", :limit => 3)
193     assert_equal 3, posts.size
194     assert_equal 2, posts[0].categories.size
195     assert_equal 1, posts[1].categories.size
196     assert_equal 0, posts[2].categories.size
197     assert posts[0].categories.include?(categories(:technology))
198     assert posts[1].categories.include?(categories(:general))
199   end
201   def test_eager_with_has_many_and_limit_and_conditions_on_the_eagers
202     posts = authors(:david).posts.find(:all, 
203       :include    => :comments, 
204       :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
205       :limit      => 2
206     )
207     assert_equal 2, posts.size
208     
209     count = Post.count(
210       :include    => [ :comments, :author ], 
211       :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
212       :limit      => 2
213     )
214     assert_equal count, posts.size
215   end
217   def test_eager_with_has_many_and_limit_and_scoped_conditions_on_the_eagers
218     posts = nil
219     Post.with_scope(:find => {
220       :include    => :comments, 
221       :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'"
222     }) do
223       posts = authors(:david).posts.find(:all, :limit => 2)
224       assert_equal 2, posts.size
225     end
226     
227     Post.with_scope(:find => {
228       :include    => [ :comments, :author ], 
229       :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')"
230     }) do
231       count = Post.count(:limit => 2)
232       assert_equal count, posts.size
233     end
234   end
236   def test_eager_with_has_many_and_limit_and_scoped_and_explicit_conditions_on_the_eagers
237     Post.with_scope(:find => { :conditions => "1=1" }) do
238       posts = authors(:david).posts.find(:all, 
239         :include    => :comments, 
240         :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
241         :limit      => 2
242       )
243       assert_equal 2, posts.size
244       
245       count = Post.count(
246         :include    => [ :comments, :author ], 
247         :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
248         :limit      => 2
249       )
250       assert_equal count, posts.size
251     end
252   end
254   def test_eager_with_scoped_order_using_association_limiting_without_explicit_scope
255     posts_with_explicit_order = Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :order => 'posts.id DESC', :limit => 2)
256     posts_with_scoped_order = Post.with_scope(:find => {:order => 'posts.id DESC'}) do
257       Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :limit => 2)
258     end
259     assert_equal posts_with_explicit_order, posts_with_scoped_order
260   end
262   def test_eager_association_loading_with_habtm
263     posts = Post.find(:all, :include => :categories, :order => "posts.id")
264     assert_equal 2, posts[0].categories.size
265     assert_equal 1, posts[1].categories.size
266     assert_equal 0, posts[2].categories.size
267     assert posts[0].categories.include?(categories(:technology))
268     assert posts[1].categories.include?(categories(:general))
269   end
271   def test_eager_with_inheritance
272     posts = SpecialPost.find(:all, :include => [ :comments ])
273   end
275   def test_eager_has_one_with_association_inheritance
276     post = Post.find(4, :include => [ :very_special_comment ])
277     assert_equal "VerySpecialComment", post.very_special_comment.class.to_s
278   end
280   def test_eager_has_many_with_association_inheritance
281     post = Post.find(4, :include => [ :special_comments ])
282     post.special_comments.each do |special_comment|
283       assert_equal "SpecialComment", special_comment.class.to_s
284     end
285   end
287   def test_eager_habtm_with_association_inheritance
288     post = Post.find(6, :include => [ :special_categories ])
289     assert_equal 1, post.special_categories.size
290     post.special_categories.each do |special_category|
291       assert_equal "SpecialCategory", special_category.class.to_s
292     end
293   end
295   def test_eager_with_has_one_dependent_does_not_destroy_dependent
296     assert_not_nil companies(:first_firm).account
297     f = Firm.find(:first, :include => :account,
298             :conditions => ["companies.name = ?", "37signals"])
299     assert_not_nil f.account
300     assert_equal companies(:first_firm, :reload).account, f.account
301   end
302   
303   def test_eager_with_multi_table_conditional_properly_counts_the_records_when_using_size
304     author = authors(:david)
305     posts_with_no_comments = author.posts.select { |post| post.comments.blank? }
306     assert_equal posts_with_no_comments.size, author.posts_with_no_comments.size
307     assert_equal posts_with_no_comments, author.posts_with_no_comments
308   end
310   def test_eager_with_invalid_association_reference
311     assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it?  You specified :include => :monkeys") {
312       post = Post.find(6, :include=> :monkeys )
313     }
314     assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it?  You specified :include => :monkeys") {
315       post = Post.find(6, :include=>[ :monkeys ])
316     }
317     assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it?  You specified :include => :monkeys") {
318       post = Post.find(6, :include=>[ 'monkeys' ])
319     }
320     assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it?  You specified :include => :monkeys, :elephants") {
321       post = Post.find(6, :include=>[ :monkeys, :elephants ])
322     }
323   end
324   
325   def find_all_ordered(className, include=nil)
326     className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :include=>include)
327   end
328   
329   def test_limited_eager_with_order
330     assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
331     assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
332   end
333   
334   def test_limited_eager_with_multiple_order_columns
335     assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
336     assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
337   end
339   def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
340     # Eager includes of has many and habtm associations aren't necessarily sorted in the same way
341     def assert_equal_after_sort(item1, item2, item3 = nil)
342       assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id})
343       assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3
344     end
345     # Test regular association, association with conditions, association with
346     # STI, and association with conditions assured not to be true
347     post_types = [:posts, :other_posts, :special_posts]
348     # test both has_many and has_and_belongs_to_many
349     [Author, Category].each do |className|
350       d1 = find_all_ordered(className)
351       # test including all post types at once
352       d2 = find_all_ordered(className, post_types) 
353       d1.each_index do |i| 
354         assert_equal(d1[i], d2[i])
355         assert_equal_after_sort(d1[i].posts, d2[i].posts)
356         post_types[1..-1].each do |post_type|
357           # test including post_types together
358           d3 = find_all_ordered(className, [:posts, post_type])
359           assert_equal(d1[i], d3[i])
360           assert_equal_after_sort(d1[i].posts, d3[i].posts)
361           assert_equal_after_sort(d1[i].send(post_type), d2[i].send(post_type), d3[i].send(post_type))
362         end
363       end
364     end
365   end
366   
367   def test_eager_with_multiple_associations_with_same_table_has_one
368     d1 = find_all_ordered(Firm)
369     d2 = find_all_ordered(Firm, :account)
370     d1.each_index do |i| 
371       assert_equal(d1[i], d2[i])
372       assert_equal(d1[i].account, d2[i].account)
373     end
374   end
375   
376   def test_eager_with_multiple_associations_with_same_table_belongs_to
377     firm_types = [:firm, :firm_with_basic_id, :firm_with_other_name, :firm_with_condition]
378     d1 = find_all_ordered(Client)
379     d2 = find_all_ordered(Client, firm_types)
380     d1.each_index do |i| 
381       assert_equal(d1[i], d2[i])
382       firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) }
383     end
384   end
385   def test_eager_with_valid_association_as_string_not_symbol
386     assert_nothing_raised { Post.find(:all, :include => 'comments') }
387   end
389   def test_preconfigured_includes_with_belongs_to
390     author = posts(:welcome).author_with_posts
391     assert_equal 5, author.posts.size
392   end
394   def test_preconfigured_includes_with_has_one
395     comment = posts(:sti_comments).very_special_comment_with_post
396     assert_equal posts(:sti_comments), comment.post
397   end
399   def test_preconfigured_includes_with_has_many
400     posts = authors(:david).posts_with_comments
401     one = posts.detect { |p| p.id == 1 }
402     assert_equal 5, posts.size
403     assert_equal 2, one.comments.size
404   end
406   def test_preconfigured_includes_with_habtm
407     posts = authors(:david).posts_with_categories
408     one = posts.detect { |p| p.id == 1 }
409     assert_equal 5, posts.size
410     assert_equal 2, one.categories.size
411   end
413   def test_preconfigured_includes_with_has_many_and_habtm
414     posts = authors(:david).posts_with_comments_and_categories
415     one = posts.detect { |p| p.id == 1 }
416     assert_equal 5, posts.size
417     assert_equal 2, one.comments.size
418     assert_equal 2, one.categories.size
419   end
420   
421   def test_count_with_include
422     if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
423       assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
424     elsif current_adapter?(:OpenBaseAdapter)
425       assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15")
426     else
427       assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
428     end
429   end