File tree Expand file tree Collapse file tree 4 files changed +117
-0
lines changed
Expand file tree Collapse file tree 4 files changed +117
-0
lines changed Original file line number Diff line number Diff line change 1+ require "rails_helper"
2+
3+ RSpec . describe "acts_as_paranoid" do
4+ let ( :currently_probably_buggy_classes_ignored ) do
5+ %w[ ]
6+ end
7+ let ( :allows_multiple_deleted ) { "(deleted_at IS NULL)" }
8+
9+ it "checks that all activerecord models using acts_as_paranoid have the deleted exclusions on unique indexes" do
10+ errors = [ ]
11+ found_ignored_error_indexes = [ ]
12+ Zeitwerk ::Loader . eager_load_all
13+ expect ( ApplicationRecord . descendants . count ) . to be >= 54 # make sure we are actually testing all model classes
14+ ApplicationRecord . descendants . each do |clazz |
15+ next if clazz . abstract_class?
16+ next unless clazz . paranoid?
17+ unique_indexes = ApplicationRecord . connection_pool . with_connection do |connection |
18+ connection . indexes ( clazz . table_name ) . select ( &:unique )
19+ end
20+ unique_indexes . each do |idx |
21+ next if idx . columns == [ "external_id" ] # it is ok for external_id to be unique
22+ if currently_probably_buggy_classes_ignored . include? ( idx . name )
23+ found_ignored_error_indexes << idx . name
24+ next
25+ end
26+ unless idx . where &.include? ( allows_multiple_deleted )
27+ errors << "#{ idx . name } on #{ clazz } uses acts_as_paranoid but has a unique index without #{ allows_multiple_deleted } but it does have: #{ idx . where } "
28+ end
29+ end
30+ end
31+ expect ( errors ) . to be_empty
32+ expect ( found_ignored_error_indexes ) . to match_array ( currently_probably_buggy_classes_ignored )
33+ end
34+ end
Original file line number Diff line number Diff line change 1+ require "rails_helper"
2+
3+ RSpec . describe "soft-deleted model shared example coverage" do
4+ let ( :skip_classes ) do
5+ %w[ ]
6+ end
7+
8+ let ( :todo_currently_missing_specs ) do
9+ %w[
10+ ContactTopicAnswer
11+ CaseContact
12+ ]
13+ end
14+
15+ it "checks that all acts_as_paranoid models have specs that include the soft-deleted model shared example" do
16+ missing = [ ]
17+ Zeitwerk ::Loader . eager_load_all
18+
19+ ApplicationRecord . descendants . each do |clazz |
20+ next if clazz . abstract_class?
21+ next unless clazz . paranoid?
22+ next if skip_classes . include? ( clazz . name )
23+ next if todo_currently_missing_specs . include? ( clazz . name )
24+
25+ source_file = Object . const_source_location ( clazz . name ) &.first
26+ next unless source_file
27+
28+ spec_file = source_file
29+ . sub ( %r{/app/models/} , "/spec/models/" )
30+ . sub ( /\. rb$/ , "_spec.rb" )
31+
32+ unless File . exist? ( spec_file )
33+ missing << "#{ clazz . name } : spec file not found (expected #{ spec_file } )"
34+ next
35+ end
36+
37+ contents = File . read ( spec_file )
38+ unless contents . include? ( '"a soft-deleted model"' )
39+ missing << clazz . name . to_s
40+ end
41+ end
42+
43+ expect ( missing ) . to be_empty , "The following paranoid models are missing the shared example:\n #{ missing . join ( "\n " ) } "
44+ end
45+ end
Original file line number Diff line number Diff line change 11# frozen_string_literal: true
22
3+ def described_class_factory
4+ described_class . name . gsub ( "::" , "" ) . underscore
5+ end
6+
37RSpec . configure do |config |
48 config . include FactoryBot ::Syntax ::Methods
59
Original file line number Diff line number Diff line change 1+ RSpec . shared_examples_for "a soft-deleted model" do |skip_ignores_deleted_records_in_validations_check : false , skip_deleted_at_index_check : false |
2+ # for usage with acts_as_paranoid models
3+
4+ it { is_expected . to have_db_column ( :deleted_at ) }
5+
6+ unless skip_deleted_at_index_check
7+ it { is_expected . to have_db_index ( :deleted_at ) }
8+ end
9+
10+ it "cannot be found, by default" do
11+ model ||= create ( described_class_factory )
12+ model . destroy!
13+ expect ( described_class . find_by ( id : model . id ) ) . to be_nil
14+ end
15+
16+ it "returned when unscoped" do
17+ model ||= create ( described_class_factory )
18+ model . destroy!
19+ expect ( described_class . unscoped . find_by ( id : model . id ) ) . to be_present
20+ end
21+
22+ context "uniqueness" do
23+ it "ignores deleted records in validations" do
24+ unless skip_ignores_deleted_records_in_validations_check
25+ obj = create ( described_class_factory )
26+ new_obj = obj . dup
27+ expect ( new_obj ) . not_to be_valid
28+ obj . destroy!
29+ expect ( new_obj ) . to be_valid
30+ expect { new_obj . save! } . not_to raise_exception
31+ end
32+ end
33+ end
34+ end
You can’t perform that action at this time.
0 commit comments