diff options
Diffstat (limited to 'activerecord/test/cases/identity_map_test.rb')
-rw-r--r-- | activerecord/test/cases/identity_map_test.rb | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/activerecord/test/cases/identity_map_test.rb b/activerecord/test/cases/identity_map_test.rb new file mode 100644 index 0000000000..bed464dbce --- /dev/null +++ b/activerecord/test/cases/identity_map_test.rb @@ -0,0 +1,404 @@ +require "cases/helper" +require 'models/developer' +require 'models/project' +require 'models/company' +require 'models/topic' +require 'models/reply' +require 'models/computer' +require 'models/customer' +require 'models/order' +require 'models/post' +require 'models/author' +require 'models/tag' +require 'models/tagging' +require 'models/comment' +require 'models/sponsor' +require 'models/member' +require 'models/essay' +require 'models/subscriber' +require "models/pirate" +require "models/bird" +require "models/parrot" + +class IdentityMapTest < ActiveRecord::TestCase + fixtures :accounts, :companies, :developers, :projects, :topics, + :developers_projects, :computers, :authors, :author_addresses, + :posts, :tags, :taggings, :comments, :subscribers + + def setup + skip unless ActiveRecord::IdentityMap.enabled? + end + + ############################################################################## + # Basic tests checking if IM is functioning properly on basic find operations# + ############################################################################## + + def test_find_id + assert_same(Client.find(3), Client.find(3)) + end + + def test_find_id_without_identity_map + ActiveRecord::IdentityMap.without do + assert_not_same(Client.find(3), Client.find(3)) + end + end + + def test_find_id_use_identity_map + ActiveRecord::IdentityMap.enabled = false + ActiveRecord::IdentityMap.use do + assert_same(Client.find(3), Client.find(3)) + end + ActiveRecord::IdentityMap.enabled = true + end + + def test_find_pkey + assert_same( + Subscriber.find('swistak'), + Subscriber.find('swistak') + ) + end + + def test_find_by_id + assert_same( + Client.find_by_id(3), + Client.find_by_id(3) + ) + end + + def test_find_by_string_and_numeric_id + assert_same( + Client.find_by_id("3"), + Client.find_by_id(3) + ) + end + + def test_find_by_pkey + assert_same( + Subscriber.find_by_nick('swistak'), + Subscriber.find_by_nick('swistak') + ) + end + + def test_find_first_id + assert_same( + Client.find(:first, :conditions => {:id => 1}), + Client.find(:first, :conditions => {:id => 1}) + ) + end + + def test_find_first_pkey + assert_same( + Subscriber.find(:first, :conditions => {:nick => 'swistak'}), + Subscriber.find(:first, :conditions => {:nick => 'swistak'}) + ) + end + + ############################################################################## + # Tests checking if IM is functioning properly on more advanced finds # + # and associations # + ############################################################################## + + def test_owner_object_is_associated_from_identity_map + post = Post.find(1) + comment = post.comments.first + + assert_no_queries do + comment.post + end + assert_same post, comment.post.target + end + + def test_associated_object_are_assigned_from_identity_map + post = Post.find(1) + + post.comments.each do |comment| + assert_same post, comment.post.target + assert_equal post.object_id, comment.post.target.object_id + end + end + + def test_creation + t1 = Topic.create("title" => "t1") + t2 = Topic.find(t1.id) + assert_same(t1, t2) + end + + ############################################################################## + # Tests checking dirty attribute behaviour with IM # + ############################################################################## + + def test_loading_new_instance_should_not_update_dirty_attributes + swistak = Subscriber.find(:first, :conditions => {:nick => 'swistak'}) + swistak.name = "Swistak Sreberkowiec" + assert_equal(["name"], swistak.changed) + assert_equal({"name" => ["Marcin Raczkowski", "Swistak Sreberkowiec"]}, swistak.changes) + + s = Subscriber.find('swistak') + + assert swistak.name_changed? + assert_equal("Swistak Sreberkowiec", swistak.name) + end + + def test_loading_new_instance_should_change_dirty_attribute_original_value + swistak = Subscriber.find(:first, :conditions => {:nick => 'swistak'}) + swistak.name = "Swistak Sreberkowiec" + + Subscriber.update_all({:name => "Raczkowski Marcin"}, {:name => "Marcin Raczkowski"}) + + s = Subscriber.find('swistak') + + assert_equal({'name' => ["Raczkowski Marcin", "Swistak Sreberkowiec"]}, swistak.changes) + assert_equal("Swistak Sreberkowiec", swistak.name) + end + + def test_loading_new_instance_should_remove_dirt + swistak = Subscriber.find(:first, :conditions => {:nick => 'swistak'}) + swistak.name = "Swistak Sreberkowiec" + + assert_equal({"name" => ["Marcin Raczkowski", "Swistak Sreberkowiec"]}, swistak.changes) + + Subscriber.update_all({:name => "Swistak Sreberkowiec"}, {:name => "Marcin Raczkowski"}) + + s = Subscriber.find('swistak') + + assert_equal("Swistak Sreberkowiec", swistak.name) + assert_equal({}, swistak.changes) + assert !swistak.name_changed? + end + + def test_has_many_associations + pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?") + pirate.birds.create!(:name => 'Posideons Killer') + pirate.birds.create!(:name => 'Killer bandita Dionne') + + posideons, killer = pirate.birds + + pirate.reload + + pirate.birds_attributes = [{ :id => posideons.id, :name => 'Grace OMalley' }] + assert_equal 'Grace OMalley', pirate.birds.send(:load_target).find { |r| r.id == posideons.id }.name + end + + def test_changing_associations + post1 = Post.create("title" => "One post", "body" => "Posting...") + post2 = Post.create("title" => "Another post", "body" => "Posting... Again...") + comment = Comment.new("body" => "comment") + + comment.post = post1 + assert comment.save + + assert_same(post1.comments.first, comment) + + comment.post = post2 + assert comment.save + + assert_same(post2.comments.first, comment) + assert_equal(0, post1.comments.size) + end + + def test_im_with_polymorphic_has_many_going_through_join_model_with_custom_select_and_joins + tag = posts(:welcome).tags.first + tag_with_joins_and_select = posts(:welcome).tags.add_joins_and_select.first + assert_same(tag, tag_with_joins_and_select) + assert_nothing_raised(NoMethodError, "Joins/select was not loaded") { tag.author_id } + end + + ############################################################################## + # Tests checking Identity Map behaviour with preloaded associations, joins, # + # includes etc. # + ############################################################################## + + def test_find_with_preloaded_associations + assert_queries(2) do + posts = Post.preload(:comments) + 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 + assert posts.first.comments.first + end + + assert_queries(2) do + posts = Post.preload(:author) + 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 + assert posts.first.author + end + + assert_queries(1) do + posts = Post.preload(:author, :comments).to_a + assert posts.first.author + assert posts.first.comments.first + end + end + + def test_find_with_included_associations + assert_queries(2) do + posts = Post.includes(:comments) + assert posts.first.comments.first + end + + assert_queries(1) do + posts = Post.scoped.includes(:comments) + assert posts.first.comments.first + end + + assert_queries(2) do + posts = Post.includes(:author) + assert posts.first.author + end + + assert_queries(1) do + posts = Post.includes(:author, :comments).to_a + assert posts.first.author + assert posts.first.comments.first + end + end + + def test_eager_loading_with_conditions_on_joined_table_preloads + posts = Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => [:comments], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id') + assert_equal [posts(:welcome)], posts + assert_equal authors(:david), assert_no_queries { posts[0].author} + assert_same posts.first.author.target, Author.first + + posts = Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => [:comments], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id') + assert_equal [posts(:welcome)], posts + assert_equal authors(:david), assert_no_queries { posts[0].author} + assert_same posts.first.author.target, Author.first + + posts = Post.find(:all, :include => :author, :joins => {:taggings => :tag}, :conditions => "tags.name = 'General'", :order => 'posts.id') + assert_equal posts(:welcome, :thinking), posts + assert_same posts.first.author.target, Author.first + + posts = Post.find(:all, :include => :author, :joins => {:taggings => {:tag => :taggings}}, :conditions => "taggings_tags.super_tag_id=2", :order => 'posts.id') + assert_equal posts(:welcome, :thinking), posts + assert_same posts.first.author.target, Author.first + end + + def test_eager_loading_with_conditions_on_string_joined_table_preloads + posts = assert_queries(2) do + Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => "INNER JOIN comments on comments.post_id = posts.id", :conditions => "comments.body like 'Thank you%'", :order => 'posts.id') + end + assert_equal [posts(:welcome)], posts + assert_equal authors(:david), assert_no_queries { posts[0].author} + + posts = assert_queries(1) do + Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => ["INNER JOIN comments on comments.post_id = posts.id"], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id') + end + assert_equal [posts(:welcome)], posts + assert_equal authors(:david), assert_no_queries { posts[0].author} + end + + ############################################################################## + # Behaviour releated to saving failures + ############################################################################## + + def test_reload_object_if_save_failed + developer = Developer.first + developer.salary = 0 + + assert !developer.save + + same_developer = Developer.first + + assert_not_same developer, same_developer + assert_not_equal 0, same_developer.salary + assert_not_equal developer.salary, same_developer.salary + end + + def test_reload_object_if_forced_save_failed + developer = Developer.first + developer.salary = 0 + + assert_raise(ActiveRecord::RecordInvalid) { developer.save! } + + same_developer = Developer.first + + assert_not_same developer, same_developer + assert_not_equal 0, same_developer.salary + assert_not_equal developer.salary, same_developer.salary + end + + def test_reload_object_if_update_attributes_fails + developer = Developer.first + developer.salary = 0 + + assert !developer.update_attributes(:salary => 0) + + same_developer = Developer.first + + assert_not_same developer, same_developer + assert_not_equal 0, same_developer.salary + assert_not_equal developer.salary, same_developer.salary + end + + ############################################################################## + # Behaviour of readonly, forzen, destroyed + ############################################################################## + + def test_find_using_identity_map_respects_readonly_when_loading_associated_object_first + author = Author.first + readonly_comment = author.readonly_comments.first + + comment = Comment.first + assert !comment.readonly? + + assert readonly_comment.readonly? + + assert_raise(ActiveRecord::ReadOnlyRecord) {readonly_comment.save} + assert comment.save + end + + def test_find_using_identity_map_respects_readonly + comment = Comment.first + assert !comment.readonly? + + author = Author.first + readonly_comment = author.readonly_comments.first + + assert readonly_comment.readonly? + + assert_raise(ActiveRecord::ReadOnlyRecord) {readonly_comment.save} + assert comment.save + end + + def test_find_using_select_and_identity_map + author_id, author = Author.select('id').first, Author.first + + assert_equal author_id, author + assert_same author_id, author + assert_not_nil author.name + + post, post_id = Post.first, Post.select('id').first + + assert_equal post_id, post + assert_same post_id, post + assert_not_nil post.title + end + +# Currently AR is not allowing changing primary key (see Persistence#update) +# So we ignore it. If this changes, this test needs to be uncommented. +# def test_updating_of_pkey +# assert client = Client.find(3), +# client.update_attribute(:id, 666) +# +# assert Client.find(666) +# assert_same(client, Client.find(666)) +# +# s = Subscriber.find_by_nick('swistak') +# assert s.update_attribute(:nick, 'swistakTheJester') +# assert_equal('swistakTheJester', s.nick) +# +# assert stj = Subscriber.find_by_nick('swistakTheJester') +# assert_same(s, stj) +# end + +end |