active_record_doctor 1.5.0 → 1.8.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 +5 -5
- data/README.md +152 -12
- data/lib/active_record_doctor.rb +20 -2
- data/lib/active_record_doctor/detectors.rb +13 -0
- data/lib/active_record_doctor/detectors/base.rb +64 -0
- data/lib/active_record_doctor/{tasks → detectors}/extraneous_indexes.rb +12 -29
- data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +40 -0
- data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +71 -0
- data/lib/active_record_doctor/{tasks → detectors}/missing_foreign_keys.rb +17 -35
- data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +60 -0
- data/lib/active_record_doctor/detectors/missing_presence_validation.rb +78 -0
- data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +61 -0
- data/lib/active_record_doctor/detectors/undefined_table_references.rb +34 -0
- data/lib/active_record_doctor/detectors/unindexed_deleted_at.rb +29 -0
- data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +48 -0
- data/lib/active_record_doctor/printers.rb +3 -1
- data/lib/active_record_doctor/printers/io_printer.rb +101 -26
- data/lib/active_record_doctor/railtie.rb +3 -1
- data/lib/active_record_doctor/task.rb +28 -0
- data/lib/active_record_doctor/version.rb +3 -1
- data/lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb +15 -11
- data/lib/tasks/active_record_doctor.rake +33 -0
- data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +67 -0
- data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +36 -0
- data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +117 -0
- data/test/active_record_doctor/detectors/missing_foreign_keys_test.rb +24 -0
- data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +102 -0
- data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +107 -0
- data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +114 -0
- data/test/active_record_doctor/detectors/undefined_table_references_test.rb +44 -0
- data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +67 -0
- data/test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb +26 -0
- data/test/active_record_doctor/printers/io_printer_test.rb +23 -10
- data/test/model_factory.rb +78 -0
- data/test/setup.rb +126 -0
- metadata +93 -149
- data/Rakefile +0 -28
- data/lib/active_record_doctor/compatibility.rb +0 -11
- data/lib/active_record_doctor/tasks.rb +0 -4
- data/lib/active_record_doctor/tasks/undefined_table_references.rb +0 -34
- data/lib/active_record_doctor/tasks/unindexed_deleted_at.rb +0 -40
- data/lib/active_record_doctor/tasks/unindexed_foreign_keys.rb +0 -66
- data/lib/tasks/active_record_doctor_tasks.rake +0 -27
- data/test/active_record_doctor/tasks/extraneous_indexes_test.rb +0 -27
- data/test/active_record_doctor/tasks/missing_foreign_keys_test.rb +0 -19
- data/test/active_record_doctor/tasks/undefined_table_references_test.rb +0 -19
- data/test/active_record_doctor/tasks/unindexed_deleted_at_test.rb +0 -19
- data/test/active_record_doctor/tasks/unindexed_foreign_keys_test.rb +0 -19
- data/test/dummy/README.rdoc +0 -28
- data/test/dummy/Rakefile +0 -6
- data/test/dummy/app/assets/javascripts/application.js +0 -13
- data/test/dummy/app/assets/stylesheets/application.css +0 -15
- data/test/dummy/app/controllers/application_controller.rb +0 -5
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/models/application_record.rb +0 -3
- data/test/dummy/app/models/comment.rb +0 -3
- data/test/dummy/app/models/contract.rb +0 -3
- data/test/dummy/app/models/employer.rb +0 -2
- data/test/dummy/app/models/profile.rb +0 -2
- data/test/dummy/app/models/user.rb +0 -3
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/bin/bundle +0 -3
- data/test/dummy/bin/rails +0 -4
- data/test/dummy/bin/rake +0 -4
- data/test/dummy/bin/setup +0 -29
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -23
- data/test/dummy/config/boot.rb +0 -5
- data/test/dummy/config/database.yml +0 -19
- data/test/dummy/config/database.yml.travis +0 -5
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -41
- data/test/dummy/config/environments/production.rb +0 -79
- data/test/dummy/config/environments/test.rb +0 -47
- data/test/dummy/config/initializers/assets.rb +0 -11
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
- data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
- data/test/dummy/config/initializers/inflections.rb +0 -16
- data/test/dummy/config/initializers/mime_types.rb +0 -4
- data/test/dummy/config/initializers/session_store.rb +0 -3
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -23
- data/test/dummy/config/routes.rb +0 -56
- data/test/dummy/config/secrets.yml +0 -22
- data/test/dummy/db/migrate/20160213101213_create_employers.rb +0 -15
- data/test/dummy/db/migrate/20160213101221_create_users.rb +0 -23
- data/test/dummy/db/migrate/20160213101232_create_profiles.rb +0 -15
- data/test/dummy/db/migrate/20160604081452_create_comments.rb +0 -11
- data/test/dummy/db/migrate/base_migration.rb +0 -5
- data/test/dummy/db/schema.rb +0 -68
- data/test/dummy/log/development.log +0 -532
- data/test/dummy/log/test.log +0 -2699
- data/test/dummy/public/404.html +0 -67
- data/test/dummy/public/422.html +0 -67
- data/test/dummy/public/500.html +0 -66
- data/test/dummy/public/favicon.ico +0 -0
- data/test/support/spy_printer.rb +0 -52
- data/test/test_helper.rb +0 -20
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveRecordDoctor::Detectors::MissingForeignKeysTest < Minitest::Test
|
4
|
+
def test_missing_foreign_key_is_reported
|
5
|
+
create_table(:companies)
|
6
|
+
create_table(:users) do |t|
|
7
|
+
t.references :company, foreign_key: false
|
8
|
+
end
|
9
|
+
|
10
|
+
assert_problems(<<OUTPUT)
|
11
|
+
The following columns lack a foreign key constraint:
|
12
|
+
users company_id
|
13
|
+
OUTPUT
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_present_foreign_key_is_not_reported
|
17
|
+
create_table(:companies)
|
18
|
+
create_table(:users) do |t|
|
19
|
+
t.references :company, foreign_key: true
|
20
|
+
end
|
21
|
+
|
22
|
+
refute_problems
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveRecordDoctor::Detectors::MissingNonNullConstraintTest < Minitest::Test
|
4
|
+
def test_presence_true_and_null_true
|
5
|
+
create_table(:users) do |t|
|
6
|
+
t.string :name, null: true
|
7
|
+
end.create_model do
|
8
|
+
validates :name, presence: true
|
9
|
+
end
|
10
|
+
|
11
|
+
assert_problems(<<OUTPUT)
|
12
|
+
The following columns should be marked as `null: false`:
|
13
|
+
users: name
|
14
|
+
OUTPUT
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_association_presence_true_and_null_true
|
18
|
+
create_table(:companies)
|
19
|
+
create_table(:users) do |t|
|
20
|
+
t.references :company
|
21
|
+
end.create_model do
|
22
|
+
belongs_to :company, required: true
|
23
|
+
end
|
24
|
+
|
25
|
+
assert_problems(<<OUTPUT)
|
26
|
+
The following columns should be marked as `null: false`:
|
27
|
+
users: company_id
|
28
|
+
OUTPUT
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_presence_true_and_null_false
|
32
|
+
create_table(:users) do |t|
|
33
|
+
t.string :name, null: false
|
34
|
+
end.create_model do
|
35
|
+
validates :name, presence: true
|
36
|
+
end
|
37
|
+
|
38
|
+
refute_problems
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_presence_false_and_null_true
|
42
|
+
create_table(:users) do |t|
|
43
|
+
t.string :name, null: true
|
44
|
+
end.create_model do
|
45
|
+
# The age validator is a form of regression test against a bug that
|
46
|
+
# caused false positives. In this test case, name is NOT validated
|
47
|
+
# for presence so it does NOT need be marked non-NULL. However, the
|
48
|
+
# bug would match the age presence validator with the NULL-able name
|
49
|
+
# column which would result in a false positive error report.
|
50
|
+
validates :age, presence: true
|
51
|
+
validates :name, presence: false
|
52
|
+
end
|
53
|
+
|
54
|
+
refute_problems
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_presence_false_and_null_false
|
58
|
+
create_table(:users) do |t|
|
59
|
+
t.string :name, null: false
|
60
|
+
end.create_model do
|
61
|
+
validates :name, presence: false
|
62
|
+
end
|
63
|
+
|
64
|
+
refute_problems
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_presence_true_with_if
|
68
|
+
create_table(:users) do |t|
|
69
|
+
t.string :name, null: true
|
70
|
+
end.create_model do
|
71
|
+
validates :name, presence: true, if: -> { false }
|
72
|
+
end
|
73
|
+
|
74
|
+
refute_problems
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_presence_true_with_unless
|
78
|
+
create_table(:users) do |t|
|
79
|
+
t.string :name, null: true
|
80
|
+
end.create_model do
|
81
|
+
validates :name, presence: true, unless: -> { false }
|
82
|
+
end
|
83
|
+
|
84
|
+
refute_problems
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_presence_true_with_allow_nil
|
88
|
+
create_table(:users) do |t|
|
89
|
+
t.string :name, null: true
|
90
|
+
end.create_model do
|
91
|
+
validates :name, presence: true, allow_nil: true
|
92
|
+
end
|
93
|
+
|
94
|
+
refute_problems
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_models_with_non_existent_tables_are_skipped
|
98
|
+
create_model(:users)
|
99
|
+
|
100
|
+
refute_problems
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveRecordDoctor::Detectors::MissingPresenceValidationTest < Minitest::Test
|
4
|
+
def test_null_column_is_not_reported_if_validation_absent
|
5
|
+
create_table(:users) do |t|
|
6
|
+
t.string :name
|
7
|
+
end.create_model do
|
8
|
+
end
|
9
|
+
|
10
|
+
refute_problems
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_non_null_column_is_reported_if_validation_absent
|
14
|
+
create_table(:users) do |t|
|
15
|
+
t.string :name, null: false
|
16
|
+
end.create_model do
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_problems(<<OUTPUT)
|
20
|
+
The following models and columns should have presence validations:
|
21
|
+
ModelFactory::Models::User: name
|
22
|
+
OUTPUT
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_non_null_column_is_not_reported_if_validation_present
|
26
|
+
create_table(:users) do |t|
|
27
|
+
t.string :name, null: false
|
28
|
+
end.create_model do
|
29
|
+
validates :name, presence: true
|
30
|
+
end
|
31
|
+
|
32
|
+
refute_problems
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_non_null_column_is_not_reported_if_association_validation_present
|
36
|
+
create_table(:companies).create_model
|
37
|
+
create_table(:users) do |t|
|
38
|
+
t.references :company, null: false
|
39
|
+
end.create_model do
|
40
|
+
belongs_to :company, required: true
|
41
|
+
end
|
42
|
+
|
43
|
+
refute_problems
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_non_null_boolean_is_reported_if_nil_included
|
47
|
+
create_table(:users) do |t|
|
48
|
+
t.boolean :active, null: false
|
49
|
+
end.create_model do
|
50
|
+
validates :active, inclusion: { in: [nil, true, false] }
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_problems(<<OUTPUT)
|
54
|
+
The following models and columns should have presence validations:
|
55
|
+
ModelFactory::Models::User: active
|
56
|
+
OUTPUT
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_non_null_boolean_is_not_reported_if_nil_not_included
|
60
|
+
create_table(:users) do |t|
|
61
|
+
t.boolean :active, null: false
|
62
|
+
end.create_model do
|
63
|
+
validates :active, inclusion: { in: [true, false] }
|
64
|
+
end
|
65
|
+
|
66
|
+
refute_problems
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_non_null_boolean_is_not_reported_if_nil_excluded
|
70
|
+
create_table(:users) do |t|
|
71
|
+
t.boolean :active, null: false
|
72
|
+
end.create_model do
|
73
|
+
validates :active, exclusion: { in: [nil] }
|
74
|
+
end
|
75
|
+
|
76
|
+
refute_problems
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_non_null_boolean_is_reported_if_nil_not_excluded
|
80
|
+
create_table(:users) do |t|
|
81
|
+
t.boolean :active, null: false
|
82
|
+
end.create_model do
|
83
|
+
validates :active, exclusion: { in: [false] }
|
84
|
+
end
|
85
|
+
|
86
|
+
assert_problems(<<OUTPUT)
|
87
|
+
The following models and columns should have presence validations:
|
88
|
+
ModelFactory::Models::User: active
|
89
|
+
OUTPUT
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_timestamps_are_not_reported
|
93
|
+
create_table(:users) do |t|
|
94
|
+
t.timestamps null: false
|
95
|
+
end.create_model do
|
96
|
+
validates :name, presence: true
|
97
|
+
end
|
98
|
+
|
99
|
+
refute_problems
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_models_with_non_existent_tables_are_skipped
|
103
|
+
create_model(:users)
|
104
|
+
|
105
|
+
refute_problems
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveRecordDoctor::Detectors::MissingUniqueIndexesTest < Minitest::Test
|
4
|
+
def test_missing_unique_index
|
5
|
+
create_table(:users) do |t|
|
6
|
+
t.string :email
|
7
|
+
t.index :email
|
8
|
+
end.create_model do
|
9
|
+
validates :email, uniqueness: true
|
10
|
+
end
|
11
|
+
|
12
|
+
assert_problems(<<OUTPUT)
|
13
|
+
The following indexes should be created to back model-level uniqueness validations:
|
14
|
+
users: email
|
15
|
+
OUTPUT
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_present_unique_index
|
19
|
+
create_table(:users) do |t|
|
20
|
+
t.string :email
|
21
|
+
t.index :email, unique: true
|
22
|
+
end.create_model do
|
23
|
+
validates :email, uniqueness: true
|
24
|
+
end
|
25
|
+
|
26
|
+
refute_problems
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_missing_unique_index_with_scope
|
30
|
+
create_table(:users) do |t|
|
31
|
+
t.string :email
|
32
|
+
t.integer :company_id
|
33
|
+
t.integer :department_id
|
34
|
+
t.index [:company_id, :department_id, :email]
|
35
|
+
end.create_model do
|
36
|
+
validates :email, uniqueness: { scope: [:company_id, :department_id] }
|
37
|
+
end
|
38
|
+
|
39
|
+
assert_problems(<<OUTPUT)
|
40
|
+
The following indexes should be created to back model-level uniqueness validations:
|
41
|
+
users: company_id, department_id, email
|
42
|
+
OUTPUT
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_present_unique_index_with_scope
|
46
|
+
create_table(:users) do |t|
|
47
|
+
t.string :email
|
48
|
+
t.integer :company_id
|
49
|
+
t.integer :department_id
|
50
|
+
t.index [:company_id, :department_id, :email], unique: true
|
51
|
+
end.create_model do
|
52
|
+
validates :email, uniqueness: { scope: [:company_id, :department_id] }
|
53
|
+
end
|
54
|
+
|
55
|
+
refute_problems
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_column_order_is_ignored
|
59
|
+
create_table(:users) do |t|
|
60
|
+
t.string :email
|
61
|
+
t.integer :organization_id
|
62
|
+
|
63
|
+
t.index [:email, :organization_id], unique: true
|
64
|
+
end.create_model do
|
65
|
+
validates :email, uniqueness: { scope: :organization_id }
|
66
|
+
end
|
67
|
+
|
68
|
+
refute_problems
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_conditions_is_skipped
|
72
|
+
assert_skipped(conditions: -> { where.not(email: nil) })
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_case_insensitive_is_skipped
|
76
|
+
assert_skipped(case_sensitive: false)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_if_is_skipped
|
80
|
+
assert_skipped(if: ->(_model) { true })
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_unless_is_skipped
|
84
|
+
assert_skipped(unless: ->(_model) { true })
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_skips_validator_without_attributes
|
88
|
+
create_table(:users) do |t|
|
89
|
+
t.string :email
|
90
|
+
t.index :email
|
91
|
+
end.create_model do
|
92
|
+
validates_with DummyValidator
|
93
|
+
end
|
94
|
+
|
95
|
+
refute_problems
|
96
|
+
end
|
97
|
+
|
98
|
+
class DummyValidator < ActiveModel::Validator
|
99
|
+
def validate(record)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def assert_skipped(options)
|
106
|
+
create_table(:users) do |t|
|
107
|
+
t.string :email
|
108
|
+
end.create_model do
|
109
|
+
validates :email, uniqueness: options
|
110
|
+
end
|
111
|
+
|
112
|
+
refute_problems
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveRecordDoctor::Detectors::UndefinedTableReferencesTest < Minitest::Test
|
4
|
+
def test_table_exists
|
5
|
+
create_table(:users) do
|
6
|
+
end.create_model do
|
7
|
+
end
|
8
|
+
|
9
|
+
refute_problems
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_table_does_not_exist_when_views_supported
|
13
|
+
create_model(:users)
|
14
|
+
|
15
|
+
if mysql? && ActiveRecord::VERSION::STRING < "5.0"
|
16
|
+
assert_problems(<<OUTPUT)
|
17
|
+
WARNING: Models backed by database views are supported only in Rails 5+ OR
|
18
|
+
Rails 4.2 + PostgreSQL. It seems this is NOT your setup. Therefore, such models
|
19
|
+
will be erroneously reported below as not having their underlying tables/views.
|
20
|
+
Consider upgrading Rails or disabling this task temporarily.
|
21
|
+
The following models reference undefined tables:
|
22
|
+
ModelFactory::Models::User (the table users is undefined)
|
23
|
+
OUTPUT
|
24
|
+
else
|
25
|
+
assert_problems(<<OUTPUT)
|
26
|
+
The following models reference undefined tables:
|
27
|
+
ModelFactory::Models::User (the table users is undefined)
|
28
|
+
OUTPUT
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_view_instead_of_table
|
33
|
+
# We replace the underlying table with a view. The view doesn't have to be
|
34
|
+
# backed by an actual table - it can simply return a predefined tuple.
|
35
|
+
ActiveRecord::Base.connection.execute("CREATE VIEW users AS SELECT 1")
|
36
|
+
create_model(:users)
|
37
|
+
|
38
|
+
begin
|
39
|
+
refute_problems
|
40
|
+
ensure
|
41
|
+
ActiveRecord::Base.connection.execute("DROP VIEW users")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveRecordDoctor::Detectors::UnindexedDeletedAtTest < Minitest::Test
|
4
|
+
def test_indexed_deleted_at_is_not_reported
|
5
|
+
skip("MySQL doesn't support partial indexes") if mysql?
|
6
|
+
|
7
|
+
create_table(:users) do |t|
|
8
|
+
t.string :first_name
|
9
|
+
t.string :last_name
|
10
|
+
t.datetime :deleted_at
|
11
|
+
t.index [:first_name, :last_name],
|
12
|
+
name: "index_profiles_on_first_name_and_last_name",
|
13
|
+
where: "deleted_at IS NULL"
|
14
|
+
end
|
15
|
+
|
16
|
+
refute_problems
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_unindexed_deleted_at_is_reported
|
20
|
+
skip("MySQL doesn't support partial indexes") if mysql?
|
21
|
+
|
22
|
+
create_table(:users) do |t|
|
23
|
+
t.string :first_name
|
24
|
+
t.string :last_name
|
25
|
+
t.datetime :deleted_at
|
26
|
+
t.index [:first_name, :last_name],
|
27
|
+
name: "index_profiles_on_first_name_and_last_name"
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_problems(<<OUTPUT)
|
31
|
+
The following indexes should include `deleted_at IS NULL`:
|
32
|
+
index_profiles_on_first_name_and_last_name
|
33
|
+
OUTPUT
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_indexed_discarded_at_is_not_reported
|
37
|
+
skip("MySQL doesn't support partial indexes") if mysql?
|
38
|
+
|
39
|
+
create_table(:users) do |t|
|
40
|
+
t.string :first_name
|
41
|
+
t.string :last_name
|
42
|
+
t.datetime :discarded_at
|
43
|
+
t.index [:first_name, :last_name],
|
44
|
+
name: "index_profiles_on_first_name_and_last_name",
|
45
|
+
where: "discarded_at IS NULL"
|
46
|
+
end
|
47
|
+
|
48
|
+
refute_problems
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_unindexed_discarded_at_is_reported
|
52
|
+
skip("MySQL doesn't support partial indexes") if mysql?
|
53
|
+
|
54
|
+
create_table(:users) do |t|
|
55
|
+
t.string :first_name
|
56
|
+
t.string :last_name
|
57
|
+
t.datetime :discarded_at
|
58
|
+
t.index [:first_name, :last_name],
|
59
|
+
name: "index_profiles_on_first_name_and_last_name"
|
60
|
+
end
|
61
|
+
|
62
|
+
assert_problems(<<OUTPUT)
|
63
|
+
The following indexes should include `deleted_at IS NULL`:
|
64
|
+
index_profiles_on_first_name_and_last_name
|
65
|
+
OUTPUT
|
66
|
+
end
|
67
|
+
end
|