active_record_doctor 1.12.0 → 1.13.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -0
  3. data/lib/active_record_doctor/config/loader.rb +1 -1
  4. data/lib/active_record_doctor/detectors/base.rb +11 -7
  5. data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +1 -1
  6. data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +9 -4
  7. data/lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb +1 -1
  8. data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +2 -2
  9. data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +16 -7
  10. data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +1 -0
  11. data/lib/active_record_doctor/logger/hierarchical.rb +1 -1
  12. data/lib/active_record_doctor/railtie.rb +1 -1
  13. data/lib/active_record_doctor/runner.rb +1 -1
  14. data/lib/active_record_doctor/utils.rb +2 -2
  15. data/lib/active_record_doctor/version.rb +1 -1
  16. data/lib/tasks/active_record_doctor.rake +2 -2
  17. metadata +12 -49
  18. data/test/active_record_doctor/config/loader_test.rb +0 -120
  19. data/test/active_record_doctor/config_test.rb +0 -116
  20. data/test/active_record_doctor/detectors/disable_test.rb +0 -30
  21. data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +0 -277
  22. data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +0 -79
  23. data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +0 -511
  24. data/test/active_record_doctor/detectors/incorrect_length_validation_test.rb +0 -107
  25. data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +0 -116
  26. data/test/active_record_doctor/detectors/missing_foreign_keys_test.rb +0 -70
  27. data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +0 -273
  28. data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +0 -232
  29. data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +0 -496
  30. data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +0 -77
  31. data/test/active_record_doctor/detectors/undefined_table_references_test.rb +0 -55
  32. data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +0 -177
  33. data/test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb +0 -116
  34. data/test/active_record_doctor/runner_test.rb +0 -41
  35. data/test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb +0 -141
  36. data/test/setup.rb +0 -124
@@ -1,511 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ActiveRecordDoctor::Detectors::IncorrectDependentOptionTest < Minitest::Test
4
- def test_invoking_no_callbacks_suggests_delete_all
5
- create_table(:companies) do
6
- end.define_model do
7
- has_many :users, dependent: :destroy
8
- end
9
-
10
- create_table(:users) do |t|
11
- t.references :companies
12
- end.define_model do
13
- belongs_to :company
14
- end
15
-
16
- assert_problems(<<~OUTPUT)
17
- use `dependent: :delete_all` or similar on TransientRecord::Models::Company.users - associated model TransientRecord::Models::User has no callbacks and can be deleted in bulk
18
- OUTPUT
19
- end
20
-
21
- def test_invoking_callbacks_does_not_suggest_delete_all
22
- create_table(:companies) do
23
- end.define_model do
24
- has_many :users, dependent: :destroy
25
- end
26
-
27
- create_table(:users) do |t|
28
- t.references :companies
29
- end.define_model do
30
- belongs_to :company
31
-
32
- before_destroy :log
33
-
34
- def log
35
- end
36
- end
37
-
38
- refute_problems
39
- end
40
-
41
- def test_skipping_callbacks_suggests_destroy
42
- create_table(:companies) do
43
- end.define_model do
44
- has_many :users, dependent: :delete_all
45
- end
46
-
47
- create_table(:users) do |t|
48
- t.references :companies
49
- end.define_model do
50
- belongs_to :company
51
-
52
- before_destroy :log
53
-
54
- def log
55
- end
56
- end
57
-
58
- assert_problems(<<~OUTPUT)
59
- use `dependent: :destroy` or similar on TransientRecord::Models::Company.users - associated model TransientRecord::Models::User has callbacks that are currently skipped
60
- OUTPUT
61
- end
62
-
63
- def test_invoking_callbacks_does_not_suggest_destroy
64
- create_table(:companies) do
65
- end.define_model do
66
- has_many :users, dependent: :destroy
67
- end
68
-
69
- create_table(:users) do |t|
70
- t.references :companies
71
- end.define_model do
72
- belongs_to :company
73
-
74
- before_destroy :log
75
-
76
- def log
77
- end
78
- end
79
-
80
- refute_problems
81
- end
82
-
83
- def test_works_on_has_one
84
- create_table(:companies) do
85
- end.define_model do
86
- has_one :owner, class_name: "TransientRecord::Models::User", dependent: :destroy
87
- end
88
-
89
- create_table(:users) do |t|
90
- t.references :companies
91
- end.define_model do
92
- belongs_to :company
93
- end
94
-
95
- assert_problems(<<~OUTPUT)
96
- use `dependent: :delete` or similar on TransientRecord::Models::Company.owner - associated model TransientRecord::Models::User has no callbacks and can be deleted without loading
97
- OUTPUT
98
- end
99
-
100
- def test_works_on_belongs_to
101
- create_table(:companies) do
102
- end.define_model do
103
- has_many :users
104
- end
105
-
106
- create_table(:users) do |t|
107
- t.references :company
108
- end.define_model do
109
- belongs_to :company, dependent: :destroy
110
- end
111
-
112
- assert_problems(<<~OUTPUT)
113
- use `dependent: :delete` or similar on TransientRecord::Models::User.company - associated model TransientRecord::Models::Company has no callbacks and can be deleted without loading
114
- OUTPUT
115
- end
116
-
117
- def test_no_foreign_key_on_second_level_association
118
- create_table(:companies) do
119
- end.define_model do
120
- has_many :users
121
- has_many :projects
122
- end
123
-
124
- create_table(:users) do |t|
125
- t.references :company
126
- end.define_model do
127
- belongs_to :company, dependent: :destroy
128
- end
129
-
130
- create_table(:projects) do |t|
131
- t.references :company
132
- end.define_model do
133
- belongs_to :company
134
- end
135
-
136
- assert_problems(<<~OUTPUT)
137
- use `dependent: :delete` or similar on TransientRecord::Models::User.company - associated model TransientRecord::Models::Company has no callbacks and can be deleted without loading
138
- OUTPUT
139
- end
140
-
141
- def test_nullify_foreign_key_on_second_level_association
142
- create_table(:companies) do
143
- end.define_model do
144
- has_many :users
145
- has_many :projects
146
- end
147
-
148
- create_table(:users) do |t|
149
- t.references :company
150
- end.define_model do
151
- belongs_to :company, dependent: :destroy
152
- end
153
-
154
- create_table(:projects) do |t|
155
- t.references :company, foreign_key: { on_delete: :nullify }
156
- end.define_model do
157
- belongs_to :company
158
- end
159
-
160
- assert_problems(<<~OUTPUT)
161
- use `dependent: :delete` or similar on TransientRecord::Models::User.company - associated model TransientRecord::Models::Company has no callbacks and can be deleted without loading
162
- OUTPUT
163
- end
164
-
165
- def test_cascade_foreign_key_and_callbacks_on_second_level_association
166
- create_table(:companies) do
167
- end.define_model do
168
- has_many :users
169
- has_many :projects
170
- end
171
-
172
- create_table(:users) do |t|
173
- t.references :company
174
- end.define_model do
175
- belongs_to :company, dependent: :delete
176
- end
177
-
178
- create_table(:projects) do |t|
179
- t.references :company, foreign_key: { on_delete: :cascade }
180
- end.define_model do
181
- belongs_to :company
182
-
183
- before_destroy :log
184
-
185
- def log
186
- end
187
- end
188
-
189
- assert_problems(<<~OUTPUT)
190
- use `dependent: :destroy` or similar on TransientRecord::Models::User.company - associated model TransientRecord::Models::Company has callbacks that are currently skipped
191
- OUTPUT
192
- end
193
-
194
- def test_cascade_foreign_key_and_no_callbacks_on_second_level_association
195
- create_table(:companies) do
196
- end.define_model do
197
- has_many :users
198
- has_many :projects
199
- end
200
-
201
- create_table(:users) do |t|
202
- t.references :company
203
- end.define_model do
204
- belongs_to :company, dependent: :delete
205
- end
206
-
207
- create_table(:projects) do |t|
208
- t.references :company, foreign_key: { on_delete: :cascade }
209
- end.define_model do
210
- belongs_to :company
211
- end
212
-
213
- refute_problems
214
- end
215
-
216
- def test_no_dependent_suggests_nothing
217
- create_table(:companies) do
218
- end.define_model do
219
- has_many :users
220
- end
221
-
222
- create_table(:users) do |t|
223
- t.references :companies
224
- end.define_model do
225
- belongs_to :company
226
- end
227
-
228
- refute_problems
229
- end
230
-
231
- def test_polymorphic_destroy_reported_when_all_associations_deletable
232
- create_table(:images) do |t|
233
- t.bigint :imageable_id, null: false
234
- t.string :imageable_type, null: true
235
- end.define_model do
236
- belongs_to :imageable, polymorphic: true, dependent: :destroy
237
- end
238
-
239
- create_table(:users) do
240
- end.define_model do
241
- has_one :image, as: :imageable
242
- end
243
-
244
- create_table(:companies) do
245
- end.define_model do
246
- has_one :image, as: :imageable
247
- end
248
-
249
- assert_problems(<<~OUTPUT)
250
- use `dependent: :delete` or similar on TransientRecord::Models::Image.imageable - associated models TransientRecord::Models::Company, TransientRecord::Models::User have no callbacks and can be deleted without loading
251
- OUTPUT
252
- end
253
-
254
- def test_polymorphic_destroy_not_reported_when_some_associations_not_deletable
255
- create_table(:images) do |t|
256
- t.bigint :imageable_id, null: false
257
- t.string :imageable_type, null: true
258
- end.define_model do
259
- belongs_to :imageable, polymorphic: true, dependent: :destroy
260
- end
261
-
262
- create_table(:users) do
263
- end.define_model do
264
- has_one :image, as: :imageable
265
-
266
- before_destroy :log
267
-
268
- def log
269
- end
270
- end
271
-
272
- create_table(:companies) do
273
- end.define_model do
274
- has_one :image, as: :imageable
275
- end
276
-
277
- refute_problems
278
- end
279
-
280
- def test_polymorphic_delete_reported_when_some_associations_not_deletable
281
- create_table(:images) do |t|
282
- t.bigint :imageable_id, null: false
283
- t.string :imageable_type, null: true
284
- end.define_model do
285
- belongs_to :imageable, polymorphic: true, dependent: :delete
286
- end
287
-
288
- create_table(:users) do
289
- end.define_model do
290
- has_one :image, as: :imageable
291
-
292
- before_destroy :log
293
-
294
- def log
295
- end
296
- end
297
-
298
- create_table(:companies) do
299
- end.define_model do
300
- has_one :image, as: :imageable
301
- end
302
-
303
- assert_problems(<<~OUTPUT)
304
- use `dependent: :destroy` or similar on TransientRecord::Models::Image.imageable - associated model TransientRecord::Models::User has callbacks that are currently skipped
305
- OUTPUT
306
- end
307
-
308
- def test_polymorphic_delete_not_reported_when_all_associations_deletable
309
- create_table(:images) do |t|
310
- t.bigint :imageable_id, null: false
311
- t.string :imageable_type, null: true
312
- end.define_model do
313
- belongs_to :imageable, polymorphic: true, dependent: :delete
314
- end
315
-
316
- create_table(:users) do
317
- end.define_model do
318
- has_one :image, as: :imageable
319
- end
320
-
321
- create_table(:companies) do
322
- end.define_model do
323
- has_one :image, as: :imageable
324
- end
325
-
326
- refute_problems
327
- end
328
-
329
- def test_works_on_has_through_associations_with_destroy
330
- create_table(:users) do
331
- end.define_model do
332
- has_many :posts
333
- has_many :comments, through: :posts, dependent: :destroy
334
- end
335
-
336
- create_table(:posts) do |t|
337
- t.references :users
338
- end.define_model do
339
- belongs_to :user
340
- has_many :comments
341
- end
342
-
343
- create_table(:comments) do |t|
344
- t.references :posts
345
- end.define_model do
346
- belongs_to :post
347
- end
348
-
349
- assert_problems(<<~OUTPUT)
350
- use `dependent: :delete_all` or similar on TransientRecord::Models::User.comments - associated join model TransientRecord::Models::Post has no callbacks and can be deleted in bulk
351
- OUTPUT
352
- end
353
-
354
- def test_works_on_has_through_associations_with_delete_all
355
- create_table(:users) do
356
- end.define_model do
357
- has_many :posts
358
- has_many :comments, through: :posts, dependent: :delete_all
359
- end
360
-
361
- create_table(:posts) do |t|
362
- t.references :users
363
- end.define_model do
364
- belongs_to :user
365
- has_many :comments
366
-
367
- before_destroy :log
368
-
369
- def log
370
- end
371
- end
372
-
373
- create_table(:comments) do |t|
374
- t.references :posts
375
- end.define_model do
376
- belongs_to :post
377
- end
378
-
379
- assert_problems(<<~OUTPUT)
380
- use `dependent: :destroy` or similar on TransientRecord::Models::User.comments - associated join model TransientRecord::Models::Post has callbacks that are currently skipped
381
- OUTPUT
382
- end
383
-
384
- def test_has_through_associations_when_join_model_incomplete
385
- create_table(:users) do
386
- end.define_model do
387
- has_many :posts
388
- has_many :comments, through: :posts
389
- end
390
-
391
- create_table(:posts) do |t|
392
- t.references :users
393
- end.define_model do
394
- # The join model should define has_many :comments, but intentionally skips
395
- # it for this test case's purpose.
396
- end
397
-
398
- create_table(:comments) do |t|
399
- t.references :posts
400
- end.define_model do
401
- end
402
-
403
- assert_problems(<<~OUTPUT)
404
- ensure TransientRecord::Models::User.comments is configured correctly - TransientRecord::Models::Post.comments may be undefined
405
- OUTPUT
406
- end
407
-
408
- def test_destroy_async_and_foreign_key_exists
409
- skip("ActiveRecord < 6.1 doesn't support :destroy_async") if ActiveRecord::VERSION::STRING < "6.1"
410
-
411
- create_table(:companies) do
412
- end.define_model do
413
- # We need an ActiveJob job defined to appease the ActiveRecord
414
- class_attribute :destroy_association_async_job, default: Class.new
415
-
416
- has_many :users, dependent: :destroy_async
417
- end
418
-
419
- create_table(:users) do |t|
420
- t.references :company, foreign_key: true
421
- end.define_model
422
-
423
- assert_problems(<<~OUTPUT)
424
- don't use `dependent: :destroy_async` on TransientRecord::Models::Company.users or remove the foreign key from users.company_id - \
425
- associated models will be deleted in the same transaction along with TransientRecord::Models::Company
426
- OUTPUT
427
- end
428
-
429
- def test_destroy_async_and_no_foreign_key
430
- skip("ActiveRecord < 6.1 doesn't support :destroy_async") if ActiveRecord::VERSION::STRING < "6.1"
431
-
432
- create_table(:companies) do
433
- end.define_model do
434
- # We need an ActiveJob job defined to appease the ActiveRecord
435
- class_attribute :destroy_association_async_job, default: Class.new
436
-
437
- has_many :users, dependent: :destroy_async
438
- end
439
-
440
- create_table(:users) do |t|
441
- t.references :company, foreign_key: false
442
- end.define_model
443
-
444
- refute_problems
445
- end
446
-
447
- def test_config_ignore_models
448
- create_table(:companies) do
449
- end.define_model do
450
- has_many :users, dependent: :destroy
451
- end
452
-
453
- create_table(:users) do |t|
454
- t.references :companies
455
- end.define_model do
456
- belongs_to :company
457
- end
458
-
459
- config_file(<<-CONFIG)
460
- ActiveRecordDoctor.configure do |config|
461
- config.detector :incorrect_dependent_option,
462
- ignore_models: ["TransientRecord::Models::Company"]
463
- end
464
- CONFIG
465
-
466
- refute_problems
467
- end
468
-
469
- def test_global_ignore_models
470
- create_table(:companies) do
471
- end.define_model do
472
- has_many :users, dependent: :destroy
473
- end
474
-
475
- create_table(:users) do |t|
476
- t.references :companies
477
- end.define_model do
478
- belongs_to :company
479
- end
480
-
481
- config_file(<<-CONFIG)
482
- ActiveRecordDoctor.configure do |config|
483
- config.global :ignore_models, ["TransientRecord::Models::Company"]
484
- end
485
- CONFIG
486
-
487
- refute_problems
488
- end
489
-
490
- def test_config_ignore_associations
491
- create_table(:companies) do
492
- end.define_model do
493
- has_many :users, dependent: :destroy
494
- end
495
-
496
- create_table(:users) do |t|
497
- t.references :companies
498
- end.define_model do
499
- belongs_to :company
500
- end
501
-
502
- config_file(<<-CONFIG)
503
- ActiveRecordDoctor.configure do |config|
504
- config.detector :incorrect_dependent_option,
505
- ignore_associations: ["TransientRecord::Models::Company.users"]
506
- end
507
- CONFIG
508
-
509
- refute_problems
510
- end
511
- end
@@ -1,107 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ActiveRecordDoctor::Detectors::IncorrectLengthValidationTest < Minitest::Test
4
- def test_validation_and_limit_equal_is_ok
5
- create_table(:users) do |t|
6
- t.string :email, limit: 64
7
- t.string :name, limit: 32
8
- end.define_model do
9
- validates :email, length: { maximum: 64 }
10
- validates :name, length: { maximum: 32 }
11
- end
12
-
13
- refute_problems
14
- end
15
-
16
- def test_validation_and_limit_different_is_error
17
- create_table(:users) do |t|
18
- t.string :email, limit: 64
19
- end.define_model do
20
- validates :email, length: { maximum: 32 }
21
- end
22
-
23
- assert_problems(<<~OUTPUT)
24
- the schema limits users.email to 64 characters but the length validator on TransientRecord::Models::User.email enforces a maximum of 32 characters - set both limits to the same value or remove both
25
- OUTPUT
26
- end
27
-
28
- def test_validation_and_no_limit_is_error
29
- skip("MySQL always sets a limit on text columns") if mysql?
30
-
31
- create_table(:users) do |t|
32
- t.string :email
33
- end.define_model do
34
- validates :email, length: { maximum: 32 }
35
- end
36
-
37
- assert_problems(<<~OUTPUT)
38
- the length validator on TransientRecord::Models::User.email enforces a maximum of 32 characters but there's no schema limit on users.email - remove the validator or the schema length limit
39
- OUTPUT
40
- end
41
-
42
- def test_no_validation_and_limit_is_error
43
- create_table(:users) do |t|
44
- t.string :email, limit: 64
45
- end.define_model do
46
- end
47
-
48
- assert_problems(<<~OUTPUT)
49
- the schema limits users.email to 64 characters but there's no length validator on TransientRecord::Models::User.email - remove the database limit or add the validator
50
- OUTPUT
51
- end
52
-
53
- def test_no_validation_and_no_limit_is_ok
54
- skip("MySQL always sets a limit on text columns") if mysql?
55
-
56
- create_table(:users) do |t|
57
- t.string :email
58
- end.define_model do
59
- end
60
-
61
- refute_problems
62
- end
63
-
64
- def test_config_ignore_models
65
- create_table(:users) do |t|
66
- t.string :email, limit: 64
67
- end.define_model
68
-
69
- config_file(<<-CONFIG)
70
- ActiveRecordDoctor.configure do |config|
71
- config.detector :incorrect_length_validation,
72
- ignore_models: ["TransientRecord::Models::User"]
73
- end
74
- CONFIG
75
-
76
- refute_problems
77
- end
78
-
79
- def test_global_ignore_models
80
- create_table(:users) do |t|
81
- t.string :email, limit: 64
82
- end.define_model
83
-
84
- config_file(<<-CONFIG)
85
- ActiveRecordDoctor.configure do |config|
86
- config.global :ignore_models, ["TransientRecord::Models::User"]
87
- end
88
- CONFIG
89
-
90
- refute_problems
91
- end
92
-
93
- def test_config_ignore_attributes
94
- create_table(:users) do |t|
95
- t.string :email, limit: 64
96
- end.define_model
97
-
98
- config_file(<<-CONFIG)
99
- ActiveRecordDoctor.configure do |config|
100
- config.detector :incorrect_length_validation,
101
- ignore_attributes: ["TransientRecord::Models::User.email"]
102
- end
103
- CONFIG
104
-
105
- refute_problems
106
- end
107
- end