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.
- checksums.yaml +4 -4
- data/README.md +29 -2
- data/lib/active_record_doctor/config/loader.rb +1 -1
- data/lib/active_record_doctor/detectors/base.rb +11 -7
- data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +1 -1
- data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +9 -4
- data/lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb +1 -1
- data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +2 -2
- data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +16 -7
- data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +1 -0
- data/lib/active_record_doctor/logger/hierarchical.rb +1 -1
- data/lib/active_record_doctor/railtie.rb +1 -1
- data/lib/active_record_doctor/rake/task.rb +35 -3
- data/lib/active_record_doctor/runner.rb +1 -1
- data/lib/active_record_doctor/utils.rb +2 -2
- data/lib/active_record_doctor/version.rb +1 -1
- data/lib/tasks/active_record_doctor.rake +1 -2
- metadata +12 -49
- data/test/active_record_doctor/config/loader_test.rb +0 -120
- data/test/active_record_doctor/config_test.rb +0 -116
- data/test/active_record_doctor/detectors/disable_test.rb +0 -30
- data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +0 -277
- data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +0 -79
- data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +0 -511
- data/test/active_record_doctor/detectors/incorrect_length_validation_test.rb +0 -107
- data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +0 -116
- data/test/active_record_doctor/detectors/missing_foreign_keys_test.rb +0 -70
- data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +0 -273
- data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +0 -232
- data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +0 -496
- data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +0 -77
- data/test/active_record_doctor/detectors/undefined_table_references_test.rb +0 -55
- data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +0 -177
- data/test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb +0 -116
- data/test/active_record_doctor/runner_test.rb +0 -41
- data/test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb +0 -141
- data/test/setup.rb +0 -124
@@ -1,177 +0,0 @@
|
|
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
|
-
t.index [:last_name],
|
15
|
-
name: "index_deleted_profiles_on_last_name",
|
16
|
-
where: "deleted_at IS NOT NULL"
|
17
|
-
end
|
18
|
-
|
19
|
-
refute_problems
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_unindexed_deleted_at_is_reported
|
23
|
-
skip("MySQL doesn't support partial indexes") if mysql?
|
24
|
-
|
25
|
-
create_table(:users) do |t|
|
26
|
-
t.string :first_name
|
27
|
-
t.string :last_name
|
28
|
-
t.datetime :deleted_at
|
29
|
-
t.index [:first_name, :last_name],
|
30
|
-
name: "index_profiles_on_first_name_and_last_name"
|
31
|
-
end
|
32
|
-
|
33
|
-
assert_problems(<<~OUTPUT)
|
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
|
35
|
-
OUTPUT
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_indexed_discarded_at_is_not_reported
|
39
|
-
skip("MySQL doesn't support partial indexes") if mysql?
|
40
|
-
|
41
|
-
create_table(:users) do |t|
|
42
|
-
t.string :first_name
|
43
|
-
t.string :last_name
|
44
|
-
t.datetime :discarded_at
|
45
|
-
t.index [:first_name, :last_name],
|
46
|
-
name: "index_profiles_on_first_name_and_last_name",
|
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"
|
51
|
-
end
|
52
|
-
|
53
|
-
refute_problems
|
54
|
-
end
|
55
|
-
|
56
|
-
def test_unindexed_discarded_at_is_reported
|
57
|
-
skip("MySQL doesn't support partial indexes") if mysql?
|
58
|
-
|
59
|
-
create_table(:users) do |t|
|
60
|
-
t.string :first_name
|
61
|
-
t.string :last_name
|
62
|
-
t.datetime :discarded_at
|
63
|
-
t.index [:first_name, :last_name],
|
64
|
-
name: "index_profiles_on_first_name_and_last_name"
|
65
|
-
end
|
66
|
-
|
67
|
-
assert_problems(<<~OUTPUT)
|
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
|
69
|
-
OUTPUT
|
70
|
-
end
|
71
|
-
|
72
|
-
def test_config_ignore_tables
|
73
|
-
skip("MySQL doesn't support partial indexes") if mysql?
|
74
|
-
|
75
|
-
create_table(:users) do |t|
|
76
|
-
t.string :first_name
|
77
|
-
t.string :last_name
|
78
|
-
t.datetime :discarded_at
|
79
|
-
t.index [:first_name, :last_name],
|
80
|
-
name: "index_profiles_on_first_name_and_last_name"
|
81
|
-
end
|
82
|
-
|
83
|
-
config_file(<<-CONFIG)
|
84
|
-
ActiveRecordDoctor.configure do |config|
|
85
|
-
config.detector :unindexed_deleted_at,
|
86
|
-
ignore_tables: ["users"]
|
87
|
-
end
|
88
|
-
CONFIG
|
89
|
-
|
90
|
-
refute_problems
|
91
|
-
end
|
92
|
-
|
93
|
-
def test_global_ignore_tables
|
94
|
-
skip("MySQL doesn't support partial indexes") if mysql?
|
95
|
-
|
96
|
-
create_table(:users) do |t|
|
97
|
-
t.string :first_name
|
98
|
-
t.string :last_name
|
99
|
-
t.datetime :discarded_at
|
100
|
-
t.index [:first_name, :last_name],
|
101
|
-
name: "index_profiles_on_first_name_and_last_name"
|
102
|
-
end
|
103
|
-
|
104
|
-
config_file(<<-CONFIG)
|
105
|
-
ActiveRecordDoctor.configure do |config|
|
106
|
-
config.global :ignore_tables, ["users"]
|
107
|
-
end
|
108
|
-
CONFIG
|
109
|
-
|
110
|
-
refute_problems
|
111
|
-
end
|
112
|
-
|
113
|
-
def test_config_ignore_columns
|
114
|
-
skip("MySQL doesn't support partial indexes") if mysql?
|
115
|
-
|
116
|
-
create_table(:users) do |t|
|
117
|
-
t.string :first_name
|
118
|
-
t.string :last_name
|
119
|
-
t.datetime :discarded_at
|
120
|
-
t.index [:first_name, :last_name],
|
121
|
-
name: "index_profiles_on_first_name_and_last_name"
|
122
|
-
end
|
123
|
-
|
124
|
-
config_file(<<-CONFIG)
|
125
|
-
ActiveRecordDoctor.configure do |config|
|
126
|
-
config.detector :unindexed_deleted_at,
|
127
|
-
ignore_columns: ["users.discarded_at"]
|
128
|
-
end
|
129
|
-
CONFIG
|
130
|
-
|
131
|
-
refute_problems
|
132
|
-
end
|
133
|
-
|
134
|
-
def test_config_ignore_indexes
|
135
|
-
skip("MySQL doesn't support partial indexes") if mysql?
|
136
|
-
|
137
|
-
create_table(:users) do |t|
|
138
|
-
t.string :first_name
|
139
|
-
t.string :last_name
|
140
|
-
t.datetime :discarded_at
|
141
|
-
t.index [:first_name, :last_name],
|
142
|
-
name: "index_profiles_on_first_name_and_last_name"
|
143
|
-
end
|
144
|
-
|
145
|
-
config_file(<<-CONFIG)
|
146
|
-
ActiveRecordDoctor.configure do |config|
|
147
|
-
config.detector :unindexed_deleted_at,
|
148
|
-
ignore_indexes: ["index_profiles_on_first_name_and_last_name"]
|
149
|
-
end
|
150
|
-
CONFIG
|
151
|
-
|
152
|
-
refute_problems
|
153
|
-
end
|
154
|
-
|
155
|
-
def test_config_column_names
|
156
|
-
skip("MySQL doesn't support partial indexes") if mysql?
|
157
|
-
|
158
|
-
create_table(:users) do |t|
|
159
|
-
t.string :first_name
|
160
|
-
t.string :last_name
|
161
|
-
t.datetime :obliverated_at
|
162
|
-
t.index [:first_name, :last_name],
|
163
|
-
name: "index_profiles_on_first_name_and_last_name"
|
164
|
-
end
|
165
|
-
|
166
|
-
config_file(<<-CONFIG)
|
167
|
-
ActiveRecordDoctor.configure do |config|
|
168
|
-
config.detector :unindexed_deleted_at,
|
169
|
-
column_names: ["obliverated_at"]
|
170
|
-
end
|
171
|
-
CONFIG
|
172
|
-
|
173
|
-
assert_problems(<<~OUTPUT)
|
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
|
175
|
-
OUTPUT
|
176
|
-
end
|
177
|
-
end
|
@@ -1,116 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class ActiveRecordDoctor::Detectors::UnindexedForeignKeysTest < Minitest::Test
|
4
|
-
def test_unindexed_foreign_key_is_reported
|
5
|
-
skip("MySQL always indexes foreign keys") if mysql?
|
6
|
-
|
7
|
-
create_table(:companies)
|
8
|
-
create_table(:users) do |t|
|
9
|
-
t.references :company, foreign_key: true, index: false
|
10
|
-
end
|
11
|
-
|
12
|
-
assert_problems(<<~OUTPUT)
|
13
|
-
add an index on users(company_id) - foreign keys are often used in database lookups and should be indexed for performance reasons
|
14
|
-
OUTPUT
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_unindexed_foreign_key_with_nonstandard_name_is_reported
|
18
|
-
skip("MySQL always indexes foreign keys") if mysql?
|
19
|
-
|
20
|
-
create_table(:companies)
|
21
|
-
create_table(:users) do |t|
|
22
|
-
t.integer :company
|
23
|
-
t.foreign_key :companies, column: :company
|
24
|
-
end
|
25
|
-
|
26
|
-
assert_problems(<<~OUTPUT)
|
27
|
-
add an index on users(company) - foreign keys are often used in database lookups and should be indexed for performance reasons
|
28
|
-
OUTPUT
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_unindexed_polymorphic_foreign_key_is_reported
|
32
|
-
create_table(:notes) do |t|
|
33
|
-
t.integer :notable_id
|
34
|
-
t.string :notable_type
|
35
|
-
end
|
36
|
-
|
37
|
-
assert_problems(<<~OUTPUT)
|
38
|
-
add an index on notes(notable_type, notable_id) - foreign keys are often used in database lookups and should be indexed for performance reasons
|
39
|
-
OUTPUT
|
40
|
-
end
|
41
|
-
|
42
|
-
def test_indexed_polymorphic_foreign_key_is_not_reported
|
43
|
-
create_table(:notes) do |t|
|
44
|
-
t.string :title
|
45
|
-
t.integer :notable_id
|
46
|
-
t.string :notable_type
|
47
|
-
|
48
|
-
# Includes additional column except `notable`
|
49
|
-
t.index [:notable_type, :notable_id, :title]
|
50
|
-
end
|
51
|
-
|
52
|
-
refute_problems
|
53
|
-
end
|
54
|
-
|
55
|
-
def test_indexed_foreign_key_is_not_reported
|
56
|
-
create_table(:companies)
|
57
|
-
create_table(:users) do |t|
|
58
|
-
t.references :company, foreign_key: true, index: true
|
59
|
-
end
|
60
|
-
|
61
|
-
refute_problems
|
62
|
-
end
|
63
|
-
|
64
|
-
def test_config_ignore_tables
|
65
|
-
skip("MySQL always indexes foreign keys") if mysql?
|
66
|
-
|
67
|
-
create_table(:companies)
|
68
|
-
create_table(:users) do |t|
|
69
|
-
t.references :company, foreign_key: true, index: false
|
70
|
-
end
|
71
|
-
|
72
|
-
config_file(<<-CONFIG)
|
73
|
-
ActiveRecordDoctor.configure do |config|
|
74
|
-
config.detector :unindexed_foreign_keys,
|
75
|
-
ignore_tables: ["users"]
|
76
|
-
end
|
77
|
-
CONFIG
|
78
|
-
|
79
|
-
refute_problems
|
80
|
-
end
|
81
|
-
|
82
|
-
def test_global_ignore_tables
|
83
|
-
skip("MySQL always indexes foreign keys") if mysql?
|
84
|
-
|
85
|
-
create_table(:companies)
|
86
|
-
create_table(:users) do |t|
|
87
|
-
t.references :company, foreign_key: true, index: false
|
88
|
-
end
|
89
|
-
|
90
|
-
config_file(<<-CONFIG)
|
91
|
-
ActiveRecordDoctor.configure do |config|
|
92
|
-
config.global :ignore_tables, ["users"]
|
93
|
-
end
|
94
|
-
CONFIG
|
95
|
-
|
96
|
-
refute_problems
|
97
|
-
end
|
98
|
-
|
99
|
-
def test_config_ignore_columns
|
100
|
-
skip("MySQL always indexes foreign keys") if mysql?
|
101
|
-
|
102
|
-
create_table(:companies)
|
103
|
-
create_table(:users) do |t|
|
104
|
-
t.references :company, foreign_key: true, index: false
|
105
|
-
end
|
106
|
-
|
107
|
-
config_file(<<-CONFIG)
|
108
|
-
ActiveRecordDoctor.configure do |config|
|
109
|
-
config.detector :unindexed_foreign_keys,
|
110
|
-
ignore_columns: ["users.company_id"]
|
111
|
-
end
|
112
|
-
CONFIG
|
113
|
-
|
114
|
-
refute_problems
|
115
|
-
end
|
116
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class ActiveRecordDoctor::RunnerTest < Minitest::Test
|
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
|
12
|
-
|
13
|
-
def test_run_one_raises_on_unknown_detectors
|
14
|
-
assert_raises(KeyError) do
|
15
|
-
@runner.run_one(:performance_issues)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_run_all_returns_true_when_no_errors
|
20
|
-
assert(@runner.run_all)
|
21
|
-
assert(@io.string.blank?)
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_run_all_returns_false_when_errors
|
25
|
-
# Create a model without its underlying table to trigger an error.
|
26
|
-
define_model(:User)
|
27
|
-
|
28
|
-
refute(@runner.run_all)
|
29
|
-
refute(@io.string.blank?)
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_help_prints_help
|
33
|
-
ActiveRecordDoctor.detectors.each do |name, _|
|
34
|
-
@io.truncate(0)
|
35
|
-
|
36
|
-
@runner.help(name)
|
37
|
-
|
38
|
-
refute(@io.string.blank?, "expected help for #{name}")
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,141 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "rails/generators"
|
4
|
-
|
5
|
-
require "generators/active_record_doctor/add_indexes/add_indexes_generator"
|
6
|
-
|
7
|
-
class ActiveRecordDoctor::AddIndexesGeneratorTest < Minitest::Test
|
8
|
-
TIMESTAMP = Time.new(2021, 2, 1, 13, 15, 30)
|
9
|
-
|
10
|
-
def test_create_migrations
|
11
|
-
create_table(:notes) do |t|
|
12
|
-
t.integer :notable_id, null: false
|
13
|
-
t.string :notable_type, null: false
|
14
|
-
end
|
15
|
-
create_table(:users) do |t|
|
16
|
-
t.integer :organization_id, null: false
|
17
|
-
t.integer :account_id, null: false
|
18
|
-
end
|
19
|
-
create_table(:organizations) do |t|
|
20
|
-
t.integer :owner_id
|
21
|
-
end
|
22
|
-
|
23
|
-
Dir.mktmpdir do |dir|
|
24
|
-
Dir.chdir(dir)
|
25
|
-
|
26
|
-
path = File.join(dir, "indexes.txt")
|
27
|
-
File.write(path, <<~INDEXES)
|
28
|
-
add an index on users(organization_id) - foreign keys are often used in database lookups and should be indexed for performance reasons
|
29
|
-
add an index on users(account_id) - foreign keys are often used in database lookups and should be indexed for performance reasons
|
30
|
-
add an index on organizations(owner_id) - foreign keys are often used in database lookups and should be indexed for performance reasons
|
31
|
-
add an index on notes(notable_type, notable_id) - foreign keys are often used in database lookups and should be indexed for performance reasons
|
32
|
-
INDEXES
|
33
|
-
|
34
|
-
capture_io do
|
35
|
-
Time.stub(:now, TIMESTAMP) do
|
36
|
-
ActiveRecordDoctor::AddIndexesGenerator.start([path])
|
37
|
-
|
38
|
-
load(File.join("db", "migrate", "20210201131530_index_foreign_keys_in_users.rb"))
|
39
|
-
IndexForeignKeysInUsers.migrate(:up)
|
40
|
-
|
41
|
-
load(File.join("db", "migrate", "20210201131531_index_foreign_keys_in_organizations.rb"))
|
42
|
-
IndexForeignKeysInOrganizations.migrate(:up)
|
43
|
-
|
44
|
-
load(File.join("db", "migrate", "20210201131532_index_foreign_keys_in_notes.rb"))
|
45
|
-
IndexForeignKeysInNotes.migrate(:up)
|
46
|
-
|
47
|
-
::Object.send(:remove_const, :IndexForeignKeysInUsers)
|
48
|
-
::Object.send(:remove_const, :IndexForeignKeysInOrganizations)
|
49
|
-
::Object.send(:remove_const, :IndexForeignKeysInNotes)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
assert_indexes([
|
54
|
-
["notes", ["notable_type", "notable_id"]],
|
55
|
-
["users", ["organization_id"]],
|
56
|
-
["users", ["account_id"]],
|
57
|
-
["organizations", ["owner_id"]]
|
58
|
-
])
|
59
|
-
|
60
|
-
assert_equal(5, Dir.entries("./db/migrate").size)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def test_create_migrations_raises_when_malformed_inpout
|
65
|
-
Tempfile.create do |file|
|
66
|
-
file.write(<<~INDEXES)
|
67
|
-
add an index on users() - foreign keys are often used in database lookups and should be indexed for performance reasons
|
68
|
-
INDEXES
|
69
|
-
file.flush
|
70
|
-
|
71
|
-
assert_raises(RuntimeError) do
|
72
|
-
capture_io do
|
73
|
-
ActiveRecordDoctor::AddIndexesGenerator.start([file.path])
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def test_create_migrations_skips_blank_lines
|
80
|
-
Dir.mktmpdir do |dir|
|
81
|
-
Dir.chdir(dir)
|
82
|
-
|
83
|
-
path = File.join(dir, "indexes.txt")
|
84
|
-
File.write(path, "\n")
|
85
|
-
|
86
|
-
capture_io do
|
87
|
-
Time.stub(:now, TIMESTAMP) do
|
88
|
-
# Not raising an exception is considered success.
|
89
|
-
ActiveRecordDoctor::AddIndexesGenerator.start([path])
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def test_create_migrations_truncates_long_index_names
|
96
|
-
# Both the table and column names must be quite long. Otherwise, the
|
97
|
-
# we might reach table or column name length limits and fail to generate an
|
98
|
-
# index name that's long enough.
|
99
|
-
create_table(:organizations_migrated_from_legacy_app) do |t|
|
100
|
-
t.integer :legacy_owner_id_compatible_with_v1_to_v8
|
101
|
-
end
|
102
|
-
|
103
|
-
Dir.mktmpdir do |dir|
|
104
|
-
Dir.chdir(dir)
|
105
|
-
|
106
|
-
path = File.join(dir, "indexes.txt")
|
107
|
-
File.write(path, <<~INDEXES)
|
108
|
-
add an index on organizations_migrated_from_legacy_app(legacy_owner_id_compatible_with_v1_to_v8) - foreign keys are often used in database lookups and should be indexed for performance reasons
|
109
|
-
INDEXES
|
110
|
-
|
111
|
-
capture_io do
|
112
|
-
Time.stub(:now, TIMESTAMP) do
|
113
|
-
# If no exceptions are raised then we consider this to be a success.
|
114
|
-
ActiveRecordDoctor::AddIndexesGenerator.start([path])
|
115
|
-
|
116
|
-
load(File.join(
|
117
|
-
"db",
|
118
|
-
"migrate",
|
119
|
-
"20210201131530_index_foreign_keys_in_organizations_migrated_from_legacy_app.rb"
|
120
|
-
))
|
121
|
-
::IndexForeignKeysInOrganizationsMigratedFromLegacyApp.migrate(:up)
|
122
|
-
|
123
|
-
::Object.send(:remove_const, :IndexForeignKeysInOrganizationsMigratedFromLegacyApp)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
private
|
130
|
-
|
131
|
-
def assert_indexes(expected_indexes)
|
132
|
-
actual_indexes =
|
133
|
-
ActiveRecord::Base.connection.tables.map do |table|
|
134
|
-
ActiveRecord::Base.connection.indexes(table).map do |index|
|
135
|
-
[index.table, index.columns]
|
136
|
-
end
|
137
|
-
end.flatten(1)
|
138
|
-
|
139
|
-
assert_equal(expected_indexes.sort, actual_indexes.sort)
|
140
|
-
end
|
141
|
-
end
|
data/test/setup.rb
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Configure Active Record.
|
4
|
-
|
5
|
-
# We must import "uri" explicitly as otherwsie URI won't be accessible in
|
6
|
-
# Ruby 2.7.2 / Rails 6.
|
7
|
-
require "uri"
|
8
|
-
|
9
|
-
require "active_record"
|
10
|
-
require "pg"
|
11
|
-
require "mysql2"
|
12
|
-
|
13
|
-
adapter = ENV.fetch("DATABASE_ADAPTER")
|
14
|
-
ActiveRecord::Base.establish_connection(
|
15
|
-
adapter: adapter,
|
16
|
-
host: ENV["DATABASE_HOST"],
|
17
|
-
port: ENV["DATABASE_PORT"],
|
18
|
-
username: ENV["DATABASE_USERNAME"],
|
19
|
-
password: ENV["DATABASE_PASSWORD"],
|
20
|
-
database: "active_record_doctor_test"
|
21
|
-
)
|
22
|
-
|
23
|
-
puts "Using #{adapter}"
|
24
|
-
|
25
|
-
# We need to call #connection to enfore Active Record to actually establish
|
26
|
-
# the connection.
|
27
|
-
ActiveRecord::Base.connection
|
28
|
-
|
29
|
-
# Load Active Record Doctor.
|
30
|
-
require "active_record_doctor"
|
31
|
-
|
32
|
-
# Configure the test suite.
|
33
|
-
require "minitest"
|
34
|
-
require "minitest/autorun"
|
35
|
-
require "minitest/fork_executor"
|
36
|
-
require "transient_record"
|
37
|
-
|
38
|
-
# Filter out Minitest backtrace while allowing backtrace from other libraries
|
39
|
-
# to be shown.
|
40
|
-
Minitest.backtrace_filter = Minitest::BacktraceFilter.new
|
41
|
-
|
42
|
-
# Uncomment in case there's test case interference.
|
43
|
-
Minitest.parallel_executor = Minitest::ForkExecutor.new
|
44
|
-
|
45
|
-
# Prepare the test class.
|
46
|
-
class Minitest::Test
|
47
|
-
include TransientRecord
|
48
|
-
|
49
|
-
def setup
|
50
|
-
# Delete remnants (models and tables) of previous test case runs.
|
51
|
-
TransientRecord.cleanup
|
52
|
-
end
|
53
|
-
|
54
|
-
def teardown
|
55
|
-
@config_path = nil
|
56
|
-
|
57
|
-
if @previous_dir
|
58
|
-
Dir.chdir(@previous_dir)
|
59
|
-
@previous_dir = nil
|
60
|
-
end
|
61
|
-
|
62
|
-
# Ensure all remnants of previous test runs, most likely in form of tables,
|
63
|
-
# are removed.
|
64
|
-
TransientRecord.cleanup
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
attr_reader :config_path
|
70
|
-
|
71
|
-
def config_file(content)
|
72
|
-
@previous_dir = Dir.pwd
|
73
|
-
|
74
|
-
directory = Dir.mktmpdir("active_record_doctor")
|
75
|
-
@config_path = File.join(directory, ".active_record_doctor")
|
76
|
-
File.write(@config_path, content)
|
77
|
-
Dir.chdir(directory)
|
78
|
-
|
79
|
-
@config_path
|
80
|
-
end
|
81
|
-
|
82
|
-
def postgresql?
|
83
|
-
ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
84
|
-
end
|
85
|
-
|
86
|
-
def mysql?
|
87
|
-
ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
88
|
-
end
|
89
|
-
|
90
|
-
def detector_name
|
91
|
-
self.class.name.sub(/Test$/, "").demodulize.underscore.to_sym
|
92
|
-
end
|
93
|
-
|
94
|
-
def run_detector
|
95
|
-
io = StringIO.new
|
96
|
-
runner = ActiveRecordDoctor::Runner.new(
|
97
|
-
config: load_config,
|
98
|
-
logger: ActiveRecordDoctor::Logger::Dummy.new,
|
99
|
-
io: io
|
100
|
-
)
|
101
|
-
success = runner.run_one(detector_name)
|
102
|
-
[success, io.string]
|
103
|
-
end
|
104
|
-
|
105
|
-
def load_config
|
106
|
-
ActiveRecordDoctor.load_config_with_defaults(@config_path)
|
107
|
-
end
|
108
|
-
|
109
|
-
def assert_problems(expected_output)
|
110
|
-
success, output = run_detector
|
111
|
-
assert_equal(sort_lines(expected_output), sort_lines(output))
|
112
|
-
refute(success, "Expected the detector to return failure.")
|
113
|
-
end
|
114
|
-
|
115
|
-
def refute_problems(expected_output = "")
|
116
|
-
success, output = run_detector
|
117
|
-
assert_equal(sort_lines(expected_output), sort_lines(output))
|
118
|
-
assert(success, "Expected the detector to return success.")
|
119
|
-
end
|
120
|
-
|
121
|
-
def sort_lines(string)
|
122
|
-
string.split("\n").sort
|
123
|
-
end
|
124
|
-
end
|