active_record_doctor 1.9.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +83 -19
- data/lib/active_record_doctor/config/default.rb +17 -0
- data/lib/active_record_doctor/detectors/base.rb +216 -56
- data/lib/active_record_doctor/detectors/extraneous_indexes.rb +38 -56
- data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +2 -6
- data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +88 -15
- data/lib/active_record_doctor/detectors/incorrect_length_validation.rb +60 -0
- data/lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb +16 -9
- data/lib/active_record_doctor/detectors/missing_foreign_keys.rb +2 -4
- data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +14 -11
- data/lib/active_record_doctor/detectors/missing_presence_validation.rb +16 -10
- data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +61 -17
- data/lib/active_record_doctor/detectors/short_primary_key_type.rb +6 -2
- data/lib/active_record_doctor/detectors/undefined_table_references.rb +2 -4
- data/lib/active_record_doctor/detectors/unindexed_deleted_at.rb +6 -15
- data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +2 -4
- data/lib/active_record_doctor/logger/dummy.rb +11 -0
- data/lib/active_record_doctor/logger/hierarchical.rb +22 -0
- data/lib/active_record_doctor/logger.rb +6 -0
- data/lib/active_record_doctor/rake/task.rb +10 -1
- data/lib/active_record_doctor/runner.rb +8 -3
- data/lib/active_record_doctor/version.rb +1 -1
- data/lib/active_record_doctor.rb +4 -0
- data/lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb +5 -5
- data/test/active_record_doctor/detectors/disable_test.rb +30 -0
- data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +34 -0
- data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +7 -7
- data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +220 -43
- data/test/active_record_doctor/detectors/incorrect_length_validation_test.rb +107 -0
- data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +35 -1
- data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +78 -21
- data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +89 -25
- data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +179 -15
- data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +27 -19
- data/test/active_record_doctor/detectors/undefined_table_references_test.rb +11 -13
- data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +9 -3
- data/test/active_record_doctor/runner_test.rb +18 -19
- data/test/setup.rb +15 -7
- metadata +25 -5
- data/test/model_factory.rb +0 -128
@@ -5,7 +5,7 @@ class ActiveRecordDoctor::Detectors::MissingUniqueIndexesTest < Minitest::Test
|
|
5
5
|
create_table(:users) do |t|
|
6
6
|
t.string :email
|
7
7
|
t.index :email
|
8
|
-
end.
|
8
|
+
end.define_model do
|
9
9
|
validates :email, uniqueness: true
|
10
10
|
end
|
11
11
|
|
@@ -14,24 +14,70 @@ class ActiveRecordDoctor::Detectors::MissingUniqueIndexesTest < Minitest::Test
|
|
14
14
|
OUTPUT
|
15
15
|
end
|
16
16
|
|
17
|
+
def test_missing_unique_index_on_functional_index
|
18
|
+
skip if !(ActiveRecord::VERSION::STRING >= "5.0" && postgresql?)
|
19
|
+
|
20
|
+
create_table(:users) do |t|
|
21
|
+
t.string :email
|
22
|
+
t.index "lower(email)"
|
23
|
+
end.define_model do
|
24
|
+
validates :email, uniqueness: true
|
25
|
+
end
|
26
|
+
|
27
|
+
# Running the detector should NOT raise an error when a functional index
|
28
|
+
# is present. No need to assert anything -- the test is successful if no
|
29
|
+
# exception was raised.
|
30
|
+
run_detector
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_validates_multiple_attributes
|
34
|
+
create_table(:users) do |t|
|
35
|
+
t.string :email
|
36
|
+
t.string :ref_token
|
37
|
+
end.define_model do
|
38
|
+
validates :email, :ref_token, uniqueness: true
|
39
|
+
end
|
40
|
+
|
41
|
+
assert_problems(<<~OUTPUT)
|
42
|
+
add a unique index on users(email) - validating uniqueness in the model without an index can lead to duplicates
|
43
|
+
add a unique index on users(ref_token) - validating uniqueness in the model without an index can lead to duplicates
|
44
|
+
OUTPUT
|
45
|
+
end
|
46
|
+
|
17
47
|
def test_present_unique_index
|
18
48
|
create_table(:users) do |t|
|
19
49
|
t.string :email
|
20
50
|
t.index :email, unique: true
|
21
|
-
end.
|
51
|
+
end.define_model do
|
22
52
|
validates :email, uniqueness: true
|
23
53
|
end
|
24
54
|
|
25
55
|
refute_problems
|
26
56
|
end
|
27
57
|
|
28
|
-
def
|
58
|
+
def test_present_partial_unique_index
|
59
|
+
skip("MySQL doesn't support partial indexes") if mysql?
|
60
|
+
|
61
|
+
create_table(:users) do |t|
|
62
|
+
t.string :email
|
63
|
+
t.boolean :active
|
64
|
+
t.index :email, unique: true, where: "active"
|
65
|
+
end.define_model do
|
66
|
+
validates :email, uniqueness: true
|
67
|
+
end
|
68
|
+
|
69
|
+
assert_problems(<<~OUTPUT)
|
70
|
+
add a unique index on users(email) - validating uniqueness in the model without an index can lead to duplicates
|
71
|
+
OUTPUT
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_unique_index_with_extra_columns_with_scope
|
29
75
|
create_table(:users) do |t|
|
30
76
|
t.string :email
|
31
77
|
t.integer :company_id
|
32
78
|
t.integer :department_id
|
33
79
|
t.index [:company_id, :department_id, :email]
|
34
|
-
end.
|
80
|
+
end.define_model do
|
35
81
|
validates :email, uniqueness: { scope: [:company_id, :department_id] }
|
36
82
|
end
|
37
83
|
|
@@ -40,26 +86,93 @@ class ActiveRecordDoctor::Detectors::MissingUniqueIndexesTest < Minitest::Test
|
|
40
86
|
OUTPUT
|
41
87
|
end
|
42
88
|
|
43
|
-
def
|
89
|
+
def test_unique_index_with_exact_columns_with_scope
|
44
90
|
create_table(:users) do |t|
|
45
91
|
t.string :email
|
46
92
|
t.integer :company_id
|
47
93
|
t.integer :department_id
|
48
94
|
t.index [:company_id, :department_id, :email], unique: true
|
49
|
-
end.
|
95
|
+
end.define_model do
|
50
96
|
validates :email, uniqueness: { scope: [:company_id, :department_id] }
|
51
97
|
end
|
52
98
|
|
53
99
|
refute_problems
|
54
100
|
end
|
55
101
|
|
102
|
+
def test_unique_index_with_fewer_columns_with_scope
|
103
|
+
create_table(:users) do |t|
|
104
|
+
t.string :email
|
105
|
+
t.integer :company_id
|
106
|
+
t.integer :department_id
|
107
|
+
t.index [:company_id, :department_id], unique: true
|
108
|
+
end.define_model do
|
109
|
+
validates :email, uniqueness: { scope: [:company_id, :department_id] }
|
110
|
+
end
|
111
|
+
|
112
|
+
refute_problems
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_missing_unique_index_with_association_attribute
|
116
|
+
create_table(:users) do |t|
|
117
|
+
t.integer :account_id
|
118
|
+
end.define_model do
|
119
|
+
belongs_to :account
|
120
|
+
validates :account, uniqueness: true
|
121
|
+
end
|
122
|
+
|
123
|
+
assert_problems(<<~OUTPUT)
|
124
|
+
add a unique index on users(account_id) - validating uniqueness in the model without an index can lead to duplicates
|
125
|
+
OUTPUT
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_present_unique_index_with_association_attribute
|
129
|
+
create_table(:users) do |t|
|
130
|
+
t.integer :account_id
|
131
|
+
t.index :account_id, unique: true
|
132
|
+
end.define_model do
|
133
|
+
belongs_to :account
|
134
|
+
validates :account, uniqueness: true
|
135
|
+
end
|
136
|
+
|
137
|
+
refute_problems
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_missing_unique_index_with_association_scope
|
141
|
+
create_table(:comments) do |t|
|
142
|
+
t.string :title
|
143
|
+
t.integer :commentable_id
|
144
|
+
t.string :commentable_type
|
145
|
+
end.define_model do
|
146
|
+
belongs_to :commentable, polymorphic: true
|
147
|
+
validates :title, uniqueness: { scope: :commentable }
|
148
|
+
end
|
149
|
+
|
150
|
+
assert_problems(<<~OUTPUT)
|
151
|
+
add a unique index on comments(commentable_type, commentable_id, title) - validating uniqueness in the model without an index can lead to duplicates
|
152
|
+
OUTPUT
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_present_unique_index_with_association_scope
|
156
|
+
create_table(:comments) do |t|
|
157
|
+
t.string :title
|
158
|
+
t.integer :commentable_id
|
159
|
+
t.string :commentable_type
|
160
|
+
t.index [:commentable_id, :commentable_type, :title], unique: true
|
161
|
+
end.define_model do
|
162
|
+
belongs_to :commentable, polymorphic: true
|
163
|
+
validates :title, uniqueness: { scope: :commentable }
|
164
|
+
end
|
165
|
+
|
166
|
+
refute_problems
|
167
|
+
end
|
168
|
+
|
56
169
|
def test_column_order_is_ignored
|
57
170
|
create_table(:users) do |t|
|
58
171
|
t.string :email
|
59
172
|
t.integer :organization_id
|
60
173
|
|
61
174
|
t.index [:email, :organization_id], unique: true
|
62
|
-
end.
|
175
|
+
end.define_model do
|
63
176
|
validates :email, uniqueness: { scope: :organization_id }
|
64
177
|
end
|
65
178
|
|
@@ -86,24 +199,75 @@ class ActiveRecordDoctor::Detectors::MissingUniqueIndexesTest < Minitest::Test
|
|
86
199
|
create_table(:users) do |t|
|
87
200
|
t.string :email
|
88
201
|
t.index :email
|
89
|
-
end.
|
202
|
+
end.define_model do
|
90
203
|
validates_with DummyValidator
|
91
204
|
end
|
92
205
|
|
93
206
|
refute_problems
|
94
207
|
end
|
95
208
|
|
209
|
+
def test_has_one_without_index
|
210
|
+
create_table(:users)
|
211
|
+
.define_model do
|
212
|
+
has_one :account, class_name: "TransientRecord::Models::Account"
|
213
|
+
has_one :account_history, through: :account, class_name: "TransientRecord::Models::Account"
|
214
|
+
end
|
215
|
+
|
216
|
+
create_table(:accounts) do |t|
|
217
|
+
t.integer :user_id
|
218
|
+
end.define_model do
|
219
|
+
has_one :account_history, class_name: "TransientRecord::Models::AccountHistory"
|
220
|
+
end
|
221
|
+
|
222
|
+
create_table(:account_histories) do |t|
|
223
|
+
t.integer :account_id
|
224
|
+
end.define_model do
|
225
|
+
belongs_to :account, class_name: "TransientRecord::Models::Account"
|
226
|
+
end
|
227
|
+
|
228
|
+
assert_problems(<<~OUTPUT)
|
229
|
+
add a unique index on accounts(user_id) - using `has_one` in the TransientRecord::Models::User model without an index can lead to duplicates
|
230
|
+
add a unique index on account_histories(account_id) - using `has_one` in the TransientRecord::Models::Account model without an index can lead to duplicates
|
231
|
+
OUTPUT
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_has_one_with_scope_and_without_index
|
235
|
+
create_table(:users)
|
236
|
+
.define_model do
|
237
|
+
has_one :last_comment, -> { order(created_at: :desc) }, class_name: "TransientRecord::Models::Comment"
|
238
|
+
end
|
239
|
+
|
240
|
+
create_table(:comments) do |t|
|
241
|
+
t.integer :user_id
|
242
|
+
end.define_model
|
243
|
+
|
244
|
+
refute_problems
|
245
|
+
end
|
246
|
+
|
247
|
+
def test_has_one_with_index
|
248
|
+
create_table(:users)
|
249
|
+
.define_model do
|
250
|
+
has_one :account, class_name: "TransientRecord::Models::Account"
|
251
|
+
end
|
252
|
+
|
253
|
+
create_table(:accounts) do |t|
|
254
|
+
t.integer :user_id, index: { unique: true }
|
255
|
+
end.define_model
|
256
|
+
|
257
|
+
refute_problems
|
258
|
+
end
|
259
|
+
|
96
260
|
def test_config_ignore_models
|
97
261
|
create_table(:users) do |t|
|
98
262
|
t.string :email
|
99
|
-
end.
|
263
|
+
end.define_model do
|
100
264
|
validates :email, uniqueness: true
|
101
265
|
end
|
102
266
|
|
103
267
|
config_file(<<-CONFIG)
|
104
268
|
ActiveRecordDoctor.configure do |config|
|
105
269
|
config.detector :missing_unique_indexes,
|
106
|
-
ignore_models: ["
|
270
|
+
ignore_models: ["TransientRecord::Models::User"]
|
107
271
|
end
|
108
272
|
CONFIG
|
109
273
|
|
@@ -113,13 +277,13 @@ class ActiveRecordDoctor::Detectors::MissingUniqueIndexesTest < Minitest::Test
|
|
113
277
|
def test_global_ignore_models
|
114
278
|
create_table(:users) do |t|
|
115
279
|
t.string :email
|
116
|
-
end.
|
280
|
+
end.define_model do
|
117
281
|
validates :email, uniqueness: true
|
118
282
|
end
|
119
283
|
|
120
284
|
config_file(<<-CONFIG)
|
121
285
|
ActiveRecordDoctor.configure do |config|
|
122
|
-
config.global :ignore_models, ["
|
286
|
+
config.global :ignore_models, ["TransientRecord::Models::User"]
|
123
287
|
end
|
124
288
|
CONFIG
|
125
289
|
|
@@ -130,14 +294,14 @@ class ActiveRecordDoctor::Detectors::MissingUniqueIndexesTest < Minitest::Test
|
|
130
294
|
create_table(:users) do |t|
|
131
295
|
t.string :email
|
132
296
|
t.integer :role
|
133
|
-
end.
|
297
|
+
end.define_model do
|
134
298
|
validates :email, :role, uniqueness: { scope: :organization_id }
|
135
299
|
end
|
136
300
|
|
137
301
|
config_file(<<-CONFIG)
|
138
302
|
ActiveRecordDoctor.configure do |config|
|
139
303
|
config.detector :missing_unique_indexes,
|
140
|
-
ignore_columns: ["
|
304
|
+
ignore_columns: ["TransientRecord::Models::User(organization_id, email)", "TransientRecord::Models::User(organization_id, role)"]
|
141
305
|
end
|
142
306
|
CONFIG
|
143
307
|
|
@@ -154,7 +318,7 @@ class ActiveRecordDoctor::Detectors::MissingUniqueIndexesTest < Minitest::Test
|
|
154
318
|
def assert_skipped(options)
|
155
319
|
create_table(:users) do |t|
|
156
320
|
t.string :email
|
157
|
-
end.
|
321
|
+
end.define_model do
|
158
322
|
validates :email, uniqueness: options
|
159
323
|
end
|
160
324
|
|
@@ -1,32 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class ActiveRecordDoctor::Detectors::ShortPrimaryKeyTypeTest < Minitest::Test
|
4
|
+
def setup
|
5
|
+
@connection = ActiveRecord::Base.connection
|
6
|
+
@connection.enable_extension("uuid-ossp") if postgresql?
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
@connection.disable_extension("uuid-ossp") if postgresql?
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
4
15
|
def test_short_integer_primary_key_is_reported
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
OUTPUT
|
11
|
-
elsif postgresql?
|
12
|
-
create_table(:companies, id: :integer)
|
13
|
-
|
14
|
-
assert_problems(<<~OUTPUT)
|
15
|
-
change the type of companies.id to bigint
|
16
|
-
OUTPUT
|
16
|
+
create_table(:companies, id: :int)
|
17
|
+
|
18
|
+
# In Rails 4.2 and MySQL primary key is not created due to a bug
|
19
|
+
if mysql? && ActiveRecord::VERSION::STRING < "5.0"
|
20
|
+
@connection.execute("ALTER TABLE companies ADD PRIMARY KEY(id)")
|
17
21
|
end
|
22
|
+
|
23
|
+
assert_problems(<<~OUTPUT)
|
24
|
+
change the type of companies.id to bigint
|
25
|
+
OUTPUT
|
18
26
|
end
|
19
27
|
|
20
28
|
def test_long_integer_primary_key_is_not_reported
|
21
|
-
|
22
|
-
|
29
|
+
create_table(:companies, id: :bigint)
|
30
|
+
refute_problems
|
31
|
+
end
|
23
32
|
|
24
|
-
|
25
|
-
|
26
|
-
create_table(:companies, id: :bigserial)
|
33
|
+
def test_uuid_primary_key_is_not_reported
|
34
|
+
skip unless postgresql?
|
27
35
|
|
28
|
-
|
29
|
-
|
36
|
+
create_table(:companies, id: :uuid)
|
37
|
+
refute_problems
|
30
38
|
end
|
31
39
|
|
32
40
|
def test_no_primary_key_is_not_reported
|
@@ -3,17 +3,17 @@
|
|
3
3
|
class ActiveRecordDoctor::Detectors::UndefinedTableReferencesTest < Minitest::Test
|
4
4
|
def test_model_backed_by_table
|
5
5
|
create_table(:users) do
|
6
|
-
end.
|
6
|
+
end.define_model do
|
7
7
|
end
|
8
8
|
|
9
9
|
refute_problems
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_model_backed_by_non_existent_table
|
13
|
-
|
13
|
+
define_model(:User)
|
14
14
|
|
15
15
|
assert_problems(<<~OUTPUT)
|
16
|
-
|
16
|
+
TransientRecord::Models::User references a non-existent table or view named users
|
17
17
|
OUTPUT
|
18
18
|
end
|
19
19
|
|
@@ -21,22 +21,20 @@ class ActiveRecordDoctor::Detectors::UndefinedTableReferencesTest < Minitest::Te
|
|
21
21
|
# We replace the underlying table with a view. The view doesn't have to be
|
22
22
|
# backed by an actual table - it can simply return a predefined tuple.
|
23
23
|
ActiveRecord::Base.connection.execute("CREATE VIEW users AS SELECT 1")
|
24
|
-
|
24
|
+
define_model(:User)
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
ActiveRecord::Base.connection.execute("DROP VIEW users")
|
30
|
-
end
|
26
|
+
refute_problems
|
27
|
+
ensure
|
28
|
+
ActiveRecord::Base.connection.execute("DROP VIEW users")
|
31
29
|
end
|
32
30
|
|
33
31
|
def test_config_ignore_tables
|
34
|
-
|
32
|
+
define_model(:User)
|
35
33
|
|
36
34
|
config_file(<<-CONFIG)
|
37
35
|
ActiveRecordDoctor.configure do |config|
|
38
36
|
config.detector :undefined_table_references,
|
39
|
-
ignore_models: ["
|
37
|
+
ignore_models: ["TransientRecord::Models::User"]
|
40
38
|
end
|
41
39
|
CONFIG
|
42
40
|
|
@@ -44,11 +42,11 @@ class ActiveRecordDoctor::Detectors::UndefinedTableReferencesTest < Minitest::Te
|
|
44
42
|
end
|
45
43
|
|
46
44
|
def test_global_ignore_tables
|
47
|
-
|
45
|
+
define_model(:User)
|
48
46
|
|
49
47
|
config_file(<<-CONFIG)
|
50
48
|
ActiveRecordDoctor.configure do |config|
|
51
|
-
config.global :ignore_models, ["
|
49
|
+
config.global :ignore_models, ["TransientRecord::Models::User"]
|
52
50
|
end
|
53
51
|
CONFIG
|
54
52
|
|
@@ -11,6 +11,9 @@ class ActiveRecordDoctor::Detectors::UnindexedDeletedAtTest < Minitest::Test
|
|
11
11
|
t.index [:first_name, :last_name],
|
12
12
|
name: "index_profiles_on_first_name_and_last_name",
|
13
13
|
where: "deleted_at IS NULL"
|
14
|
+
t.index [:last_name],
|
15
|
+
name: "index_deleted_profiles_on_last_name",
|
16
|
+
where: "deleted_at IS NOT NULL"
|
14
17
|
end
|
15
18
|
|
16
19
|
refute_problems
|
@@ -28,7 +31,7 @@ class ActiveRecordDoctor::Detectors::UnindexedDeletedAtTest < Minitest::Test
|
|
28
31
|
end
|
29
32
|
|
30
33
|
assert_problems(<<~OUTPUT)
|
31
|
-
consider adding `WHERE deleted_at IS NULL` to index_profiles_on_first_name_and_last_name - a partial index can speed lookups of soft-deletable models
|
34
|
+
consider adding `WHERE deleted_at IS NULL` or `WHERE deleted_at IS NOT NULL` to index_profiles_on_first_name_and_last_name - a partial index can speed lookups of soft-deletable models
|
32
35
|
OUTPUT
|
33
36
|
end
|
34
37
|
|
@@ -42,6 +45,9 @@ class ActiveRecordDoctor::Detectors::UnindexedDeletedAtTest < Minitest::Test
|
|
42
45
|
t.index [:first_name, :last_name],
|
43
46
|
name: "index_profiles_on_first_name_and_last_name",
|
44
47
|
where: "discarded_at IS NULL"
|
48
|
+
t.index [:last_name],
|
49
|
+
name: "index_discarded_profiles_on_last_name",
|
50
|
+
where: "discarded_at IS NOT NULL"
|
45
51
|
end
|
46
52
|
|
47
53
|
refute_problems
|
@@ -59,7 +65,7 @@ class ActiveRecordDoctor::Detectors::UnindexedDeletedAtTest < Minitest::Test
|
|
59
65
|
end
|
60
66
|
|
61
67
|
assert_problems(<<~OUTPUT)
|
62
|
-
consider adding `WHERE discarded_at IS NULL` to index_profiles_on_first_name_and_last_name - a partial index can speed lookups of soft-deletable models
|
68
|
+
consider adding `WHERE discarded_at IS NULL` or `WHERE discarded_at IS NOT NULL` to index_profiles_on_first_name_and_last_name - a partial index can speed lookups of soft-deletable models
|
63
69
|
OUTPUT
|
64
70
|
end
|
65
71
|
|
@@ -165,7 +171,7 @@ class ActiveRecordDoctor::Detectors::UnindexedDeletedAtTest < Minitest::Test
|
|
165
171
|
CONFIG
|
166
172
|
|
167
173
|
assert_problems(<<~OUTPUT)
|
168
|
-
consider adding `WHERE obliverated_at IS NULL` to index_profiles_on_first_name_and_last_name - a partial index can speed lookups of soft-deletable models
|
174
|
+
consider adding `WHERE obliverated_at IS NULL` or `WHERE obliverated_at IS NOT NULL` to index_profiles_on_first_name_and_last_name - a partial index can speed lookups of soft-deletable models
|
169
175
|
OUTPUT
|
170
176
|
end
|
171
177
|
end
|
@@ -1,42 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class ActiveRecordDoctor::RunnerTest < Minitest::Test
|
4
|
-
def
|
5
|
-
io = StringIO.new
|
6
|
-
runner = ActiveRecordDoctor::Runner.new(
|
4
|
+
def setup
|
5
|
+
@io = StringIO.new
|
6
|
+
@runner = ActiveRecordDoctor::Runner.new(
|
7
|
+
config: load_config,
|
8
|
+
logger: ActiveRecordDoctor::Logger::Dummy.new,
|
9
|
+
io: @io
|
10
|
+
)
|
11
|
+
end
|
7
12
|
|
13
|
+
def test_run_one_raises_on_unknown_detectors
|
8
14
|
assert_raises(KeyError) do
|
9
|
-
runner.run_one(:performance_issues)
|
15
|
+
@runner.run_one(:performance_issues)
|
10
16
|
end
|
11
17
|
end
|
12
18
|
|
13
19
|
def test_run_all_returns_true_when_no_errors
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
assert(runner.run_all)
|
18
|
-
assert(io.string.blank?)
|
20
|
+
assert(@runner.run_all)
|
21
|
+
assert(@io.string.blank?)
|
19
22
|
end
|
20
23
|
|
21
24
|
def test_run_all_returns_false_when_errors
|
22
25
|
# Create a model without its underlying table to trigger an error.
|
23
|
-
|
24
|
-
|
25
|
-
io = StringIO.new
|
26
|
-
runner = ActiveRecordDoctor::Runner.new(load_config, io)
|
26
|
+
define_model(:User)
|
27
27
|
|
28
|
-
refute(runner.run_all)
|
29
|
-
refute(io.string.blank?)
|
28
|
+
refute(@runner.run_all)
|
29
|
+
refute(@io.string.blank?)
|
30
30
|
end
|
31
31
|
|
32
32
|
def test_help_prints_help
|
33
33
|
ActiveRecordDoctor.detectors.each do |name, _|
|
34
|
-
io
|
35
|
-
runner = ActiveRecordDoctor::Runner.new(load_config, io)
|
34
|
+
@io.truncate(0)
|
36
35
|
|
37
|
-
runner.help(name)
|
36
|
+
@runner.help(name)
|
38
37
|
|
39
|
-
refute(io.string.blank?, "expected help for #{name}")
|
38
|
+
refute(@io.string.blank?, "expected help for #{name}")
|
40
39
|
end
|
41
40
|
end
|
42
41
|
end
|
data/test/setup.rb
CHANGED
@@ -33,7 +33,7 @@ require "active_record_doctor"
|
|
33
33
|
require "minitest"
|
34
34
|
require "minitest/autorun"
|
35
35
|
require "minitest/fork_executor"
|
36
|
-
|
36
|
+
require "transient_record"
|
37
37
|
|
38
38
|
# Filter out Minitest backtrace while allowing backtrace from other libraries
|
39
39
|
# to be shown.
|
@@ -44,11 +44,11 @@ Minitest.parallel_executor = Minitest::ForkExecutor.new
|
|
44
44
|
|
45
45
|
# Prepare the test class.
|
46
46
|
class Minitest::Test
|
47
|
-
include
|
47
|
+
include TransientRecord
|
48
48
|
|
49
49
|
def setup
|
50
50
|
# Delete remnants (models and tables) of previous test case runs.
|
51
|
-
|
51
|
+
TransientRecord.cleanup
|
52
52
|
end
|
53
53
|
|
54
54
|
def teardown
|
@@ -61,7 +61,7 @@ class Minitest::Test
|
|
61
61
|
|
62
62
|
# Ensure all remnants of previous test runs, most likely in form of tables,
|
63
63
|
# are removed.
|
64
|
-
|
64
|
+
TransientRecord.cleanup
|
65
65
|
end
|
66
66
|
|
67
67
|
private
|
@@ -93,7 +93,11 @@ class Minitest::Test
|
|
93
93
|
|
94
94
|
def run_detector
|
95
95
|
io = StringIO.new
|
96
|
-
runner = ActiveRecordDoctor::Runner.new(
|
96
|
+
runner = ActiveRecordDoctor::Runner.new(
|
97
|
+
config: load_config,
|
98
|
+
logger: ActiveRecordDoctor::Logger::Dummy.new,
|
99
|
+
io: io
|
100
|
+
)
|
97
101
|
success = runner.run_one(detector_name)
|
98
102
|
[success, io.string]
|
99
103
|
end
|
@@ -104,13 +108,17 @@ class Minitest::Test
|
|
104
108
|
|
105
109
|
def assert_problems(expected_output)
|
106
110
|
success, output = run_detector
|
107
|
-
assert_equal(expected_output, output)
|
111
|
+
assert_equal(sort_lines(expected_output), sort_lines(output))
|
108
112
|
refute(success, "Expected the detector to return failure.")
|
109
113
|
end
|
110
114
|
|
111
115
|
def refute_problems(expected_output = "")
|
112
116
|
success, output = run_detector
|
113
|
-
assert_equal(expected_output, output)
|
117
|
+
assert_equal(sort_lines(expected_output), sort_lines(output))
|
114
118
|
assert(success, "Expected the detector to return success.")
|
115
119
|
end
|
120
|
+
|
121
|
+
def sort_lines(string)
|
122
|
+
string.split("\n").sort
|
123
|
+
end
|
116
124
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_doctor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Navis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 12.3.3
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: transient_record
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.0.0.rc1
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.0.0.rc1
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: rubocop
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,6 +140,7 @@ files:
|
|
126
140
|
- lib/active_record_doctor/detectors/extraneous_indexes.rb
|
127
141
|
- lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb
|
128
142
|
- lib/active_record_doctor/detectors/incorrect_dependent_option.rb
|
143
|
+
- lib/active_record_doctor/detectors/incorrect_length_validation.rb
|
129
144
|
- lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb
|
130
145
|
- lib/active_record_doctor/detectors/missing_foreign_keys.rb
|
131
146
|
- lib/active_record_doctor/detectors/missing_non_null_constraint.rb
|
@@ -137,6 +152,9 @@ files:
|
|
137
152
|
- lib/active_record_doctor/detectors/unindexed_foreign_keys.rb
|
138
153
|
- lib/active_record_doctor/errors.rb
|
139
154
|
- lib/active_record_doctor/help.rb
|
155
|
+
- lib/active_record_doctor/logger.rb
|
156
|
+
- lib/active_record_doctor/logger/dummy.rb
|
157
|
+
- lib/active_record_doctor/logger/hierarchical.rb
|
140
158
|
- lib/active_record_doctor/printers.rb
|
141
159
|
- lib/active_record_doctor/railtie.rb
|
142
160
|
- lib/active_record_doctor/rake/task.rb
|
@@ -147,9 +165,11 @@ files:
|
|
147
165
|
- lib/tasks/active_record_doctor.rake
|
148
166
|
- test/active_record_doctor/config/loader_test.rb
|
149
167
|
- test/active_record_doctor/config_test.rb
|
168
|
+
- test/active_record_doctor/detectors/disable_test.rb
|
150
169
|
- test/active_record_doctor/detectors/extraneous_indexes_test.rb
|
151
170
|
- test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb
|
152
171
|
- test/active_record_doctor/detectors/incorrect_dependent_option_test.rb
|
172
|
+
- test/active_record_doctor/detectors/incorrect_length_validation_test.rb
|
153
173
|
- test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb
|
154
174
|
- test/active_record_doctor/detectors/missing_foreign_keys_test.rb
|
155
175
|
- test/active_record_doctor/detectors/missing_non_null_constraint_test.rb
|
@@ -161,7 +181,6 @@ files:
|
|
161
181
|
- test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb
|
162
182
|
- test/active_record_doctor/runner_test.rb
|
163
183
|
- test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb
|
164
|
-
- test/model_factory.rb
|
165
184
|
- test/setup.rb
|
166
185
|
homepage: https://github.com/gregnavis/active_record_doctor
|
167
186
|
licenses:
|
@@ -182,16 +201,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
182
201
|
- !ruby/object:Gem::Version
|
183
202
|
version: '0'
|
184
203
|
requirements: []
|
185
|
-
rubygems_version: 3.2.
|
204
|
+
rubygems_version: 3.2.33
|
186
205
|
signing_key:
|
187
206
|
specification_version: 4
|
188
207
|
summary: Identify database issues before they hit production.
|
189
208
|
test_files:
|
190
209
|
- test/active_record_doctor/config/loader_test.rb
|
191
210
|
- test/active_record_doctor/config_test.rb
|
211
|
+
- test/active_record_doctor/detectors/disable_test.rb
|
192
212
|
- test/active_record_doctor/detectors/extraneous_indexes_test.rb
|
193
213
|
- test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb
|
194
214
|
- test/active_record_doctor/detectors/incorrect_dependent_option_test.rb
|
215
|
+
- test/active_record_doctor/detectors/incorrect_length_validation_test.rb
|
195
216
|
- test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb
|
196
217
|
- test/active_record_doctor/detectors/missing_foreign_keys_test.rb
|
197
218
|
- test/active_record_doctor/detectors/missing_non_null_constraint_test.rb
|
@@ -203,5 +224,4 @@ test_files:
|
|
203
224
|
- test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb
|
204
225
|
- test/active_record_doctor/runner_test.rb
|
205
226
|
- test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb
|
206
|
-
- test/model_factory.rb
|
207
227
|
- test/setup.rb
|