test_data 0.0.2 → 0.1.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/CHANGELOG.md +13 -0
- data/Gemfile.lock +15 -15
- data/LICENSE.txt +1 -6
- data/README.md +652 -276
- data/example/Gemfile.lock +73 -73
- data/example/test/integration/better_mode_switching_demo_test.rb +4 -0
- data/example/test/integration/parallel_boops_with_fixtures_test.rb +2 -2
- data/example/test/integration/parallel_boops_without_fixtures_test.rb +2 -2
- data/example/test/integration/rails_fixtures_double_load_test.rb +10 -0
- data/example/test/integration/rails_fixtures_override_test.rb +127 -0
- data/example/test/integration/transaction_committing_boops_test.rb +14 -3
- data/example/test/test_helper.rb +2 -2
- data/lib/generators/test_data/cable_yaml_generator.rb +18 -0
- data/lib/generators/test_data/database_yaml_generator.rb +2 -3
- data/lib/generators/test_data/environment_file_generator.rb +3 -0
- data/lib/generators/test_data/initializer_generator.rb +7 -0
- data/lib/generators/test_data/secrets_yaml_generator.rb +19 -0
- data/lib/generators/test_data/webpacker_yaml_generator.rb +3 -2
- data/lib/test_data.rb +5 -0
- data/lib/test_data/active_record_ext.rb +11 -0
- data/lib/test_data/config.rb +14 -2
- data/lib/test_data/configurators.rb +2 -0
- data/lib/test_data/configurators/cable_yaml.rb +25 -0
- data/lib/test_data/configurators/environment_file.rb +3 -2
- data/lib/test_data/configurators/initializer.rb +3 -2
- data/lib/test_data/configurators/secrets_yaml.rb +25 -0
- data/lib/test_data/configurators/webpacker_yaml.rb +4 -3
- data/lib/test_data/dumps_database.rb +24 -1
- data/lib/test_data/generator_support.rb +3 -0
- data/lib/test_data/loads_database_dumps.rb +1 -1
- data/lib/test_data/log.rb +19 -1
- data/lib/test_data/rake.rb +16 -6
- data/lib/test_data/statistics.rb +6 -1
- data/lib/test_data/transactional_data_loader.rb +156 -46
- data/lib/test_data/version.rb +1 -1
- data/script/test +11 -0
- data/test_data.gemspec +1 -1
- metadata +11 -3
data/lib/test_data/statistics.rb
CHANGED
@@ -4,7 +4,7 @@ module TestData
|
|
4
4
|
end
|
5
5
|
|
6
6
|
class Statistics
|
7
|
-
attr_reader :load_count, :truncate_count
|
7
|
+
attr_reader :load_count, :truncate_count, :load_rails_fixtures_count
|
8
8
|
|
9
9
|
def initialize
|
10
10
|
reset
|
@@ -18,9 +18,14 @@ module TestData
|
|
18
18
|
@truncate_count += 1
|
19
19
|
end
|
20
20
|
|
21
|
+
def count_load_rails_fixtures!
|
22
|
+
@load_rails_fixtures_count += 1
|
23
|
+
end
|
24
|
+
|
21
25
|
def reset
|
22
26
|
@load_count = 0
|
23
27
|
@truncate_count = 0
|
28
|
+
@load_rails_fixtures_count = 0
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
@@ -1,37 +1,132 @@
|
|
1
1
|
module TestData
|
2
|
-
def self.load
|
3
|
-
@
|
4
|
-
@
|
2
|
+
def self.load
|
3
|
+
@data_loader ||= TestData.config.use_transactional_data_loader ? TransactionalDataLoader.new : CommittingDataLoader.new
|
4
|
+
@data_loader.load
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.ensure_we_dont_mix_transactional_and_non_transactional_data_loaders!(use_transactions)
|
8
|
+
unless @data_loader.nil? || use_transactions == @data_loader.transactional?
|
9
|
+
raise Error.new("There is already a #{@data_loader.transactional? ? "transactional" : "non-transactional"} data loader in use, and test_data does not support mixing both types of loaders in a single process.")
|
10
|
+
end
|
5
11
|
end
|
6
12
|
|
7
13
|
def self.rollback(save_point_name = :after_data_load)
|
8
|
-
|
14
|
+
unless TestData.config.use_transactional_data_loader
|
15
|
+
raise Error.new("TestData.rollback requires config.use_transactional_data_loader = true")
|
16
|
+
end
|
17
|
+
@data_loader ||= TransactionalDataLoader.new
|
9
18
|
case save_point_name
|
10
19
|
when :before_data_load
|
11
|
-
@
|
20
|
+
@data_loader.rollback_to_before_data_load
|
12
21
|
when :after_data_load
|
13
|
-
@
|
22
|
+
@data_loader.rollback_to_after_data_load
|
14
23
|
when :after_data_truncate
|
15
|
-
@
|
24
|
+
@data_loader.rollback_to_after_data_truncate
|
25
|
+
when :after_load_rails_fixtures
|
26
|
+
@data_loader.rollback_to_after_load_rails_fixtures
|
16
27
|
else
|
17
|
-
raise Error.new("No known save point named '#{save_point_name}'. Valid values are: [:before_data_load, :after_data_load, :after_data_truncate]")
|
28
|
+
raise Error.new("No known save point named '#{save_point_name}'. Valid values are: [:before_data_load, :after_data_load, :after_data_truncate, :after_load_rails_fixtures]")
|
18
29
|
end
|
19
30
|
end
|
20
31
|
|
21
|
-
def self.truncate
|
22
|
-
@
|
23
|
-
@
|
32
|
+
def self.truncate
|
33
|
+
@data_loader ||= TestData.config.use_transactional_data_loader ? TransactionalDataLoader.new : CommittingDataLoader.new
|
34
|
+
@data_loader.truncate
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.load_rails_fixtures(test_instance)
|
38
|
+
if !test_instance.respond_to?(:setup_fixtures)
|
39
|
+
raise Error.new("'TestData.load_rails_fixtures' must be passed a test instance that has had ActiveRecord::TestFixtures mixed-in (e.g. `TestData.load_rails_fixtures(self)` in an ActiveSupport::TestCase `setup` block), but the provided argument does not respond to 'setup_fixtures'")
|
40
|
+
elsif !test_instance.respond_to?(:__test_data_gem_setup_fixtures)
|
41
|
+
raise Error.new("'TestData.load_rails_fixtures' depends on Rails' default fixture-loading behavior being disabled by calling 'TestData.prevent_rails_fixtures_from_loading_automatically!' as early as possible (e.g. near the top of your test_helper.rb), but it looks like it was never called.")
|
42
|
+
elsif !TestData.config.use_transactional_data_loader
|
43
|
+
raise Error.new("'TestData.load_rails_fixtures' requires config.use_transactional_data_loader = true")
|
44
|
+
end
|
45
|
+
@data_loader ||= TransactionalDataLoader.new
|
46
|
+
@data_loader.load_rails_fixtures(test_instance)
|
24
47
|
end
|
25
48
|
|
26
|
-
class
|
49
|
+
class AbstractDataLoader
|
27
50
|
def initialize
|
28
51
|
@config = TestData.config
|
29
52
|
@statistics = TestData.statistics
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def execute_data_load
|
58
|
+
search_path = execute("show search_path").first["search_path"]
|
59
|
+
connection.disable_referential_integrity do
|
60
|
+
execute(File.read(@config.data_dump_full_path))
|
61
|
+
end
|
62
|
+
execute <<~SQL
|
63
|
+
select pg_catalog.set_config('search_path', '#{search_path}', false)
|
64
|
+
SQL
|
65
|
+
@statistics.count_load!
|
66
|
+
end
|
67
|
+
|
68
|
+
def execute_data_truncate
|
69
|
+
connection.disable_referential_integrity do
|
70
|
+
execute("TRUNCATE TABLE #{tables_to_truncate.map { |t| connection.quote_table_name(t) }.join(", ")} #{"CASCADE" unless @config.truncate_these_test_data_tables.present?}")
|
71
|
+
end
|
72
|
+
@statistics.count_truncate!
|
73
|
+
end
|
74
|
+
|
75
|
+
def execute_load_rails_fixtures(test_instance)
|
76
|
+
test_instance.pre_loaded_fixtures = false
|
77
|
+
test_instance.use_transactional_tests = false
|
78
|
+
test_instance.__test_data_gem_setup_fixtures
|
79
|
+
@already_loaded_rails_fixtures[test_instance.class] = test_instance.instance_variable_get(:@loaded_fixtures)
|
80
|
+
@statistics.count_load_rails_fixtures!
|
81
|
+
end
|
82
|
+
|
83
|
+
def tables_to_truncate
|
84
|
+
if @config.truncate_these_test_data_tables.present?
|
85
|
+
@config.truncate_these_test_data_tables
|
86
|
+
else
|
87
|
+
@tables_to_truncate ||= IO.foreach(@config.data_dump_path).grep(/^INSERT INTO/) { |line|
|
88
|
+
line.match(/^INSERT INTO ([^\s]+)/)&.captures&.first
|
89
|
+
}.compact.uniq
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def connection
|
94
|
+
ActiveRecord::Base.connection
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def execute(sql)
|
100
|
+
connection.execute(sql)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class CommittingDataLoader < AbstractDataLoader
|
105
|
+
def transactional?
|
106
|
+
false
|
107
|
+
end
|
108
|
+
|
109
|
+
def load
|
110
|
+
execute_data_load
|
111
|
+
end
|
112
|
+
|
113
|
+
def truncate
|
114
|
+
execute_data_truncate
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class TransactionalDataLoader < AbstractDataLoader
|
119
|
+
def initialize
|
120
|
+
super
|
30
121
|
@save_points = []
|
122
|
+
@already_loaded_rails_fixtures = {}
|
123
|
+
end
|
124
|
+
|
125
|
+
def transactional?
|
126
|
+
true
|
31
127
|
end
|
32
128
|
|
33
|
-
def load
|
34
|
-
return execute_data_load unless transactions
|
129
|
+
def load
|
35
130
|
ensure_after_load_save_point_is_active_if_data_is_loaded!
|
36
131
|
return rollback_to_after_data_load if save_point_active?(:after_data_load)
|
37
132
|
|
@@ -62,8 +157,14 @@ module TestData
|
|
62
157
|
end
|
63
158
|
end
|
64
159
|
|
65
|
-
def
|
66
|
-
|
160
|
+
def rollback_to_after_load_rails_fixtures
|
161
|
+
if save_point_active?(:after_load_rails_fixtures)
|
162
|
+
rollback_save_point(:after_load_rails_fixtures)
|
163
|
+
create_save_point(:after_load_rails_fixtures)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def truncate
|
67
168
|
ensure_after_load_save_point_is_active_if_data_is_loaded!
|
68
169
|
ensure_after_truncate_save_point_is_active_if_data_is_truncated!
|
69
170
|
return rollback_to_after_data_truncate if save_point_active?(:after_data_truncate)
|
@@ -81,8 +182,8 @@ module TestData
|
|
81
182
|
# should expect that the existence of :after_data_truncate save point
|
82
183
|
# implies that it's safe to rollback to the :after_data_load save
|
83
184
|
# point; since tests run in random order, it's likely to happen
|
84
|
-
TestData.log.debug("TestData.truncate was called, but data was not loaded. Loading data before truncate to preserve the
|
85
|
-
load
|
185
|
+
TestData.log.debug("TestData.truncate was called, but data was not loaded. Loading data before truncate to preserve the transaction save point ordering")
|
186
|
+
load
|
86
187
|
end
|
87
188
|
|
88
189
|
execute_data_truncate
|
@@ -90,6 +191,26 @@ module TestData
|
|
90
191
|
create_save_point(:after_data_truncate)
|
91
192
|
end
|
92
193
|
|
194
|
+
# logic is beat-for-beat the same as #truncate just one step deeper down
|
195
|
+
# this rabbit hole…
|
196
|
+
def load_rails_fixtures(test_instance)
|
197
|
+
ensure_after_load_save_point_is_active_if_data_is_loaded!
|
198
|
+
ensure_after_truncate_save_point_is_active_if_data_is_truncated!
|
199
|
+
ensure_after_load_rails_fixtures_save_point_is_active_if_fixtures_are_loaded!
|
200
|
+
reset_rails_fixture_caches(test_instance)
|
201
|
+
return rollback_to_after_load_rails_fixtures if save_point_active?(:after_load_rails_fixtures) && @already_loaded_rails_fixtures[test_instance.class].present?
|
202
|
+
|
203
|
+
if save_point_active?(:after_data_truncate)
|
204
|
+
rollback_to_after_data_truncate
|
205
|
+
else
|
206
|
+
truncate
|
207
|
+
end
|
208
|
+
|
209
|
+
execute_load_rails_fixtures(test_instance)
|
210
|
+
record_ar_internal_metadata_that_rails_fixtures_are_loaded
|
211
|
+
create_save_point(:after_load_rails_fixtures)
|
212
|
+
end
|
213
|
+
|
93
214
|
private
|
94
215
|
|
95
216
|
def ensure_after_load_save_point_is_active_if_data_is_loaded!
|
@@ -130,32 +251,29 @@ module TestData
|
|
130
251
|
ActiveRecord::InternalMetadata.find_by(key: "test_data:truncated")&.value == "true"
|
131
252
|
end
|
132
253
|
|
133
|
-
def
|
134
|
-
|
135
|
-
|
136
|
-
|
254
|
+
def ensure_after_load_rails_fixtures_save_point_is_active_if_fixtures_are_loaded!
|
255
|
+
if !save_point_active?(:after_load_rails_fixtures) && ar_internal_metadata_shows_rails_fixtures_are_loaded?
|
256
|
+
TestData.log.debug "Rails Fixtures appears to have been loaded by test_data, but the :after_load_rails_fixtures save point was rolled back (and not by this gem). Recreating the :after_load_rails_fixtures save point"
|
257
|
+
create_save_point(:after_load_rails_fixtures)
|
137
258
|
end
|
138
|
-
execute <<~SQL
|
139
|
-
select pg_catalog.set_config('search_path', '#{search_path}', false)
|
140
|
-
SQL
|
141
|
-
@statistics.count_load!
|
142
259
|
end
|
143
260
|
|
144
|
-
def
|
145
|
-
|
146
|
-
|
261
|
+
def record_ar_internal_metadata_that_rails_fixtures_are_loaded
|
262
|
+
if ar_internal_metadata_shows_rails_fixtures_are_loaded?
|
263
|
+
TestData.log.warn "Attempted to record that test_data had loaded your Rails fixtures in ar_internal_metadata, but record already existed. Perhaps a previous test run committed the loading of your Rails fixtures?"
|
264
|
+
else
|
265
|
+
ActiveRecord::InternalMetadata.create!(key: "test_data:rails_fixtures_loaded", value: "true")
|
147
266
|
end
|
148
|
-
@statistics.count_truncate!
|
149
267
|
end
|
150
268
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
269
|
+
def ar_internal_metadata_shows_rails_fixtures_are_loaded?
|
270
|
+
ActiveRecord::InternalMetadata.find_by(key: "test_data:rails_fixtures_loaded")&.value == "true"
|
271
|
+
end
|
272
|
+
|
273
|
+
def reset_rails_fixture_caches(test_instance)
|
274
|
+
ActiveRecord::FixtureSet.reset_cache
|
275
|
+
test_instance.instance_variable_set(:@loaded_fixtures, @already_loaded_rails_fixtures[test_instance.class])
|
276
|
+
test_instance.instance_variable_set(:@fixture_cache, {})
|
159
277
|
end
|
160
278
|
|
161
279
|
def save_point_active?(name)
|
@@ -178,13 +296,5 @@ module TestData
|
|
178
296
|
save_point.active?
|
179
297
|
}
|
180
298
|
end
|
181
|
-
|
182
|
-
def execute(sql)
|
183
|
-
connection.execute(sql)
|
184
|
-
end
|
185
|
-
|
186
|
-
def connection
|
187
|
-
ActiveRecord::Base.connection
|
188
|
-
end
|
189
299
|
end
|
190
300
|
end
|
data/lib/test_data/version.rb
CHANGED
data/script/test
CHANGED
@@ -35,6 +35,9 @@ bin/rails test test/integration/updated_boops_test.rb
|
|
35
35
|
|
36
36
|
# Test a migration being added and run and an out-of-date dump being loaded
|
37
37
|
cp ../test/fixtures/20210418220133_add_beep_to_boops.rb db/migrate
|
38
|
+
cp ../test/fixtures/20210624180810_create_pants.rb db/migrate
|
39
|
+
cp ../test/fixtures/pant.rb app/models
|
40
|
+
cp ../test/fixtures/pants.yml test/fixtures
|
38
41
|
bin/rake db:migrate
|
39
42
|
bin/rake db:test:prepare
|
40
43
|
bin/rake test_data:drop_database
|
@@ -46,6 +49,12 @@ bin/rails test test/integration/migrated_boops_test.rb
|
|
46
49
|
# Run a test that commits test data thru to the database
|
47
50
|
bin/rails test test/integration/transaction_committing_boops_test.rb
|
48
51
|
|
52
|
+
# Run a test that prevents Rails fixtures for preloading and then loads them in a transaction
|
53
|
+
bin/rails test test/integration/rails_fixtures_override_test.rb
|
54
|
+
|
55
|
+
# Run a test that forgets to prevent Rails fixtures but then tries to load them in a transaction
|
56
|
+
bin/rails test test/integration/rails_fixtures_double_load_test.rb
|
57
|
+
|
49
58
|
# Add a second migration, this time without wiping the test_data db and with a table we want to ignore
|
50
59
|
cp ../test/fixtures/20210423114916_add_table_we_want_to_ignore.rb db/migrate
|
51
60
|
cp ../test/fixtures/chatty_audit_log.rb app/models
|
@@ -88,3 +97,5 @@ bin/rails test test/integration/boops_that_boop_boops_test.rb
|
|
88
97
|
# Cleanup
|
89
98
|
cd ..
|
90
99
|
./script/reset_example_app
|
100
|
+
|
101
|
+
echo "You win!"
|
data/test_data.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
|
13
13
|
spec.metadata["homepage_uri"] = spec.homepage
|
14
14
|
spec.metadata["source_code_uri"] = spec.homepage
|
15
|
-
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/
|
15
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
16
16
|
|
17
17
|
# Specify which files should be added to the gem when it is released.
|
18
18
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: test_data
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Searls
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -106,25 +106,33 @@ files:
|
|
106
106
|
- example/test/integration/mode_switching_demo_test.rb
|
107
107
|
- example/test/integration/parallel_boops_with_fixtures_test.rb
|
108
108
|
- example/test/integration/parallel_boops_without_fixtures_test.rb
|
109
|
+
- example/test/integration/rails_fixtures_double_load_test.rb
|
110
|
+
- example/test/integration/rails_fixtures_override_test.rb
|
109
111
|
- example/test/integration/transaction_committing_boops_test.rb
|
110
112
|
- example/test/integration/updated_boops_test.rb
|
111
113
|
- example/test/test_helper.rb
|
114
|
+
- lib/generators/test_data/cable_yaml_generator.rb
|
112
115
|
- lib/generators/test_data/database_yaml_generator.rb
|
113
116
|
- lib/generators/test_data/environment_file_generator.rb
|
114
117
|
- lib/generators/test_data/initializer_generator.rb
|
118
|
+
- lib/generators/test_data/secrets_yaml_generator.rb
|
115
119
|
- lib/generators/test_data/webpacker_yaml_generator.rb
|
116
120
|
- lib/test_data.rb
|
121
|
+
- lib/test_data/active_record_ext.rb
|
117
122
|
- lib/test_data/active_support_ext.rb
|
118
123
|
- lib/test_data/config.rb
|
119
124
|
- lib/test_data/configuration_verification.rb
|
120
125
|
- lib/test_data/configurators.rb
|
126
|
+
- lib/test_data/configurators/cable_yaml.rb
|
121
127
|
- lib/test_data/configurators/database_yaml.rb
|
122
128
|
- lib/test_data/configurators/environment_file.rb
|
123
129
|
- lib/test_data/configurators/initializer.rb
|
130
|
+
- lib/test_data/configurators/secrets_yaml.rb
|
124
131
|
- lib/test_data/configurators/webpacker_yaml.rb
|
125
132
|
- lib/test_data/detects_database_emptiness.rb
|
126
133
|
- lib/test_data/dumps_database.rb
|
127
134
|
- lib/test_data/error.rb
|
135
|
+
- lib/test_data/generator_support.rb
|
128
136
|
- lib/test_data/installs_configuration.rb
|
129
137
|
- lib/test_data/loads_database_dumps.rb
|
130
138
|
- lib/test_data/log.rb
|
@@ -144,7 +152,7 @@ licenses: []
|
|
144
152
|
metadata:
|
145
153
|
homepage_uri: https://github.com/testdouble/test_data
|
146
154
|
source_code_uri: https://github.com/testdouble/test_data
|
147
|
-
changelog_uri: https://github.com/testdouble/test_data/blob/
|
155
|
+
changelog_uri: https://github.com/testdouble/test_data/blob/main/CHANGELOG.md
|
148
156
|
post_install_message:
|
149
157
|
rdoc_options: []
|
150
158
|
require_paths:
|