active_record_doctor 1.8.0 → 1.9.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +246 -48
- data/lib/active_record_doctor/config/default.rb +59 -0
- data/lib/active_record_doctor/config/loader.rb +137 -0
- data/lib/active_record_doctor/config.rb +14 -0
- data/lib/active_record_doctor/detectors/base.rb +110 -19
- data/lib/active_record_doctor/detectors/extraneous_indexes.rb +63 -37
- data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +32 -23
- data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +70 -34
- data/lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb +45 -0
- data/lib/active_record_doctor/detectors/missing_foreign_keys.rb +32 -23
- data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +40 -28
- data/lib/active_record_doctor/detectors/missing_presence_validation.rb +28 -21
- data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +40 -30
- data/lib/active_record_doctor/detectors/short_primary_key_type.rb +41 -0
- data/lib/active_record_doctor/detectors/undefined_table_references.rb +19 -20
- data/lib/active_record_doctor/detectors/unindexed_deleted_at.rb +44 -18
- data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +31 -20
- data/lib/active_record_doctor/detectors.rb +12 -4
- data/lib/active_record_doctor/errors.rb +226 -0
- data/lib/active_record_doctor/help.rb +39 -0
- data/lib/active_record_doctor/rake/task.rb +78 -0
- data/lib/active_record_doctor/runner.rb +41 -0
- data/lib/active_record_doctor/version.rb +1 -1
- data/lib/active_record_doctor.rb +7 -3
- data/lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb +34 -21
- data/lib/tasks/active_record_doctor.rake +9 -18
- data/test/active_record_doctor/config/loader_test.rb +120 -0
- data/test/active_record_doctor/config_test.rb +116 -0
- data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +131 -8
- data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +48 -5
- data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +190 -12
- data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +82 -0
- data/test/active_record_doctor/detectors/missing_foreign_keys_test.rb +50 -4
- data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +138 -24
- data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +74 -13
- data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +57 -8
- data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +64 -0
- data/test/active_record_doctor/detectors/undefined_table_references_test.rb +34 -21
- data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +112 -8
- data/test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb +56 -4
- data/test/active_record_doctor/runner_test.rb +42 -0
- data/test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb +131 -0
- data/test/model_factory.rb +73 -23
- data/test/setup.rb +62 -72
- metadata +40 -9
- data/lib/active_record_doctor/printers/io_printer.rb +0 -133
- data/lib/active_record_doctor/task.rb +0 -28
- data/test/active_record_doctor/printers/io_printer_test.rb +0 -33
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveRecordDoctor::ConfigTest < Minitest::Test
|
4
|
+
def test_merge_globals_empty
|
5
|
+
config1 = ActiveRecordDoctor::Config.new({}, {})
|
6
|
+
config2 = ActiveRecordDoctor::Config.new({}, {})
|
7
|
+
|
8
|
+
config = config1.merge(config2)
|
9
|
+
|
10
|
+
assert_equal({}, config.globals)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_merge_globals_in_config1
|
14
|
+
config1 = ActiveRecordDoctor::Config.new(
|
15
|
+
{ config1_global: "config1:config1_global" },
|
16
|
+
{}
|
17
|
+
)
|
18
|
+
config2 = ActiveRecordDoctor::Config.new({}, {})
|
19
|
+
|
20
|
+
config = config1.merge(config2)
|
21
|
+
|
22
|
+
assert_equal(
|
23
|
+
{ config1_global: "config1:config1_global" },
|
24
|
+
config.globals
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_merge_globals_in_config2
|
29
|
+
config1 = ActiveRecordDoctor::Config.new({}, {})
|
30
|
+
config2 = ActiveRecordDoctor::Config.new(
|
31
|
+
{ config2_global: "config2:config2_global" },
|
32
|
+
{}
|
33
|
+
)
|
34
|
+
|
35
|
+
config = config1.merge(config2)
|
36
|
+
|
37
|
+
assert_equal(
|
38
|
+
{ config2_global: "config2:config2_global" },
|
39
|
+
config.globals
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_merge_globals_in_config1_and_config2
|
44
|
+
config1 = ActiveRecordDoctor::Config.new(
|
45
|
+
{
|
46
|
+
config1_global: "config1:config1_global",
|
47
|
+
shared_global: "config1:shared_global"
|
48
|
+
},
|
49
|
+
{}
|
50
|
+
)
|
51
|
+
config2 = ActiveRecordDoctor::Config.new(
|
52
|
+
{
|
53
|
+
config2_global: "config2:config2_global",
|
54
|
+
shared_global: "config2:shared_global"
|
55
|
+
},
|
56
|
+
{}
|
57
|
+
)
|
58
|
+
|
59
|
+
config = config1.merge(config2)
|
60
|
+
|
61
|
+
assert_equal(
|
62
|
+
{
|
63
|
+
config1_global: "config1:config1_global",
|
64
|
+
shared_global: "config2:shared_global",
|
65
|
+
config2_global: "config2:config2_global"
|
66
|
+
},
|
67
|
+
config.globals
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_merge_detectors
|
72
|
+
config1 = ActiveRecordDoctor::Config.new(
|
73
|
+
{},
|
74
|
+
{
|
75
|
+
config1_detector: {
|
76
|
+
config1_setting: "config1:config1_detector.config1_setting"
|
77
|
+
},
|
78
|
+
shared_detector: {
|
79
|
+
config1_setting: "config1:shared_detector.config1_setting",
|
80
|
+
shared_setting: "config1:shared_detector.shared_setting"
|
81
|
+
}
|
82
|
+
}
|
83
|
+
)
|
84
|
+
config2 = ActiveRecordDoctor::Config.new(
|
85
|
+
{},
|
86
|
+
{
|
87
|
+
config2_detector: {
|
88
|
+
config2_setting: "config2:config2_detector.config2_setting"
|
89
|
+
},
|
90
|
+
shared_detector: {
|
91
|
+
config2_setting: "config2:shared_detector.config2_setting",
|
92
|
+
shared_setting: "config2:shared_detector.shared_setting"
|
93
|
+
}
|
94
|
+
}
|
95
|
+
)
|
96
|
+
|
97
|
+
config = config1.merge(config2)
|
98
|
+
|
99
|
+
assert_equal(
|
100
|
+
{
|
101
|
+
config1_detector: {
|
102
|
+
config1_setting: "config1:config1_detector.config1_setting"
|
103
|
+
},
|
104
|
+
config2_detector: {
|
105
|
+
config2_setting: "config2:config2_detector.config2_setting"
|
106
|
+
},
|
107
|
+
shared_detector: {
|
108
|
+
config1_setting: "config1:shared_detector.config1_setting",
|
109
|
+
config2_setting: "config2:shared_detector.config2_setting",
|
110
|
+
shared_setting: "config2:shared_detector.shared_setting"
|
111
|
+
}
|
112
|
+
},
|
113
|
+
config.detectors
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
@@ -7,8 +7,7 @@ class ActiveRecordDoctor::Detectors::ExtraneousIndexesTest < Minitest::Test
|
|
7
7
|
end
|
8
8
|
|
9
9
|
assert_problems(<<OUTPUT)
|
10
|
-
|
11
|
-
index_users_on_id (is a primary key of users)
|
10
|
+
remove index_users_on_id - coincides with the primary key on the table
|
12
11
|
OUTPUT
|
13
12
|
end
|
14
13
|
|
@@ -22,8 +21,7 @@ OUTPUT
|
|
22
21
|
ActiveRecord::Base.connection.add_index :users, :email, name: "index_users_on_email"
|
23
22
|
|
24
23
|
assert_problems(<<OUTPUT)
|
25
|
-
|
26
|
-
index_users_on_email (can be handled by unique_index_on_users_email)
|
24
|
+
remove index_users_on_email - can be replaced by unique_index_on_users_email
|
27
25
|
OUTPUT
|
28
26
|
end
|
29
27
|
|
@@ -40,8 +38,7 @@ OUTPUT
|
|
40
38
|
end
|
41
39
|
|
42
40
|
assert_problems(<<OUTPUT)
|
43
|
-
|
44
|
-
index_users_on_last_name (can be handled by index_users_on_last_name_and_first_name_and_email, unique_index_on_users_last_name_and_first_name)
|
41
|
+
remove index_users_on_last_name - can be replaced by index_users_on_last_name_and_first_name_and_email or unique_index_on_users_last_name_and_first_name
|
45
42
|
OUTPUT
|
46
43
|
end
|
47
44
|
|
@@ -60,8 +57,134 @@ OUTPUT
|
|
60
57
|
ActiveRecord::Base.connection.add_index :users, [:last_name, :first_name]
|
61
58
|
|
62
59
|
assert_problems(<<OUTPUT)
|
63
|
-
|
64
|
-
index_users_on_last_name_and_first_name (can be handled by index_users_on_last_name_and_first_name_and_email, unique_index_on_users_last_name_and_first_name)
|
60
|
+
remove index_users_on_last_name_and_first_name - can be replaced by index_users_on_last_name_and_first_name_and_email or unique_index_on_users_last_name_and_first_name
|
65
61
|
OUTPUT
|
66
62
|
end
|
63
|
+
|
64
|
+
def test_not_covered_by_different_index_type
|
65
|
+
create_table(:users) do |t|
|
66
|
+
t.string :first_name
|
67
|
+
t.string :last_name
|
68
|
+
t.index [:last_name, :first_name], using: :btree
|
69
|
+
|
70
|
+
if mysql?
|
71
|
+
t.index :last_name, type: :fulltext
|
72
|
+
else
|
73
|
+
t.index :last_name, using: :hash
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
refute_problems
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_not_covered_by_partial_index
|
81
|
+
skip("MySQL doesn't support partial indexes") if mysql?
|
82
|
+
|
83
|
+
create_table(:users) do |t|
|
84
|
+
t.string :first_name
|
85
|
+
t.string :last_name
|
86
|
+
t.boolean :active
|
87
|
+
t.index [:last_name, :first_name], where: "active"
|
88
|
+
t.index :last_name
|
89
|
+
end
|
90
|
+
|
91
|
+
refute_problems
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_not_covered_with_different_opclasses
|
95
|
+
skip("ActiveRecord < 5.2 doesn't support operator classes") if ActiveRecord::VERSION::STRING < "5.2"
|
96
|
+
skip("MySQL doesn't support operator classes") if mysql?
|
97
|
+
|
98
|
+
create_table(:users) do |t|
|
99
|
+
t.string :first_name
|
100
|
+
t.string :last_name
|
101
|
+
t.index [:last_name, :first_name], opclass: :varchar_pattern_ops
|
102
|
+
t.index :last_name
|
103
|
+
end
|
104
|
+
|
105
|
+
refute_problems
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_config_ignore_tables
|
109
|
+
# The detector recognizes two kinds of errors and both must take
|
110
|
+
# ignore_tables into account. We trigger those errors by indexing the
|
111
|
+
# primary key (the first extraneous index) and then indexing email twice
|
112
|
+
# (index2... is the other extraneous index).
|
113
|
+
create_table(:users) do |t|
|
114
|
+
t.index :id
|
115
|
+
t.string :email
|
116
|
+
|
117
|
+
t.index :email, name: "index1_on_users_email"
|
118
|
+
t.index :email, name: "index2_on_users_email"
|
119
|
+
end
|
120
|
+
|
121
|
+
config_file(<<-CONFIG)
|
122
|
+
ActiveRecordDoctor.configure do |config|
|
123
|
+
config.detector :extraneous_indexes,
|
124
|
+
ignore_tables: ["users"]
|
125
|
+
end
|
126
|
+
CONFIG
|
127
|
+
|
128
|
+
refute_problems
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_config_global_ignore_tables
|
132
|
+
create_table(:users) do |t|
|
133
|
+
t.index :id
|
134
|
+
t.string :email
|
135
|
+
|
136
|
+
t.index :email, name: "index1_on_users_email"
|
137
|
+
t.index :email, name: "index2_on_users_email"
|
138
|
+
end
|
139
|
+
|
140
|
+
config_file(<<-CONFIG)
|
141
|
+
ActiveRecordDoctor.configure do |config|
|
142
|
+
config.global :ignore_tables, ["users"]
|
143
|
+
end
|
144
|
+
CONFIG
|
145
|
+
|
146
|
+
refute_problems
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_config_global_ignore_indexes
|
150
|
+
create_table(:users) do |t|
|
151
|
+
t.index :id
|
152
|
+
t.string :email
|
153
|
+
|
154
|
+
t.index :email, name: "index1_on_users_email"
|
155
|
+
t.index :email, name: "index2_on_users_email"
|
156
|
+
end
|
157
|
+
|
158
|
+
config_file(<<-CONFIG)
|
159
|
+
ActiveRecordDoctor.configure do |config|
|
160
|
+
config.global :ignore_indexes, [
|
161
|
+
"index1_on_users_email",
|
162
|
+
"index2_on_users_email",
|
163
|
+
"index_users_on_id",
|
164
|
+
]
|
165
|
+
end
|
166
|
+
CONFIG
|
167
|
+
|
168
|
+
refute_problems
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_config_detector_ignore_indexes
|
172
|
+
create_table(:users) do |t|
|
173
|
+
t.index :id
|
174
|
+
t.string :email
|
175
|
+
t.string :api_key
|
176
|
+
|
177
|
+
t.index :email, name: "index_on_users_email"
|
178
|
+
t.index [:email, :api_key], name: "index_on_users_email_and_api_key"
|
179
|
+
end
|
180
|
+
|
181
|
+
config_file(<<-CONFIG)
|
182
|
+
ActiveRecordDoctor.configure do |config|
|
183
|
+
config.detector :extraneous_indexes,
|
184
|
+
ignore_indexes: ["index_users_on_id", "index_on_users_email"]
|
185
|
+
end
|
186
|
+
CONFIG
|
187
|
+
|
188
|
+
refute_problems
|
189
|
+
end
|
67
190
|
end
|
@@ -12,10 +12,9 @@ class ActiveRecordDoctor::Detectors::IncorrectBooleanPresenceValidationTest < Mi
|
|
12
12
|
validates :email, :active, presence: true
|
13
13
|
end
|
14
14
|
|
15
|
-
assert_problems(
|
16
|
-
|
17
|
-
|
18
|
-
OUTPUT
|
15
|
+
assert_problems(<<~OUTPUT)
|
16
|
+
replace the `presence` validator on ModelFactory::Models::User.active with `inclusion` - `presence` can't be used on booleans
|
17
|
+
OUTPUT
|
19
18
|
end
|
20
19
|
|
21
20
|
def test_inclusion_is_not_reported
|
@@ -29,7 +28,51 @@ OUTPUT
|
|
29
28
|
end
|
30
29
|
|
31
30
|
def test_models_with_non_existent_tables_are_skipped
|
32
|
-
create_model(:
|
31
|
+
create_model(:User)
|
32
|
+
|
33
|
+
refute_problems
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_config_ignore_models
|
37
|
+
create_table(:users) do |t|
|
38
|
+
t.string :email, null: false
|
39
|
+
end.create_model
|
40
|
+
|
41
|
+
config_file(<<-CONFIG)
|
42
|
+
ActiveRecordDoctor.configure do |config|
|
43
|
+
config.detector :incorrect_boolean_presence_validation,
|
44
|
+
ignore_models: ["ModelFactory.User"]
|
45
|
+
end
|
46
|
+
CONFIG
|
47
|
+
|
48
|
+
refute_problems
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_global_ignore_models
|
52
|
+
create_table(:users) do |t|
|
53
|
+
t.string :email, null: false
|
54
|
+
end.create_model
|
55
|
+
|
56
|
+
config_file(<<-CONFIG)
|
57
|
+
ActiveRecordDoctor.configure do |config|
|
58
|
+
config.global :ignore_models, ["ModelFactory.User"]
|
59
|
+
end
|
60
|
+
CONFIG
|
61
|
+
|
62
|
+
refute_problems
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_config_ignore_attributes
|
66
|
+
create_table(:users) do |t|
|
67
|
+
t.string :email, null: false
|
68
|
+
end.create_model
|
69
|
+
|
70
|
+
config_file(<<-CONFIG)
|
71
|
+
ActiveRecordDoctor.configure do |config|
|
72
|
+
config.detector :incorrect_boolean_presence_validation,
|
73
|
+
ignore_attributes: ["ModelFactory.User.email"]
|
74
|
+
end
|
75
|
+
CONFIG
|
33
76
|
|
34
77
|
refute_problems
|
35
78
|
end
|
@@ -13,10 +13,9 @@ class ActiveRecordDoctor::Detectors::IncorrectDependentOptionTest < Minitest::Te
|
|
13
13
|
belongs_to :company
|
14
14
|
end
|
15
15
|
|
16
|
-
assert_problems(
|
17
|
-
|
18
|
-
|
19
|
-
OUTPUT
|
16
|
+
assert_problems(<<~OUTPUT)
|
17
|
+
use `dependent: :delete_all` or similar on ModelFactory::Models::Company.users - associated models have no validations and can be deleted in bulk
|
18
|
+
OUTPUT
|
20
19
|
end
|
21
20
|
|
22
21
|
def test_invoking_callbacks_does_not_suggest_delete_all
|
@@ -56,10 +55,9 @@ OUTPUT
|
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
59
|
-
assert_problems(
|
60
|
-
|
61
|
-
|
62
|
-
OUTPUT
|
58
|
+
assert_problems(<<~OUTPUT)
|
59
|
+
use `dependent: :destroy` or similar on ModelFactory::Models::Company.users - the associated model has callbacks that are currently skipped
|
60
|
+
OUTPUT
|
63
61
|
end
|
64
62
|
|
65
63
|
def test_invoking_callbacks_does_not_suggest_destroy
|
@@ -94,10 +92,125 @@ OUTPUT
|
|
94
92
|
belongs_to :company
|
95
93
|
end
|
96
94
|
|
97
|
-
assert_problems(
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
assert_problems(<<~OUTPUT)
|
96
|
+
use `dependent: :delete` or similar on ModelFactory::Models::Company.owner - the associated model 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.create_model do
|
103
|
+
has_many :users
|
104
|
+
end
|
105
|
+
|
106
|
+
create_table(:users) do |t|
|
107
|
+
t.references :company
|
108
|
+
end.create_model do
|
109
|
+
belongs_to :company, dependent: :destroy
|
110
|
+
end
|
111
|
+
|
112
|
+
assert_problems(<<~OUTPUT)
|
113
|
+
use `dependent: :delete` or similar on ModelFactory::Models::User.company - the associated model 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.create_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.create_model do
|
127
|
+
belongs_to :company, dependent: :destroy
|
128
|
+
end
|
129
|
+
|
130
|
+
create_table(:projects) do |t|
|
131
|
+
t.references :company
|
132
|
+
end.create_model do
|
133
|
+
belongs_to :company
|
134
|
+
end
|
135
|
+
|
136
|
+
assert_problems(<<~OUTPUT)
|
137
|
+
use `dependent: :delete` or similar on ModelFactory::Models::User.company - the associated model 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.create_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.create_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.create_model do
|
157
|
+
belongs_to :company
|
158
|
+
end
|
159
|
+
|
160
|
+
assert_problems(<<~OUTPUT)
|
161
|
+
use `dependent: :delete` or similar on ModelFactory::Models::User.company - the associated model 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.create_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.create_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.create_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 ModelFactory::Models::User.company - the associated model 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.create_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.create_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.create_model do
|
210
|
+
belongs_to :company
|
211
|
+
end
|
212
|
+
|
213
|
+
refute_problems
|
101
214
|
end
|
102
215
|
|
103
216
|
def test_no_dependent_suggests_nothing
|
@@ -114,4 +227,69 @@ OUTPUT
|
|
114
227
|
|
115
228
|
refute_problems
|
116
229
|
end
|
230
|
+
|
231
|
+
def test_config_ignore_models
|
232
|
+
create_table(:companies) do
|
233
|
+
end.create_model do
|
234
|
+
has_many :users, dependent: :destroy
|
235
|
+
end
|
236
|
+
|
237
|
+
create_table(:users) do |t|
|
238
|
+
t.references :companies
|
239
|
+
end.create_model do
|
240
|
+
belongs_to :company
|
241
|
+
end
|
242
|
+
|
243
|
+
config_file(<<-CONFIG)
|
244
|
+
ActiveRecordDoctor.configure do |config|
|
245
|
+
config.detector :incorrect_dependent_option,
|
246
|
+
ignore_models: ["ModelFactory::Models::Company"]
|
247
|
+
end
|
248
|
+
CONFIG
|
249
|
+
|
250
|
+
refute_problems
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_global_ignore_models
|
254
|
+
create_table(:companies) do
|
255
|
+
end.create_model do
|
256
|
+
has_many :users, dependent: :destroy
|
257
|
+
end
|
258
|
+
|
259
|
+
create_table(:users) do |t|
|
260
|
+
t.references :companies
|
261
|
+
end.create_model do
|
262
|
+
belongs_to :company
|
263
|
+
end
|
264
|
+
|
265
|
+
config_file(<<-CONFIG)
|
266
|
+
ActiveRecordDoctor.configure do |config|
|
267
|
+
config.global :ignore_models, ["ModelFactory::Models::Company"]
|
268
|
+
end
|
269
|
+
CONFIG
|
270
|
+
|
271
|
+
refute_problems
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_config_ignore_associations
|
275
|
+
create_table(:companies) do
|
276
|
+
end.create_model do
|
277
|
+
has_many :users, dependent: :destroy
|
278
|
+
end
|
279
|
+
|
280
|
+
create_table(:users) do |t|
|
281
|
+
t.references :companies
|
282
|
+
end.create_model do
|
283
|
+
belongs_to :company
|
284
|
+
end
|
285
|
+
|
286
|
+
config_file(<<-CONFIG)
|
287
|
+
ActiveRecordDoctor.configure do |config|
|
288
|
+
config.detector :incorrect_dependent_option,
|
289
|
+
ignore_associations: ["ModelFactory::Models::Company.users"]
|
290
|
+
end
|
291
|
+
CONFIG
|
292
|
+
|
293
|
+
refute_problems
|
294
|
+
end
|
117
295
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveRecordDoctor::Detectors::MismatchedForeignKeyTypeTest < Minitest::Test
|
4
|
+
def test_mismatched_foreign_key_type_is_reported
|
5
|
+
# MySQL does not allow foreign keys to have different type than paired primary keys
|
6
|
+
return if mysql?
|
7
|
+
|
8
|
+
create_table(:companies, id: :bigint)
|
9
|
+
create_table(:users) do |t|
|
10
|
+
t.references :company, foreign_key: true, type: :integer
|
11
|
+
end
|
12
|
+
|
13
|
+
assert_problems(<<~OUTPUT)
|
14
|
+
users.company_id references a column of different type - foreign keys should be of the same type as the referenced column
|
15
|
+
OUTPUT
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_matched_foreign_key_type_is_not_reported
|
19
|
+
create_table(:companies)
|
20
|
+
create_table(:users) do |t|
|
21
|
+
t.references :company, foreign_key: true
|
22
|
+
end
|
23
|
+
|
24
|
+
refute_problems
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_config_ignore_tables
|
28
|
+
# MySQL does not allow foreign keys to have different type than paired primary keys
|
29
|
+
return if mysql?
|
30
|
+
|
31
|
+
create_table(:companies, id: :bigint)
|
32
|
+
create_table(:users) do |t|
|
33
|
+
t.references :company, foreign_key: true, type: :integer
|
34
|
+
end
|
35
|
+
|
36
|
+
config_file(<<-CONFIG)
|
37
|
+
ActiveRecordDoctor.configure do |config|
|
38
|
+
config.detector :mismatched_foreign_key_type,
|
39
|
+
ignore_tables: ["users"]
|
40
|
+
end
|
41
|
+
CONFIG
|
42
|
+
|
43
|
+
refute_problems
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_global_ignore_tables
|
47
|
+
# MySQL does not allow foreign keys to have different type than paired primary keys
|
48
|
+
return if mysql?
|
49
|
+
|
50
|
+
create_table(:companies, id: :bigint)
|
51
|
+
create_table(:users) do |t|
|
52
|
+
t.references :company, foreign_key: true, type: :integer
|
53
|
+
end
|
54
|
+
|
55
|
+
config_file(<<-CONFIG)
|
56
|
+
ActiveRecordDoctor.configure do |config|
|
57
|
+
config.global :ignore_tables, ["users"]
|
58
|
+
end
|
59
|
+
CONFIG
|
60
|
+
|
61
|
+
refute_problems
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_config_ignore_columns
|
65
|
+
# MySQL does not allow foreign keys to have different type than paired primary keys
|
66
|
+
return if mysql?
|
67
|
+
|
68
|
+
create_table(:companies, id: :bigint)
|
69
|
+
create_table(:users) do |t|
|
70
|
+
t.references :company, foreign_key: true, type: :integer
|
71
|
+
end
|
72
|
+
|
73
|
+
config_file(<<-CONFIG)
|
74
|
+
ActiveRecordDoctor.configure do |config|
|
75
|
+
config.detector :mismatched_foreign_key_type,
|
76
|
+
ignore_columns: ["users.company_id"]
|
77
|
+
end
|
78
|
+
CONFIG
|
79
|
+
|
80
|
+
refute_problems
|
81
|
+
end
|
82
|
+
end
|