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.
- checksums.yaml +4 -4
- data/README.md +27 -0
- 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/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 +2 -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
|