test: cover OrmPolymorphicTest and teardown
This commit is contained in:
1 parent
9efcddd66a
commit
84db98ce58
1 file changed
+174
@@ -0,0 +1,174 @@
|
||||
require 'minitest/autorun'
|
||||
require 'prism'
|
||||
require 'tmpdir'
|
||||
|
||||
class OrmPolymorphicTest < Minitest::Test
|
||||
def setup
|
||||
@dir = Dir.mktmpdir('prism-polymorphic-store')
|
||||
@store = CountingPolymorphicStore.new(File.join(@dir, 'test.pstore'))
|
||||
@store.create_table(:posts, %w[id title])
|
||||
@store.create_table(:photos, %w[id caption])
|
||||
@store.create_table(:likes, %w[id likeable_id likeable_type user_id])
|
||||
@store.create_table(:previews, %w[id previewable_id previewable_type url])
|
||||
|
||||
post_class = Class.new(Prism::Orm::Model) do
|
||||
table_name :posts
|
||||
has_many :likes, class_name: 'Like', as: :likeable, dependent: :destroy
|
||||
has_one :preview, class_name: 'Preview', as: :previewable, dependent: :nullify
|
||||
end
|
||||
photo_class = Class.new(Prism::Orm::Model) do
|
||||
table_name :photos
|
||||
has_many :likes, class_name: 'Like', as: :likeable, dependent: :destroy
|
||||
end
|
||||
like_class = Class.new(Prism::Orm::Model) do
|
||||
table_name :likes
|
||||
belongs_to :likeable, polymorphic: true
|
||||
end
|
||||
preview_class = Class.new(Prism::Orm::Model) do
|
||||
table_name :previews
|
||||
belongs_to :previewable, polymorphic: true
|
||||
end
|
||||
[post_class, photo_class, like_class, preview_class].each { |klass| klass.store = @store }
|
||||
Object.const_set(:Post, post_class)
|
||||
Object.const_set(:Photo, photo_class)
|
||||
Object.const_set(:Like, like_class)
|
||||
Object.const_set(:Preview, preview_class)
|
||||
end
|
||||
|
||||
def teardown
|
||||
FileUtils.remove_entry(@dir)
|
||||
%i[Post Photo Like Preview].each { |name| Object.send(:remove_const, name) if Object.const_defined?(name) }
|
||||
end
|
||||
|
||||
def test_polymorphic_belongs_to_resolves_the_right_class
|
||||
post = Post.create!(title: 'hello')
|
||||
photo = Photo.create!(caption: 'sunset')
|
||||
like_on_post = Like.create!(likeable_id: post.id, likeable_type: 'Post', user_id: 1)
|
||||
like_on_photo = Like.create!(likeable_id: photo.id, likeable_type: 'Photo', user_id: 1)
|
||||
|
||||
assert_instance_of Post, like_on_post.likeable
|
||||
assert_equal post.id, like_on_post.likeable.id
|
||||
assert_instance_of Photo, like_on_photo.likeable
|
||||
assert_equal photo.id, like_on_photo.likeable.id
|
||||
end
|
||||
|
||||
def test_polymorphic_belongs_to_setter_stamps_both_columns
|
||||
post = Post.create!(title: 'hello')
|
||||
like = Like.new(user_id: 1)
|
||||
like.likeable = post
|
||||
like.save!
|
||||
|
||||
assert_equal post.id, like.likeable_id
|
||||
assert_equal 'Post', like.likeable_type
|
||||
end
|
||||
|
||||
def test_polymorphic_belongs_to_returns_nil_without_a_target
|
||||
like = Like.create!(user_id: 1)
|
||||
assert_nil like.likeable
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_scopes_by_type_not_just_id
|
||||
post = Post.create!(title: 'hello')
|
||||
photo = Photo.create!(caption: 'sunset')
|
||||
# Deliberately share the same numeric id across two different owner
|
||||
# types - the whole point of the type column is that this must not
|
||||
# cause cross-talk between Post#likes and Photo#likes.
|
||||
Like.create!(likeable_id: post.id, likeable_type: 'Post', user_id: 1)
|
||||
Like.create!(likeable_id: post.id, likeable_type: 'Photo', user_id: 2)
|
||||
|
||||
assert_equal 1, post.likes.count
|
||||
assert_equal 1, post.likes.first.user_id
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_dependent_destroy_only_removes_own_type
|
||||
post = Post.create!(title: 'hello')
|
||||
photo = Photo.create!(caption: 'sunset')
|
||||
Like.create!(likeable_id: post.id, likeable_type: 'Post', user_id: 1)
|
||||
Like.create!(likeable_id: photo.id, likeable_type: 'Photo', user_id: 1)
|
||||
|
||||
post.destroy
|
||||
|
||||
assert_equal 1, Like.all.count
|
||||
assert_equal 'Photo', Like.all.first.likeable_type
|
||||
end
|
||||
|
||||
def test_polymorphic_has_one_dependent_nullify_clears_both_columns
|
||||
post = Post.create!(title: 'hello')
|
||||
preview = Preview.create!(previewable_id: post.id, previewable_type: 'Post', url: '/p.png')
|
||||
|
||||
post.destroy
|
||||
|
||||
reloaded = Preview.find(preview.id)
|
||||
assert_nil reloaded.previewable_id
|
||||
assert_nil reloaded.previewable_type
|
||||
end
|
||||
|
||||
def test_polymorphic_has_one_setter_stamps_both_columns
|
||||
post = Post.create!(title: 'hello')
|
||||
preview = Preview.create!(url: '/p.png')
|
||||
post.preview = preview
|
||||
|
||||
reloaded = Preview.find(preview.id)
|
||||
assert_equal post.id, reloaded.previewable_id
|
||||
assert_equal 'Post', reloaded.previewable_type
|
||||
end
|
||||
|
||||
def test_includes_polymorphic_belongs_to_batches_across_mixed_types
|
||||
post = Post.create!(title: 'hello')
|
||||
photo = Photo.create!(caption: 'sunset')
|
||||
Like.create!(likeable_id: post.id, likeable_type: 'Post', user_id: 1)
|
||||
Like.create!(likeable_id: photo.id, likeable_type: 'Photo', user_id: 2)
|
||||
Like.create!(likeable_id: post.id, likeable_type: 'Post', user_id: 3)
|
||||
|
||||
@store.reset_query_count!
|
||||
likes = Like.all.includes(:likeable).to_a
|
||||
baseline = @store.query_count
|
||||
|
||||
targets = likes.map { |like| like.likeable.class.name }.sort
|
||||
assert_equal baseline, @store.query_count,
|
||||
"expected preloaded #likeable to be free, but it issued #{@store.query_count - baseline} more queries"
|
||||
assert_equal %w[Photo Post Post], targets
|
||||
end
|
||||
|
||||
def test_includes_polymorphic_has_many_avoids_n_plus_one_queries
|
||||
3.times do |i|
|
||||
post = Post.create!(title: "post#{i}")
|
||||
2.times { |j| Like.create!(likeable_id: post.id, likeable_type: 'Post', user_id: j) }
|
||||
end
|
||||
Photo.create!(caption: 'unrelated')
|
||||
|
||||
@store.reset_query_count!
|
||||
posts = Post.all.includes(:likes).to_a
|
||||
baseline = @store.query_count
|
||||
|
||||
total = posts.sum { |post| post.likes.size }
|
||||
assert_equal baseline, @store.query_count,
|
||||
"expected preloaded #likes to be free, but it issued #{@store.query_count - baseline} more queries"
|
||||
assert_equal 6, total
|
||||
end
|
||||
end
|
||||
|
||||
# Mirrors CountingStore/CountingThroughStore from the eager-loading tests -
|
||||
# a small local double so this file has no load-order dependency on them.
|
||||
class CountingPolymorphicStore < Prism::Orm::Store
|
||||
def initialize(*)
|
||||
super
|
||||
@query_count = 0
|
||||
end
|
||||
|
||||
attr_reader :query_count
|
||||
|
||||
def reset_query_count!
|
||||
@query_count = 0
|
||||
end
|
||||
|
||||
def all(table)
|
||||
@query_count += 1
|
||||
super
|
||||
end
|
||||
|
||||
def find(table, id)
|
||||
@query_count += 1
|
||||
super
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user