active_record_doctor 1.12.0 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -2
  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/rake/task.rb +35 -3
  14. data/lib/active_record_doctor/runner.rb +1 -1
  15. data/lib/active_record_doctor/utils.rb +2 -2
  16. data/lib/active_record_doctor/version.rb +1 -1
  17. data/lib/tasks/active_record_doctor.rake +1 -2
  18. metadata +12 -49
  19. data/test/active_record_doctor/config/loader_test.rb +0 -120
  20. data/test/active_record_doctor/config_test.rb +0 -116
  21. data/test/active_record_doctor/detectors/disable_test.rb +0 -30
  22. data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +0 -277
  23. data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +0 -79
  24. data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +0 -511
  25. data/test/active_record_doctor/detectors/incorrect_length_validation_test.rb +0 -107
  26. data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +0 -116
  27. data/test/active_record_doctor/detectors/missing_foreign_keys_test.rb +0 -70
  28. data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +0 -273
  29. data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +0 -232
  30. data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +0 -496
  31. data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +0 -77
  32. data/test/active_record_doctor/detectors/undefined_table_references_test.rb +0 -55
  33. data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +0 -177
  34. data/test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb +0 -116
  35. data/test/active_record_doctor/runner_test.rb +0 -41
  36. data/test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb +0 -141
  37. 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