diff options
author | John Mileham <jmileham@gmail.com> | 2011-03-24 15:03:08 -0400 |
---|---|---|
committer | John Mileham <jmileham@gmail.com> | 2011-03-24 15:03:08 -0400 |
commit | b44a0ec153c84384e9b97a069e81f28bc5c2a4bf (patch) | |
tree | 94713aff0c241f91530dfd714bf685019c84ccab /activerecord/test/cases | |
parent | d5994ee48af14d67f0eec7d23863d4b19211b078 (diff) | |
parent | 5214e73850916de3c9127d35a4ecee0424d364a3 (diff) | |
download | rails-b44a0ec153c84384e9b97a069e81f28bc5c2a4bf.tar.gz rails-b44a0ec153c84384e9b97a069e81f28bc5c2a4bf.tar.bz2 rails-b44a0ec153c84384e9b97a069e81f28bc5c2a4bf.zip |
Merge branch 'master' of github.com:rails/rails into count_behavior
Diffstat (limited to 'activerecord/test/cases')
21 files changed, 928 insertions, 99 deletions
diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb new file mode 100644 index 0000000000..c6c1d1dad5 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/schema_test.rb @@ -0,0 +1,36 @@ +require "cases/helper" +require 'models/post' +require 'models/comment' + +module ActiveRecord + module ConnectionAdapters + class MysqlSchemaTest < ActiveRecord::TestCase + fixtures :posts + + def setup + @connection = ActiveRecord::Base.connection + db = Post.connection_pool.spec.config[:database] + table = Post.table_name + @db_name = db + + @omgpost = Class.new(Post) do + set_table_name "#{db}.#{table}" + def self.name; 'Post'; end + end + end + + def test_schema + assert @omgpost.find(:first) + end + + def test_table_exists? + name = @omgpost.table_name + assert @connection.table_exists?(name), "#{name} table should exist" + end + + def test_table_exists_wrong_schema + assert(!@connection.table_exists?("#{@db_name}.zomg"), "table should not exist") + end + end if current_adapter?(:MysqlAdapter) + end +end diff --git a/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb b/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb index ce0b2f5f5b..d1fc470907 100644 --- a/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb @@ -1,8 +1,13 @@ +# encoding: utf-8 require "cases/helper" +require 'models/binary' module ActiveRecord module ConnectionAdapters class SQLiteAdapterTest < ActiveRecord::TestCase + class DualEncoding < ActiveRecord::Base + end + def setup @ctx = Base.sqlite3_connection :database => ':memory:', :adapter => 'sqlite3', @@ -15,6 +20,20 @@ module ActiveRecord eosql end + def test_quote_binary_column_escapes_it + DualEncoding.connection.execute(<<-eosql) + CREATE TABLE dual_encodings ( + id integer PRIMARY KEY AUTOINCREMENT, + name string, + data binary + ) + eosql + str = "\x80".force_encoding("ASCII-8BIT") + binary = DualEncoding.new :name => 'いただきます!', :data => str + binary.save! + assert_equal str, binary.data + end + def test_execute @ctx.execute "INSERT INTO items (number) VALUES (10)" records = @ctx.execute "SELECT * FROM items" diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 0742e311d9..39e8a7960a 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -15,17 +15,17 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase def test_eager_association_loading_with_cascaded_two_levels authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id") - assert_equal 2, authors.size + assert_equal 3, authors.size assert_equal 5, authors[0].posts.size - assert_equal 1, authors[1].posts.size + assert_equal 3, authors[1].posts.size assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i} end def test_eager_association_loading_with_cascaded_two_levels_and_one_level authors = Author.find(:all, :include=>[{:posts=>:comments}, :categorizations], :order=>"authors.id") - assert_equal 2, authors.size + assert_equal 3, authors.size assert_equal 5, authors[0].posts.size - assert_equal 1, authors[1].posts.size + assert_equal 3, authors[1].posts.size assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i} assert_equal 1, authors[0].categorizations.size assert_equal 2, authors[1].categorizations.size @@ -51,24 +51,24 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase categories = Category.joins(:categorizations).includes([{:posts=>:comments}, :authors]) assert_nothing_raised do - assert_equal 2, categories.count - assert_equal 2, categories.all.uniq.size # Must uniq since instantiating with inner joins will get dupes + assert_equal 3, categories.count + assert_equal 3, categories.all.uniq.size # Must uniq since instantiating with inner joins will get dupes end end def test_cascaded_eager_association_loading_with_duplicated_includes categories = Category.includes(:categorizations).includes(:categorizations => :author).where("categorizations.id is not null") assert_nothing_raised do - assert_equal 2, categories.count - assert_equal 2, categories.all.size + assert_equal 3, categories.count + assert_equal 3, categories.all.size end end def test_cascaded_eager_association_loading_with_twice_includes_edge_cases categories = Category.includes(:categorizations => :author).includes(:categorizations => :post).where("posts.id is not null") assert_nothing_raised do - assert_equal 2, categories.count - assert_equal 2, categories.all.size + assert_equal 3, categories.count + assert_equal 3, categories.all.size end end @@ -81,15 +81,15 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id") - assert_equal 2, authors.size + assert_equal 3, authors.size assert_equal 5, authors[0].posts.size - assert_equal 1, authors[1].posts.size + assert_equal 3, authors[1].posts.size assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i} end def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference authors = Author.find(:all, :include=>{:posts=>[:comments, :author]}, :order=>"authors.id") - assert_equal 2, authors.size + assert_equal 3, authors.size assert_equal 5, authors[0].posts.size assert_equal authors(:david).name, authors[0].name assert_equal [authors(:david).name], authors[0].posts.collect{|post| post.author.name}.uniq @@ -157,9 +157,9 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase def test_eager_association_loading_where_first_level_returns_nil authors = Author.find(:all, :include => {:post_about_thinking => :comments}, :order => 'authors.id DESC') - assert_equal [authors(:mary), authors(:david)], authors + assert_equal [authors(:bob), authors(:mary), authors(:david)], authors assert_no_queries do - authors[1].post_about_thinking.comments.first + authors[2].post_about_thinking.comments.first end end end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 26808ae931..40c82f2fb8 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -68,8 +68,8 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_with_ordering list = Post.find(:all, :include => :comments, :order => "posts.id DESC") - [:eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments, - :authorless, :thinking, :welcome + [:other_by_mary, :other_by_bob, :misc_by_mary, :misc_by_bob, :eager_other, + :sti_habtm, :sti_post_and_comments, :sti_comments, :authorless, :thinking, :welcome ].each_with_index do |post, index| assert_equal posts(post), list[index] end @@ -97,25 +97,25 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle Post.connection.expects(:in_clause_length).at_least_once.returns(5) posts = Post.find(:all, :include=>:comments) - assert_equal 7, posts.size + assert_equal 11, posts.size end def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle Post.connection.expects(:in_clause_length).at_least_once.returns(nil) posts = Post.find(:all, :include=>:comments) - assert_equal 7, posts.size + assert_equal 11, posts.size end def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle Post.connection.expects(:in_clause_length).at_least_once.returns(5) posts = Post.find(:all, :include=>:categories) - assert_equal 7, posts.size + assert_equal 11, posts.size end def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle Post.connection.expects(:in_clause_length).at_least_once.returns(nil) posts = Post.find(:all, :include=>:categories) - assert_equal 7, posts.size + assert_equal 11, posts.size end def test_load_associated_records_in_one_query_when_adapter_has_no_limit @@ -525,6 +525,22 @@ class EagerAssociationTest < ActiveRecord::TestCase assert posts[1].categories.include?(categories(:general)) end + # This is only really relevant when the identity map is off. Since the preloader for habtm + # gets raw row hashes from the database and then instantiates them, this test ensures that + # it only instantiates one actual object per record from the database. + def test_has_and_belongs_to_many_should_not_instantiate_same_records_multiple_times + welcome = posts(:welcome) + categories = Category.includes(:posts) + + general = categories.find { |c| c == categories(:general) } + technology = categories.find { |c| c == categories(:technology) } + + post1 = general.posts.to_a.find { |p| p == posts(:welcome) } + post2 = technology.posts.to_a.find { |p| p == posts(:welcome) } + + assert_equal post1.object_id, post2.object_id + end + def test_eager_with_has_many_and_limit_and_conditions_on_the_eagers posts = authors(:david).posts.find(:all, :include => :comments, diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index dc382c3007..73d02c9676 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -642,12 +642,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_find_grouped all_posts_from_category1 = Post.find(:all, :conditions => "category_id = 1", :joins => :categories) grouped_posts_of_category1 = Post.find(:all, :conditions => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories) - assert_equal 4, all_posts_from_category1.size - assert_equal 1, grouped_posts_of_category1.size + assert_equal 5, all_posts_from_category1.size + assert_equal 2, grouped_posts_of_category1.size end def test_find_scoped_grouped - assert_equal 4, categories(:general).posts_grouped_by_title.size + assert_equal 5, categories(:general).posts_grouped_by_title.size assert_equal 1, categories(:technology).posts_grouped_by_title.size end 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 efdecd4b09..9adaebe924 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -17,9 +17,10 @@ require 'models/developer' require 'models/subscriber' require 'models/book' require 'models/subscription' -require 'models/categorization' -require 'models/category' require 'models/essay' +require 'models/category' +require 'models/owner' +require 'models/categorization' require 'models/member' require 'models/membership' require 'models/club' @@ -27,7 +28,7 @@ require 'models/club' class HasManyThroughAssociationsTest < ActiveRecord::TestCase fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags, :owners, :pets, :toys, :jobs, :references, :companies, :members, :author_addresses, - :subscribers, :books, :subscriptions, :developers, :categorizations + :subscribers, :books, :subscriptions, :developers, :categorizations, :essays # Dummies to force column loads so query counts are clean. def setup @@ -656,6 +657,25 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert author.comments.include?(comment) end + def test_has_many_through_polymorphic_with_primary_key_option + assert_equal [categories(:general)], authors(:david).essay_categories + + authors = Author.joins(:essay_categories).where('categories.id' => categories(:general).id) + assert_equal authors(:david), authors.first + + assert_equal [owners(:blackbeard)], authors(:david).essay_owners + + authors = Author.joins(:essay_owners).where("owners.name = 'blackbeard'") + assert_equal authors(:david), authors.first + end + + def test_has_many_through_with_primary_key_option + assert_equal [categories(:general)], authors(:david).essay_categories_2 + + authors = Author.joins(:essay_categories_2).where('categories.id' => categories(:general).id) + assert_equal authors(:david), authors.first + end + def test_size_of_through_association_should_increase_correctly_when_has_many_association_is_added post = posts(:thinking) readers = post.readers.size @@ -679,10 +699,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end def test_joining_has_many_through_belongs_to - posts = Post.joins(:author_categorizations). + posts = Post.joins(:author_categorizations).order('posts.id'). where('categorizations.id' => categorizations(:mary_thinking_sti).id) - assert_equal [posts(:eager_other)], posts + assert_equal [posts(:eager_other), posts(:misc_by_mary), posts(:other_by_mary)], posts end def test_select_chosen_fields_only diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index bfc5ddc747..9ba5549277 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -9,13 +9,16 @@ 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' class HasOneThroughAssociationsTest < ActiveRecord::TestCase fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans, - :dashboards, :speedometers, :authors, :posts, :comments + :dashboards, :speedometers, :authors, :posts, :comments, :categories, :essays, :owners def setup @member = members(:groucho) @@ -242,6 +245,25 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase 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 diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 19303fef9f..1f95b31497 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -2,6 +2,7 @@ require "cases/helper" require 'models/tag' require 'models/tagging' require 'models/post' +require 'models/rating' require 'models/item' require 'models/comment' require 'models/author' @@ -288,7 +289,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_has_many_going_through_join_model_with_custom_foreign_key - assert_equal [], posts(:thinking).authors + assert_equal [authors(:bob)], posts(:thinking).authors assert_equal [authors(:mary)], posts(:authorless).authors end @@ -305,7 +306,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_has_many_through_with_custom_primary_key_on_has_many_source - assert_equal [authors(:david)], posts(:thinking).authors_using_custom_pk + assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order('authors.id') end def test_both_scoped_and_explicit_joins_should_be_respected @@ -399,14 +400,6 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end end - def test_has_many_through_has_many_through - assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags } - end - - def test_has_many_through_habtm - assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).post_categories } - end - def test_eager_load_has_many_through_has_many author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id' SpecialComment.new; VerySpecialComment.new diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb new file mode 100644 index 0000000000..dd450a2a8e --- /dev/null +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -0,0 +1,546 @@ +require "cases/helper" +require 'models/author' +require 'models/post' +require 'models/person' +require 'models/reference' +require 'models/job' +require 'models/reader' +require 'models/comment' +require 'models/tag' +require 'models/tagging' +require 'models/subscriber' +require 'models/book' +require 'models/subscription' +require 'models/rating' +require 'models/member' +require 'models/member_detail' +require 'models/member_type' +require 'models/sponsor' +require 'models/club' +require 'models/organization' +require 'models/category' +require 'models/categorization' +require 'models/membership' +require 'models/essay' + +class NestedThroughAssociationsTest < ActiveRecord::TestCase + fixtures :authors, :books, :posts, :subscriptions, :subscribers, :tags, :taggings, + :people, :readers, :references, :jobs, :ratings, :comments, :members, :member_details, + :member_types, :sponsors, :clubs, :organizations, :categories, :categories_posts, + :categorizations, :memberships, :essays + + # Through associations can either use the has_many or has_one macros. + # + # has_many + # - Source reflection can be has_many, has_one, belongs_to or has_and_belongs_to_many + # - Through reflection can be has_many, has_one, belongs_to or has_and_belongs_to_many + # + # has_one + # - Source reflection can be has_one or belongs_to + # - Through reflection can be has_one or belongs_to + # + # Additionally, the source reflection and/or through reflection may be subject to + # polymorphism and/or STI. + # + # When testing these, we need to make sure it works via loading the association directly, or + # joining the association, or including the association. We also need to ensure that associations + # are readonly where relevant. + + # has_many through + # Source: has_many through + # Through: has_many + def test_has_many_through_has_many_with_has_many_through_source_reflection + general = tags(:general) + assert_equal [general, general], authors(:david).tags + end + + def test_has_many_through_has_many_with_has_many_through_source_reflection_preload + authors = assert_queries(5) { Author.includes(:tags).to_a } + general = tags(:general) + + assert_no_queries do + assert_equal [general, general], authors.first.tags + end + end + + def test_has_many_through_has_many_with_has_many_through_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Author.where('tags.id' => tags(:general).id), + [authors(:david)], :tags + ) + + # This ensures that the polymorphism of taggings is being observed correctly + authors = Author.joins(:tags).where('taggings.taggable_type' => 'FakeModel') + assert authors.empty? + end + + # has_many through + # Source: has_many + # Through: has_many through + def test_has_many_through_has_many_through_with_has_many_source_reflection + luke, david = subscribers(:first), subscribers(:second) + assert_equal [luke, david, david], authors(:david).subscribers.order('subscribers.nick') + end + + def test_has_many_through_has_many_through_with_has_many_source_reflection_preload + luke, david = subscribers(:first), subscribers(:second) + authors = assert_queries(4) { Author.includes(:subscribers).to_a } + assert_no_queries do + assert_equal [luke, david, david], authors.first.subscribers.sort_by(&:nick) + end + end + + def test_has_many_through_has_many_through_with_has_many_source_reflection_preload_via_joins + # All authors with subscribers where one of the subscribers' nick is 'alterself' + assert_includes_and_joins_equal( + Author.where('subscribers.nick' => 'alterself'), + [authors(:david)], :subscribers + ) + end + + # has_many through + # Source: has_one through + # Through: has_one + def test_has_many_through_has_one_with_has_one_through_source_reflection + assert_equal [member_types(:founding)], members(:groucho).nested_member_types + end + + def test_has_many_through_has_one_with_has_one_through_source_reflection_preload + members = assert_queries(4) { Member.includes(:nested_member_types).to_a } + founding = member_types(:founding) + assert_no_queries do + assert_equal [founding], members.first.nested_member_types + end + end + + def test_has_many_through_has_one_with_has_one_through_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Member.where('member_types.id' => member_types(:founding).id), + [members(:groucho)], :nested_member_types + ) + end + + # has_many through + # Source: has_one + # Through: has_one through + def test_has_many_through_has_one_through_with_has_one_source_reflection + assert_equal [sponsors(:moustache_club_sponsor_for_groucho)], members(:groucho).nested_sponsors + end + + def test_has_many_through_has_one_through_with_has_one_source_reflection_preload + members = assert_queries(4) { Member.includes(:nested_sponsors).to_a } + mustache = sponsors(:moustache_club_sponsor_for_groucho) + assert_no_queries do + assert_equal [mustache], members.first.nested_sponsors + end + end + + def test_has_many_through_has_one_through_with_has_one_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Member.where('sponsors.id' => sponsors(:moustache_club_sponsor_for_groucho).id), + [members(:groucho)], :nested_sponsors + ) + end + + # has_many through + # Source: has_many through + # Through: has_one + def test_has_many_through_has_one_with_has_many_through_source_reflection + groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) + + assert_equal [groucho_details, other_details], + members(:groucho).organization_member_details.order('member_details.id') + end + + def test_has_many_through_has_one_with_has_many_through_source_reflection_preload + members = assert_queries(4) { Member.includes(:organization_member_details).to_a.sort_by(&:id) } + groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) + + assert_no_queries do + assert_equal [groucho_details, other_details], members.first.organization_member_details.sort_by(&:id) + end + end + + def test_has_many_through_has_one_with_has_many_through_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Member.where('member_details.id' => member_details(:groucho).id).order('member_details.id'), + [members(:groucho), members(:some_other_guy)], :organization_member_details + ) + + members = Member.joins(:organization_member_details). + where('member_details.id' => 9) + assert members.empty? + end + + # has_many through + # Source: has_many + # Through: has_one through + def test_has_many_through_has_one_through_with_has_many_source_reflection + groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) + + assert_equal [groucho_details, other_details], + members(:groucho).organization_member_details_2.order('member_details.id') + end + + def test_has_many_through_has_one_through_with_has_many_source_reflection_preload + members = assert_queries(4) { Member.includes(:organization_member_details_2).to_a.sort_by(&:id) } + groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) + + assert_no_queries do + assert_equal [groucho_details, other_details], members.first.organization_member_details_2.sort_by(&:id) + end + end + + def test_has_many_through_has_one_through_with_has_many_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Member.where('member_details.id' => member_details(:groucho).id).order('member_details.id'), + [members(:groucho), members(:some_other_guy)], :organization_member_details_2 + ) + + members = Member.joins(:organization_member_details_2). + where('member_details.id' => 9) + assert members.empty? + end + + # has_many through + # Source: has_and_belongs_to_many + # Through: has_many + def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection + general, cooking = categories(:general), categories(:cooking) + + assert_equal [general, cooking], authors(:bob).post_categories.order('categories.id') + end + + def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection_preload + authors = assert_queries(3) { Author.includes(:post_categories).to_a.sort_by(&:id) } + general, cooking = categories(:general), categories(:cooking) + + assert_no_queries do + assert_equal [general, cooking], authors[2].post_categories.sort_by(&:id) + end + end + + def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Author.where('categories.id' => categories(:cooking).id), + [authors(:bob)], :post_categories + ) + end + + # has_many through + # Source: has_many + # Through: has_and_belongs_to_many + def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection + greetings, more = comments(:greetings), comments(:more_greetings) + + assert_equal [greetings, more], categories(:technology).post_comments.order('comments.id') + end + + def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload + categories = assert_queries(3) { Category.includes(:post_comments).to_a.sort_by(&:id) } + greetings, more = comments(:greetings), comments(:more_greetings) + + assert_no_queries do + assert_equal [greetings, more], categories[1].post_comments.sort_by(&:id) + end + end + + def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Category.where('comments.id' => comments(:more_greetings).id).order('comments.id'), + [categories(:general), categories(:technology)], :post_comments + ) + end + + # has_many through + # Source: has_many through a habtm + # Through: has_many through + def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection + greetings, more = comments(:greetings), comments(:more_greetings) + + assert_equal [greetings, more], authors(:bob).category_post_comments.order('comments.id') + end + + def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload + authors = assert_queries(5) { Author.includes(:category_post_comments).to_a.sort_by(&:id) } + greetings, more = comments(:greetings), comments(:more_greetings) + + assert_no_queries do + assert_equal [greetings, more], authors[2].category_post_comments.sort_by(&:id) + end + end + + def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Author.where('comments.id' => comments(:does_it_hurt).id).order('authors.id'), + [authors(:david), authors(:mary)], :category_post_comments + ) + end + + # has_many through + # Source: belongs_to + # Through: has_many through + def test_has_many_through_has_many_through_with_belongs_to_source_reflection + assert_equal [tags(:general), tags(:general)], authors(:david).tagging_tags + end + + def test_has_many_through_has_many_through_with_belongs_to_source_reflection_preload + authors = assert_queries(5) { Author.includes(:tagging_tags).to_a } + general = tags(:general) + + assert_no_queries do + assert_equal [general, general], authors.first.tagging_tags + end + end + + def test_has_many_through_has_many_through_with_belongs_to_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Author.where('tags.id' => tags(:general).id), + [authors(:david)], :tagging_tags + ) + end + + # has_many through + # Source: has_many through + # Through: belongs_to + def test_has_many_through_belongs_to_with_has_many_through_source_reflection + welcome_general, thinking_general = taggings(:welcome_general), taggings(:thinking_general) + + assert_equal [welcome_general, thinking_general], + categorizations(:david_welcome_general).post_taggings.order('taggings.id') + end + + def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload + categorizations = assert_queries(4) { Categorization.includes(:post_taggings).to_a.sort_by(&:id) } + welcome_general, thinking_general = taggings(:welcome_general), taggings(:thinking_general) + + assert_no_queries do + assert_equal [welcome_general, thinking_general], categorizations.first.post_taggings.sort_by(&:id) + end + end + + def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Categorization.where('taggings.id' => taggings(:welcome_general).id).order('taggings.id'), + [categorizations(:david_welcome_general)], :post_taggings + ) + end + + # has_one through + # Source: has_one through + # Through: has_one + def test_has_one_through_has_one_with_has_one_through_source_reflection + assert_equal member_types(:founding), members(:groucho).nested_member_type + end + + def test_has_one_through_has_one_with_has_one_through_source_reflection_preload + members = assert_queries(4) { Member.includes(:nested_member_type).to_a.sort_by(&:id) } + founding = member_types(:founding) + + assert_no_queries do + assert_equal founding, members.first.nested_member_type + end + end + + def test_has_one_through_has_one_with_has_one_through_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Member.where('member_types.id' => member_types(:founding).id), + [members(:groucho)], :nested_member_type + ) + end + + # has_one through + # Source: belongs_to + # Through: has_one through + def test_has_one_through_has_one_through_with_belongs_to_source_reflection + assert_equal categories(:general), members(:groucho).club_category + end + + def test_has_one_through_has_one_through_with_belongs_to_source_reflection_preload + members = assert_queries(4) { Member.includes(:club_category).to_a.sort_by(&:id) } + general = categories(:general) + + assert_no_queries do + assert_equal general, members.first.club_category + end + end + + def test_has_one_through_has_one_through_with_belongs_to_source_reflection_preload_via_joins + assert_includes_and_joins_equal( + Member.where('categories.id' => categories(:technology).id), + [members(:blarpy_winkup)], :club_category + ) + end + + def test_distinct_has_many_through_a_has_many_through_association_on_source_reflection + author = authors(:david) + assert_equal [tags(:general)], author.distinct_tags + end + + def test_distinct_has_many_through_a_has_many_through_association_on_through_reflection + author = authors(:david) + assert_equal [subscribers(:first), subscribers(:second)], + author.distinct_subscribers.order('subscribers.nick') + end + + def test_nested_has_many_through_with_a_table_referenced_multiple_times + author = authors(:bob) + assert_equal [posts(:misc_by_bob), posts(:misc_by_mary), posts(:other_by_bob), posts(:other_by_mary)], + author.similar_posts.sort_by(&:id) + + # Mary and Bob both have posts in misc, but they are the only ones. + authors = Author.joins(:similar_posts).where('posts.id' => posts(:misc_by_bob).id) + assert_equal [authors(:mary), authors(:bob)], authors.uniq.sort_by(&:id) + + # Check the polymorphism of taggings is being observed correctly (in both joins) + authors = Author.joins(:similar_posts).where('taggings.taggable_type' => 'FakeModel') + assert authors.empty? + authors = Author.joins(:similar_posts).where('taggings_authors_join.taggable_type' => 'FakeModel') + assert authors.empty? + end + + def test_has_many_through_with_foreign_key_option_on_through_reflection + assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order('posts.id') + assert_equal [authors(:david)], references(:david_unicyclist).agents_posts_authors + + references = Reference.joins(:agents_posts_authors).where('authors.id' => authors(:david).id) + assert_equal [references(:david_unicyclist)], references + end + + def test_has_many_through_with_foreign_key_option_on_source_reflection + assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order('people.id') + + jobs = Job.joins(:agents) + assert_equal [jobs(:unicyclist), jobs(:unicyclist)], jobs + end + + def test_has_many_through_with_sti_on_through_reflection + ratings = posts(:sti_comments).special_comments_ratings.sort_by(&:id) + assert_equal [ratings(:special_comment_rating), ratings(:sub_special_comment_rating)], ratings + + # Ensure STI is respected in the join + scope = Post.joins(:special_comments_ratings).where(:id => posts(:sti_comments).id) + assert scope.where("comments.type" => "Comment").empty? + assert !scope.where("comments.type" => "SpecialComment").empty? + assert !scope.where("comments.type" => "SubSpecialComment").empty? + end + + def test_has_many_through_with_sti_on_nested_through_reflection + taggings = posts(:sti_comments).special_comments_ratings_taggings + assert_equal [taggings(:special_comment_rating)], taggings + + scope = Post.joins(:special_comments_ratings_taggings).where(:id => posts(:sti_comments).id) + assert scope.where("comments.type" => "Comment").empty? + assert !scope.where("comments.type" => "SpecialComment").empty? + end + + def test_nested_has_many_through_writers_should_raise_error + david = authors(:david) + subscriber = subscribers(:first) + + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do + david.subscribers = [subscriber] + end + + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do + david.subscriber_ids = [subscriber.id] + end + + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do + david.subscribers << subscriber + end + + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do + david.subscribers.delete(subscriber) + end + + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do + david.subscribers.clear + end + + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do + david.subscribers.build + end + + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do + david.subscribers.create + end + end + + def test_nested_has_one_through_writers_should_raise_error + groucho = members(:groucho) + founding = member_types(:founding) + + assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do + groucho.nested_member_type = founding + end + end + + def test_nested_has_many_through_with_conditions_on_through_associations + assert_equal [tags(:blue)], authors(:bob).misc_post_first_blue_tags + end + + def test_nested_has_many_through_with_conditions_on_through_associations_preload + assert Author.where('tags.id' => 100).joins(:misc_post_first_blue_tags).empty? + + authors = assert_queries(3) { Author.includes(:misc_post_first_blue_tags).to_a.sort_by(&:id) } + blue = tags(:blue) + + assert_no_queries do + assert_equal [blue], authors[2].misc_post_first_blue_tags + end + end + + def test_nested_has_many_through_with_conditions_on_through_associations_preload_via_joins + # Pointless condition to force single-query loading + assert_includes_and_joins_equal( + Author.where('tags.id = tags.id'), + [authors(:bob)], :misc_post_first_blue_tags + ) + end + + def test_nested_has_many_through_with_conditions_on_source_associations + assert_equal [tags(:blue)], authors(:bob).misc_post_first_blue_tags_2 + end + + def test_nested_has_many_through_with_conditions_on_source_associations_preload + authors = assert_queries(4) { Author.includes(:misc_post_first_blue_tags_2).to_a.sort_by(&:id) } + blue = tags(:blue) + + assert_no_queries do + assert_equal [blue], authors[2].misc_post_first_blue_tags_2 + end + end + + def test_nested_has_many_through_with_conditions_on_source_associations_preload_via_joins + # Pointless condition to force single-query loading + assert_includes_and_joins_equal( + Author.where('tags.id = tags.id'), + [authors(:bob)], :misc_post_first_blue_tags_2 + ) + end + + def test_nested_has_many_through_with_foreign_key_option_on_the_source_reflection_through_reflection + assert_equal [categories(:general)], organizations(:nsa).author_essay_categories + + organizations = Organization.joins(:author_essay_categories). + where('categories.id' => categories(:general).id) + assert_equal [organizations(:nsa)], organizations + + assert_equal categories(:general), organizations(:nsa).author_owned_essay_category + + organizations = Organization.joins(:author_owned_essay_category). + where('categories.id' => categories(:general).id) + assert_equal [organizations(:nsa)], organizations + end + + private + + def assert_includes_and_joins_equal(query, expected, association) + actual = assert_queries(1) { query.joins(association).to_a.uniq } + assert_equal expected, actual + + actual = assert_queries(1) { query.includes(association).to_a.uniq } + assert_equal expected, actual + end +end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index dfacf58da8..d8638ee776 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -118,22 +118,18 @@ class AttributeMethodsTest < ActiveRecord::TestCase end def test_read_attributes_before_type_cast_on_datetime - developer = Developer.find(:first) - if current_adapter?(:Mysql2Adapter, :OracleAdapter) - # Mysql2 and Oracle adapters keep the value in Time instance - assert_equal developer.created_at.to_s(:db), developer.attributes_before_type_cast["created_at"].to_s(:db) - else - assert_equal developer.created_at.to_s(:db), developer.attributes_before_type_cast["created_at"].to_s + in_time_zone "Pacific Time (US & Canada)" do + record = @target.new + + record.written_on = "345643456" + assert_equal "345643456", record.written_on_before_type_cast + assert_equal nil, record.written_on + + record.written_on = "2009-10-11 12:13:14" + assert_equal "2009-10-11 12:13:14", record.written_on_before_type_cast + assert_equal Time.zone.parse("2009-10-11 12:13:14"), record.written_on + assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone end - - developer.created_at = "345643456" - - assert_equal developer.created_at_before_type_cast, "345643456" - assert_equal developer.created_at, nil - - developer.created_at = "2010-03-21 21:23:32" - assert_equal developer.created_at_before_type_cast, "2010-03-21 21:23:32" - assert_equal developer.created_at, Time.parse("2010-03-21 21:23:32") end def test_hash_content diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index b62b5003e4..fba7af741d 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -45,6 +45,8 @@ class ReadonlyTitlePost < Post attr_readonly :title end +class Weird < ActiveRecord::Base; end + class Boolean < ActiveRecord::Base; end class BasicsTest < ActiveRecord::TestCase @@ -477,6 +479,16 @@ class BasicsTest < ActiveRecord::TestCase assert_equal "changed", post.body end + def test_non_valid_identifier_column_name + weird = Weird.create('a$b' => 'value') + weird.reload + assert_equal 'value', weird.send('a$b') + + weird.update_attribute('a$b', 'value2') + weird.reload + assert_equal 'value2', weird.send('a$b') + end + def test_multiparameter_attributes_on_date attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" } topic = Topic.find(1) @@ -832,12 +844,12 @@ class BasicsTest < ActiveRecord::TestCase def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes developer = Developer.create! :name => 'Bjorn' - assert !developer.name_changed? # both attributes of saved object should be threated as not changed + assert !developer.name_changed? # both attributes of saved object should be treated as not changed assert !developer.salary_changed? cloned_developer = developer.dup assert cloned_developer.name_changed? # ... but on cloned object should be - assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be threated as not changed on cloned instance + assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be treated as not changed on cloned instance end def test_bignum diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index 6f65fb96d1..dc0e0da4c5 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -25,7 +25,7 @@ class EachTest < ActiveRecord::TestCase end def test_each_should_execute_if_id_is_in_select - assert_queries(4) do + assert_queries(6) do Post.find_each(:select => "id, title, type", :batch_size => 2) do |post| assert_kind_of Post, post end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index c35590b84b..543981b4a0 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -124,11 +124,13 @@ class FinderTest < ActiveRecord::TestCase def test_find_all_with_limit_and_offset_and_multiple_order_clauses first_three_posts = Post.find :all, :order => 'author_id, id', :limit => 3, :offset => 0 second_three_posts = Post.find :all, :order => ' author_id,id ', :limit => 3, :offset => 3 - last_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 6 + third_three_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 6 + last_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 9 assert_equal [[0,3],[1,1],[1,2]], first_three_posts.map { |p| [p.author_id, p.id] } assert_equal [[1,4],[1,5],[1,6]], second_three_posts.map { |p| [p.author_id, p.id] } - assert_equal [[2,7]], last_posts.map { |p| [p.author_id, p.id] } + assert_equal [[2,7],[2,9],[2,11]], third_three_posts.map { |p| [p.author_id, p.id] } + assert_equal [[3,8],[3,10]], last_posts.map { |p| [p.author_id, p.id] } end @@ -189,6 +191,30 @@ class FinderTest < ActiveRecord::TestCase assert_nil Topic.where("title = 'The Second Topic of the day!'").first end + def test_first_bang_present + assert_nothing_raised do + assert_equal topics(:second), Topic.where("title = 'The Second Topic of the day'").first! + end + end + + def test_first_bang_missing + assert_raises ActiveRecord::RecordNotFound do + Topic.where("title = 'This title does not exist'").first! + end + end + + def test_last_bang_present + assert_nothing_raised do + assert_equal topics(:second), Topic.where("title = 'The Second Topic of the day'").last! + end + end + + def test_last_bang_missing + assert_raises ActiveRecord::RecordNotFound do + Topic.where("title = 'This title does not exist'").last! + end + end + def test_unexisting_record_exception_handling assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1).parent diff --git a/activerecord/test/cases/habtm_destroy_order_test.rb b/activerecord/test/cases/habtm_destroy_order_test.rb index 15598392e2..f2b91d977e 100644 --- a/activerecord/test/cases/habtm_destroy_order_test.rb +++ b/activerecord/test/cases/habtm_destroy_order_test.rb @@ -13,5 +13,39 @@ class HabtmDestroyOrderTest < ActiveRecord::TestCase sicp.destroy end end + assert !sicp.destroyed? + end + + test "not destroying a student with lessons leaves student<=>lesson association intact" do + # test a normal before_destroy doesn't destroy the habtm joins + begin + sicp = Lesson.new(:name => "SICP") + ben = Student.new(:name => "Ben Bitdiddle") + # add a before destroy to student + Student.class_eval do + before_destroy do + raise ActiveRecord::Rollback unless lessons.empty? + end + end + ben.lessons << sicp + ben.save! + ben.destroy + assert !ben.reload.lessons.empty? + ensure + # get rid of it so Student is still like it was + Student.reset_callbacks(:destroy) + end + end + + test "not destroying a lesson with students leaves student<=>lesson association intact" do + # test a more aggressive before_destroy doesn't destroy the habtm joins and still throws the exception + sicp = Lesson.new(:name => "SICP") + ben = Student.new(:name => "Ben Bitdiddle") + sicp.students << ben + sicp.save! + assert_raises LessonError do + sicp.destroy + end + assert !sicp.reload.students.empty? end end diff --git a/activerecord/test/cases/identity_map_test.rb b/activerecord/test/cases/identity_map_test.rb index d98638ab73..89f7b92d09 100644 --- a/activerecord/test/cases/identity_map_test.rb +++ b/activerecord/test/cases/identity_map_test.rb @@ -207,31 +207,31 @@ class IdentityMapTest < ActiveRecord::TestCase def test_find_with_preloaded_associations assert_queries(2) do - posts = Post.preload(:comments) + posts = Post.preload(:comments).order('posts.id') assert posts.first.comments.first end # With IM we'll retrieve post object from previous query, it'll have comments # already preloaded from first call assert_queries(1) do - posts = Post.preload(:comments).to_a + posts = Post.preload(:comments).order('posts.id') assert posts.first.comments.first end assert_queries(2) do - posts = Post.preload(:author) + posts = Post.preload(:author).order('posts.id') assert posts.first.author end # With IM we'll retrieve post object from previous query, it'll have comments # already preloaded from first call assert_queries(1) do - posts = Post.preload(:author).to_a + posts = Post.preload(:author).order('posts.id') assert posts.first.author end assert_queries(1) do - posts = Post.preload(:author, :comments).to_a + posts = Post.preload(:author, :comments).order('posts.id') assert posts.first.author assert posts.first.comments.first end @@ -239,22 +239,22 @@ class IdentityMapTest < ActiveRecord::TestCase def test_find_with_included_associations assert_queries(2) do - posts = Post.includes(:comments) + posts = Post.includes(:comments).order('posts.id') assert posts.first.comments.first end assert_queries(1) do - posts = Post.scoped.includes(:comments) + posts = Post.scoped.includes(:comments).order('posts.id') assert posts.first.comments.first end assert_queries(2) do - posts = Post.includes(:author) + posts = Post.includes(:author).order('posts.id') assert posts.first.author end assert_queries(1) do - posts = Post.includes(:author, :comments).to_a + posts = Post.includes(:author, :comments).order('posts.id') assert posts.first.author assert posts.first.comments.first end @@ -295,7 +295,7 @@ class IdentityMapTest < ActiveRecord::TestCase end ############################################################################## - # Behaviour releated to saving failures + # Behaviour related to saving failures ############################################################################## def test_reload_object_if_save_failed @@ -338,7 +338,7 @@ class IdentityMapTest < ActiveRecord::TestCase end ############################################################################## - # Behaviour of readonly, forzen, destroyed + # Behaviour of readonly, frozen, destroyed ############################################################################## def test_find_using_identity_map_respects_readonly_when_loading_associated_object_first diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb index 5da7f9e1b9..8664d63e8f 100644 --- a/activerecord/test/cases/json_serialization_test.rb +++ b/activerecord/test/cases/json_serialization_test.rb @@ -181,7 +181,11 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase def test_should_allow_except_option_for_list_of_authors ActiveRecord::Base.include_root_in_json = false authors = [@david, @mary] - assert_equal %([{"id":1},{"id":2}]), ActiveSupport::JSON.encode(authors, :except => [:name, :author_address_id, :author_address_extra_id]) + encoded = ActiveSupport::JSON.encode(authors, :except => [ + :name, :author_address_id, :author_address_extra_id, + :organization_id, :owned_essay_id + ]) + assert_equal %([{"id":1},{"id":2}]), encoded ensure ActiveRecord::Base.include_root_in_json = true end @@ -196,7 +200,7 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase ) ['"name":"David"', '"posts":[', '{"id":1}', '{"id":2}', '{"id":4}', - '{"id":5}', '{"id":6}', '"name":"Mary"', '"posts":[{"id":7}]'].each do |fragment| + '{"id":5}', '{"id":6}', '"name":"Mary"', '"posts":[', '{"id":7}', '{"id":9}'].each do |fragment| assert json.include?(fragment), json end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 9d7c49768b..bf7565a0d0 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -543,7 +543,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal "I was born ....", bob.bio assert_equal 18, bob.age - # Test for 30 significent digits (beyond the 16 of float), 10 of them + # Test for 30 significant digits (beyond the 16 of float), 10 of them # after the decimal place. unless current_adapter?(:SQLite3Adapter) @@ -1975,7 +1975,7 @@ if ActiveRecord::Base.connection.supports_migrations? t.integer :age end - # Adding an index fires a query everytime to check if an index already exists or not + # Adding an index fires a query every time to check if an index already exists or not assert_queries(3) do with_bulk_change_table do |t| t.index :username, :unique => true, :name => :awesome_username_index diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index 6269437b14..379cf5b44e 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -94,6 +94,11 @@ class PooledConnectionsTest < ActiveRecord::TestCase ActiveRecord::Base.connection_handler = old_handler end + def test_connection_config + ActiveRecord::Base.establish_connection(@connection) + assert_equal @connection, ActiveRecord::Base.connection_config + end + def test_with_connection_nesting_safety ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1, :wait_timeout => 0.1})) diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index eb580928ba..97d9669483 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -7,9 +7,16 @@ require 'models/subscriber' require 'models/ship' require 'models/pirate' require 'models/price_estimate' -require 'models/tagging' +require 'models/essay' require 'models/author' +require 'models/organization' require 'models/post' +require 'models/tagging' +require 'models/category' +require 'models/book' +require 'models/subscriber' +require 'models/subscription' +require 'models/tag' require 'models/sponsor' class ReflectionTest < ActiveRecord::TestCase @@ -195,10 +202,54 @@ class ReflectionTest < ActiveRecord::TestCase assert_kind_of ThroughReflection, Subscriber.reflect_on_association(:books) end + def test_chain + expected = [ + Organization.reflect_on_association(:author_essay_categories), + Author.reflect_on_association(:essays), + Organization.reflect_on_association(:authors) + ] + actual = Organization.reflect_on_association(:author_essay_categories).chain + + assert_equal expected, actual + end + + def test_conditions + expected = [ + [{ :tags => { :name => 'Blue' } }], + [{ :taggings => { :comment => 'first' } }, { "taggable_type" => "Post" }], + [{ :posts => { :title => ['misc post by bob', 'misc post by mary'] } }] + ] + actual = Author.reflect_on_association(:misc_post_first_blue_tags).conditions + assert_equal expected, actual + + expected = [ + [{ :tags => { :name => 'Blue' } }, { :taggings => { :comment => 'first' } }, { :posts => { :title => ['misc post by bob', 'misc post by mary'] } }], + [{ "taggable_type" => "Post" }], + [] + ] + actual = Author.reflect_on_association(:misc_post_first_blue_tags_2).conditions + assert_equal expected, actual + end + + def test_nested? + assert !Author.reflect_on_association(:comments).nested? + assert Author.reflect_on_association(:tags).nested? + + # Only goes :through once, but the through_reflection is a has_and_belongs_to_many, so this is + # a nested through association + assert Category.reflect_on_association(:post_comments).nested? + end + def test_association_primary_key - assert_equal "id", Author.reflect_on_association(:posts).association_primary_key.to_s + # Normal association + assert_equal "id", Author.reflect_on_association(:posts).association_primary_key.to_s assert_equal "name", Author.reflect_on_association(:essay).association_primary_key.to_s - assert_equal "id", Tagging.reflect_on_association(:taggable).association_primary_key.to_s + assert_equal "id", Tagging.reflect_on_association(:taggable).association_primary_key.to_s + + # Through association (uses the :primary_key option from the source reflection) + assert_equal "nick", Author.reflect_on_association(:subscribers).association_primary_key.to_s + assert_equal "name", Author.reflect_on_association(:essay_category).association_primary_key.to_s + assert_equal "custom_primary_key", Author.reflect_on_association(:tags_with_primary_key).association_primary_key.to_s # nested end def test_active_record_primary_key diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index cda2850b02..7369aaea1d 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -335,7 +335,7 @@ class DefaultScopingTest < ActiveRecord::TestCase end records = klass.all - assert_equal 1, records.length + assert_equal 3, records.length assert_equal 2, records.first.author_id end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index e9c006d623..dad6665990 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -165,7 +165,7 @@ class RelationTest < ActiveRecord::TestCase def test_finding_with_complex_order tags = Tag.includes(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").to_a - assert_equal 2, tags.length + assert_equal 3, tags.length end def test_finding_with_order_limit_and_offset @@ -281,27 +281,27 @@ class RelationTest < ActiveRecord::TestCase def test_find_with_preloaded_associations assert_queries(2) do - posts = Post.preload(:comments) + posts = Post.preload(:comments).order('posts.id') assert posts.first.comments.first end assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 2) do - posts = Post.preload(:comments).to_a + posts = Post.preload(:comments).order('posts.id') assert posts.first.comments.first end assert_queries(2) do - posts = Post.preload(:author) + posts = Post.preload(:author).order('posts.id') assert posts.first.author end assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 2) do - posts = Post.preload(:author).to_a + posts = Post.preload(:author).order('posts.id') assert posts.first.author end assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 3) do - posts = Post.preload(:author, :comments).to_a + posts = Post.preload(:author, :comments).order('posts.id') assert posts.first.author assert posts.first.comments.first end @@ -309,22 +309,22 @@ class RelationTest < ActiveRecord::TestCase def test_find_with_included_associations assert_queries(2) do - posts = Post.includes(:comments) + posts = Post.includes(:comments).order('posts.id') assert posts.first.comments.first end assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 2) do - posts = Post.scoped.includes(:comments) + posts = Post.scoped.includes(:comments).order('posts.id') assert posts.first.comments.first end assert_queries(2) do - posts = Post.includes(:author) + posts = Post.includes(:author).order('posts.id') assert posts.first.author end assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 3) do - posts = Post.includes(:author, :comments).to_a + posts = Post.includes(:author, :comments).order('posts.id') assert posts.first.author assert posts.first.comments.first end @@ -538,7 +538,7 @@ class RelationTest < ActiveRecord::TestCase def test_last authors = Author.scoped - assert_equal authors(:mary), authors.last + assert_equal authors(:bob), authors.last end def test_destroy_all @@ -618,22 +618,22 @@ class RelationTest < ActiveRecord::TestCase def test_count posts = Post.scoped - assert_equal 7, posts.count - assert_equal 7, posts.count(:all) - assert_equal 7, posts.count(:id) + assert_equal 11, posts.count + assert_equal 11, posts.count(:all) + assert_equal 11, posts.count(:id) assert_equal 1, posts.where('comments_count > 1').count - assert_equal 5, posts.where(:comments_count => 0).count + assert_equal 9, posts.where(:comments_count => 0).count end def test_count_with_distinct posts = Post.scoped assert_equal 3, posts.count(:comments_count, :distinct => true) - assert_equal 7, posts.count(:comments_count, :distinct => false) + assert_equal 11, posts.count(:comments_count, :distinct => false) assert_equal 3, posts.select(:comments_count).count(:distinct => true) - assert_equal 7, posts.select(:comments_count).count(:distinct => false) + assert_equal 11, posts.select(:comments_count).count(:distinct => false) end def test_count_explicit_columns @@ -643,7 +643,7 @@ class RelationTest < ActiveRecord::TestCase assert_equal [0], posts.select('comments_count').where('id is not null').group('id').order('id').count.values.uniq assert_equal 0, posts.where('id is not null').select('comments_count').count - assert_equal 7, posts.select('comments_count').count('id') + assert_equal 11, posts.select('comments_count').count('id') assert_equal 0, posts.select('comments_count').count assert_equal 0, posts.count(:comments_count) assert_equal 0, posts.count('comments_count') @@ -658,12 +658,12 @@ class RelationTest < ActiveRecord::TestCase def test_size posts = Post.scoped - assert_queries(1) { assert_equal 7, posts.size } + assert_queries(1) { assert_equal 11, posts.size } assert ! posts.loaded? best_posts = posts.where(:comments_count => 0) best_posts.to_a # force load - assert_no_queries { assert_equal 5, best_posts.size } + assert_no_queries { assert_equal 9, best_posts.size } end def test_size_with_limit @@ -701,6 +701,32 @@ class RelationTest < ActiveRecord::TestCase assert_equal expected, posts.count end + def test_empty + posts = Post.scoped + + assert_queries(1) { assert_equal false, posts.empty? } + assert ! posts.loaded? + + no_posts = posts.where(:title => "") + assert_queries(1) { assert_equal true, no_posts.empty? } + assert ! no_posts.loaded? + + best_posts = posts.where(:comments_count => 0) + best_posts.to_a # force load + assert_no_queries { assert_equal false, best_posts.empty? } + end + + def test_empty_complex_chained_relations + posts = Post.select("comments_count").where("id is not null").group("author_id").where("comments_count > 0") + + assert_queries(1) { assert_equal false, posts.empty? } + assert ! posts.loaded? + + no_posts = posts.where(:title => "") + assert_queries(1) { assert_equal true, no_posts.empty? } + assert ! no_posts.loaded? + end + def test_any posts = Post.scoped @@ -799,6 +825,10 @@ class RelationTest < ActiveRecord::TestCase assert_equal Post.all, all_posts.all end + def test_extensions_with_except + assert_equal 2, Topic.named_extension.order(:author_name).except(:order).two + end + def test_only relation = Post.where(:author_id => 1).order('id ASC').limit(1) assert_equal [posts(:welcome)], relation.all @@ -810,6 +840,10 @@ class RelationTest < ActiveRecord::TestCase assert_equal Post.limit(1).all.first, all_posts.first end + def test_extensions_with_only + assert_equal 2, Topic.named_extension.order(:author_name).only(:order).two + end + def test_anonymous_extension relation = Post.where(:author_id => 1).order('id ASC').extending do def author @@ -878,4 +912,19 @@ class RelationTest < ActiveRecord::TestCase def test_primary_key assert_equal "id", Post.scoped.primary_key end + + def test_eager_loading_with_conditions_on_joins + scope = Post.includes(:comments) + + # This references the comments table, and so it should cause the comments to be eager + # loaded via a JOIN, rather than by subsequent queries. + scope = scope.joins( + Post.arel_table.create_join( + Post.arel_table, + Post.arel_table.create_on(Comment.arel_table[:id].eq(3)) + ) + ) + + assert scope.eager_loading? + end end |