From 36c3bd517c6c2dd02201ee98de7257562f0644f9 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sat, 12 Sep 2026 21:26:58 +0200 Subject: [PATCH] test: cover OrmParanoiaTest and teardown --- test/test_orm_paranoia.rb | 158 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 test/test_orm_paranoia.rb diff --git a/test/test_orm_paranoia.rb b/test/test_orm_paranoia.rb new file mode 100644 index 0000000..75c227d --- /dev/null +++ b/test/test_orm_paranoia.rb @@ -0,0 +1,158 @@ +require 'minitest/autorun' +require 'prism' +require 'tmpdir' +require 'fileutils' + +class OrmParanoiaTest < Minitest::Test + def setup + @dir = Dir.mktmpdir('prism-paranoia-store') + @store = Prism::Orm::Store.new(File.join(@dir, 'test.pstore')) + @store.create_table(:widgets, %w[id name deleted_at]) + @store.create_table(:plain_things, %w[id name]) + @store.create_table(:locked_widgets, %w[id name deleted_at lock_version]) + @store.create_table(:authors, %w[id name]) + @store.create_table(:posts, %w[id title author_id deleted_at]) + @store.create_table(:blogs, %w[id name deleted_at entries_count]) + @store.create_table(:entries, %w[id title blog_id deleted_at]) + + widget_class = Class.new(Prism::Orm::Model) do + table_name :widgets + acts_as_paranoid + end + plain_class = Class.new(Prism::Orm::Model) { table_name :plain_things } + locked_widget_class = Class.new(Prism::Orm::Model) do + table_name :locked_widgets + acts_as_paranoid + end + author_class = Class.new(Prism::Orm::Model) do + table_name :authors + has_many :posts, class_name: 'Post', foreign_key: 'author_id', dependent: :destroy + end + post_class = Class.new(Prism::Orm::Model) do + table_name :posts + acts_as_paranoid + belongs_to :author, class_name: 'Author', foreign_key: 'author_id' + end + + blog_class = Class.new(Prism::Orm::Model) do + table_name :blogs + acts_as_paranoid + has_many :entries, class_name: 'Entry', foreign_key: 'blog_id', dependent: :destroy + end + entry_class = Class.new(Prism::Orm::Model) do + table_name :entries + acts_as_paranoid + belongs_to :blog, class_name: 'Blog', foreign_key: 'blog_id', counter_cache: true + end + + [widget_class, plain_class, locked_widget_class, author_class, post_class, blog_class, entry_class].each do |klass| + klass.store = @store + end + Object.const_set(:Widget, widget_class) + Object.const_set(:PlainThing, plain_class) + Object.const_set(:LockedWidget, locked_widget_class) + Object.const_set(:Author, author_class) + Object.const_set(:Post, post_class) + Object.const_set(:Blog, blog_class) + Object.const_set(:Entry, entry_class) + end + + def teardown + FileUtils.remove_entry(@dir) + %i[Widget PlainThing LockedWidget Author Post Blog Entry].each do |name| + Object.send(:remove_const, name) if Object.const_defined?(name) + end + end + + def test_plain_model_is_not_paranoid_and_destroy_removes_the_row_for_real + refute PlainThing.paranoid? + thing = PlainThing.create!(name: 'gizmo') + thing.destroy + assert_raises(Prism::Orm::RecordNotFound) { PlainThing.find(thing.id) } + end + + def test_paranoid_destroy_stamps_the_column_instead_of_deleting + widget = Widget.create!(name: 'gizmo') + widget.destroy + + assert widget.destroyed? + row = @store.find('widgets', widget.id) + refute_nil row, 'the row must still physically exist' + refute_nil row['deleted_at'] + end + + def test_paranoid_destroy_hides_the_record_from_find_all_and_count + widget = Widget.create!(name: 'gizmo') + widget.destroy + + assert_raises(Prism::Orm::RecordNotFound) { Widget.find(widget.id) } + assert_empty Widget.all.to_a + assert_equal 0, Widget.count + end + + def test_paranoid_destroy_still_runs_before_and_after_destroy_callbacks + log = [] + widget_class = Class.new(Prism::Orm::Model) do + table_name :widgets + acts_as_paranoid + before_destroy { log << :before_destroy } + after_destroy { log << :after_destroy } + end + widget_class.store = @store + widget = widget_class.create!(name: 'gizmo') + + widget.destroy + + assert_equal %i[before_destroy after_destroy], log + end + + def test_before_destroy_abort_leaves_the_record_undeleted + widget_class = Class.new(Prism::Orm::Model) do + table_name :widgets + acts_as_paranoid + before_destroy { throw :abort } + end + widget_class.store = @store + widget = widget_class.create!(name: 'gizmo') + + widget.destroy + + refute widget.destroyed? + assert widget_class.find(widget.id) + end + + def test_deleted_predicate + widget = Widget.create!(name: 'gizmo') + refute widget.deleted? + widget.destroy + assert widget.deleted? + end + + def test_soft_deleting_predicate_is_true_inside_before_destroy_for_a_plain_soft_delete + seen = nil + widget_class = Class.new(Prism::Orm::Model) do + table_name :widgets + acts_as_paranoid + before_destroy { seen = soft_deleting? } + end + widget_class.store = @store + widget = widget_class.create!(name: 'gizmo') + + widget.destroy + + assert_equal true, seen + assert widget.soft_deleting?, 'still true after #destroy returns, not just mid-callback' + end + + def test_soft_deleting_predicate_is_false_for_really_destroy_bang + seen = nil + widget_class = Class.new(Prism::Orm::Model) do + table_name :widgets + acts_as_paranoid + before_destroy { seen = soft_deleting? } + end + widget_class.store = @store + widget = widget_class.create!(name: 'gizmo') + + widget.really_destroy! +