test_data 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +45 -0
  3. data/CHANGELOG.md +8 -1
  4. data/Gemfile.lock +5 -3
  5. data/README.md +961 -17
  6. data/example/Gemfile +3 -0
  7. data/example/Gemfile.lock +32 -3
  8. data/example/README.md +2 -22
  9. data/example/config/credentials.yml.enc +2 -1
  10. data/example/config/database.yml +2 -0
  11. data/example/spec/rails_helper.rb +64 -0
  12. data/example/spec/requests/boops_spec.rb +21 -0
  13. data/example/spec/spec_helper.rb +94 -0
  14. data/example/test/factories.rb +4 -0
  15. data/example/test/integration/better_mode_switching_demo_test.rb +45 -0
  16. data/example/test/integration/boops_that_boop_boops_test.rb +17 -0
  17. data/example/test/integration/dont_dump_tables_test.rb +7 -0
  18. data/example/test/integration/load_rollback_truncate_test.rb +195 -0
  19. data/example/test/integration/mode_switching_demo_test.rb +48 -0
  20. data/example/test/integration/parallel_boops_with_fixtures_test.rb +14 -0
  21. data/example/test/integration/parallel_boops_without_fixtures_test.rb +13 -0
  22. data/example/test/integration/transaction_committing_boops_test.rb +25 -0
  23. data/example/test/test_helper.rb +3 -26
  24. data/lib/generators/test_data/database_yaml_generator.rb +1 -1
  25. data/lib/generators/test_data/environment_file_generator.rb +0 -14
  26. data/lib/generators/test_data/initializer_generator.rb +38 -0
  27. data/lib/generators/test_data/webpacker_yaml_generator.rb +1 -1
  28. data/lib/test_data.rb +5 -0
  29. data/lib/test_data/config.rb +25 -2
  30. data/lib/test_data/configurators.rb +1 -0
  31. data/lib/test_data/configurators/initializer.rb +25 -0
  32. data/lib/test_data/dumps_database.rb +31 -4
  33. data/lib/test_data/loads_database_dumps.rb +7 -7
  34. data/lib/test_data/log.rb +58 -0
  35. data/lib/test_data/rake.rb +7 -5
  36. data/lib/test_data/save_point.rb +34 -0
  37. data/lib/test_data/statistics.rb +26 -0
  38. data/lib/test_data/transactional_data_loader.rb +145 -32
  39. data/lib/test_data/verifies_dumps_are_loadable.rb +4 -4
  40. data/lib/test_data/version.rb +1 -1
  41. data/script/reset_example_app +17 -0
  42. data/script/test +54 -13
  43. metadata +19 -2
data/example/Gemfile CHANGED
@@ -10,6 +10,9 @@ group :development, :test, :test_data do
10
10
  gem "test_data", path: ".."
11
11
  gem "standard"
12
12
  gem "pry-rails"
13
+ gem "factory_bot_rails", require: false
14
+ gem "noncommittal"
15
+ gem "rspec-rails"
13
16
  end
14
17
 
15
18
  group :test do
data/example/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- test_data (0.0.1)
4
+ test_data (0.0.2)
5
5
  railties (~> 6.0)
6
6
 
7
7
  GEM
@@ -84,7 +84,13 @@ GEM
84
84
  coderay (1.1.3)
85
85
  concurrent-ruby (1.1.8)
86
86
  crass (1.0.6)
87
+ diff-lcs (1.4.4)
87
88
  erubi (1.10.0)
89
+ factory_bot (6.1.0)
90
+ activesupport (>= 5.0.0)
91
+ factory_bot_rails (6.1.0)
92
+ factory_bot (~> 6.1.0)
93
+ railties (>= 5.0.0)
88
94
  globalid (0.4.2)
89
95
  activesupport (>= 4.2.0)
90
96
  i18n (1.8.10)
@@ -97,11 +103,14 @@ GEM
97
103
  marcel (1.0.1)
98
104
  method_source (1.0.0)
99
105
  mini_mime (1.0.3)
106
+ mini_portile2 (2.5.0)
100
107
  minitest (5.14.4)
101
108
  msgpack (1.4.2)
102
109
  nio4r (2.5.7)
103
- nokogiri (1.11.3-arm64-darwin)
110
+ nokogiri (1.11.3)
111
+ mini_portile2 (~> 2.5.0)
104
112
  racc (~> 1.4)
113
+ noncommittal (0.2.0)
105
114
  parallel (1.20.1)
106
115
  parser (3.0.1.0)
107
116
  ast (~> 2.4.1)
@@ -148,6 +157,23 @@ GEM
148
157
  rake (13.0.3)
149
158
  regexp_parser (2.1.1)
150
159
  rexml (3.2.5)
160
+ rspec-core (3.10.1)
161
+ rspec-support (~> 3.10.0)
162
+ rspec-expectations (3.10.1)
163
+ diff-lcs (>= 1.2.0, < 2.0)
164
+ rspec-support (~> 3.10.0)
165
+ rspec-mocks (3.10.2)
166
+ diff-lcs (>= 1.2.0, < 2.0)
167
+ rspec-support (~> 3.10.0)
168
+ rspec-rails (5.0.1)
169
+ actionpack (>= 5.2)
170
+ activesupport (>= 5.2)
171
+ railties (>= 5.2)
172
+ rspec-core (~> 3.10)
173
+ rspec-expectations (~> 3.10)
174
+ rspec-mocks (~> 3.10)
175
+ rspec-support (~> 3.10)
176
+ rspec-support (3.10.2)
151
177
  rubocop (1.12.1)
152
178
  parallel (~> 1.10)
153
179
  parser (>= 3.0.0.0)
@@ -193,15 +219,18 @@ GEM
193
219
  zeitwerk (2.4.2)
194
220
 
195
221
  PLATFORMS
196
- arm64-darwin-20
222
+ ruby
197
223
 
198
224
  DEPENDENCIES
199
225
  bootsnap
200
226
  capybara (>= 3.26)
227
+ factory_bot_rails
228
+ noncommittal
201
229
  pg
202
230
  pry-rails
203
231
  puma
204
232
  rails
233
+ rspec-rails
205
234
  selenium-webdriver
206
235
  standard
207
236
  test_data!
data/example/README.md CHANGED
@@ -1,24 +1,4 @@
1
1
  # README
2
2
 
3
- This README would normally document whatever steps are necessary to get the
4
- application up and running.
5
-
6
- Things you may want to cover:
7
-
8
- * Ruby version
9
-
10
- * System dependencies
11
-
12
- * Configuration
13
-
14
- * Database creation
15
-
16
- * Database initialization
17
-
18
- * How to run the test suite
19
-
20
- * Services (job queues, cache servers, search engines, etc.)
21
-
22
- * Deployment instructions
23
-
24
- * ...
3
+ This is an example app used by [script/test](/script/test) to exercise the
4
+ `test_data` gem
@@ -1 +1,2 @@
1
- F/lkjSeOZwy+4IAYNV/+5sOwn9XOGjyNNvDkNyrpRG0kqoP8jmI4GE8mb73fdRIVoI9H7zefa3GxQ7R+udxqKWMLwAtSgTt+YGf5Eng0IzRBPp7hY2gIxFO0b84/+eG4Q6rD5xsg0kogzK3Ab4jj7f8Ci8vzFclSwqyUwP6TBCnaZJYsLMcNmF3tKBnWrJNKcQJ6Rj6/J2OH/nNi5x+KbsIgbARCJsZEK8PjD+cx8Qs9Cvk99Pj3VljLmjYA0L3jx1uCKahr0tzkkPkujBCKci05QHF/+vq0fcFduxc5wwlf5qMf7wGayz8i1YONyM9yORMrcnvSz/6AKC25np24+nmvZNsuVqtyoA4LRL+k3RBCMKo867FkwJmmGkhZSAFplFdQ2C0qJ+Gzem6UD6Lr70klDaUq1hITDqIc--nPRVkeqxjYlJDoKQ--Iu4hCDiJSpYuKL56Z/CUjA==
1
+ F/lkjSeOZwy+4IAYNV/+5sOwn9XOGjyNNvDkNyrpRG0kqoP8jmI4GE8mb73fdRIVoI9H7zefa3GxQ7R+udxqKWMLwAtSgTt+YGf5Eng0IzRBPp7hY2gIxFO0b84/+eG4Q6rD5xsg0kogzK3Ab4jj7f8Ci8vzFclSwqyUwP6TBCnaZJYsLMcNmF3tKBnWrJNKcQJ6Rj6/J2OH/nNi5x+KbsIgbARCJsZEK8PjD+cx8Qs9Cvk99Pj3VljLmjYA0L3jx1uCKahr0tzkkPkujBCKci05QHF/+vq0fcFduxc5wwlf5qMf7wGayz8i1YONyM9yORMrcnvSz/6AKC25np24+nmvZNsuVqtyoA4LRL+k3RBCMKo867FkwJmmGkhZSAFplFdQ2C0qJ+Gzem6UD6Lr70klDaUq1hITDqIc--nPRVkeqxjYlJDoKQ--Iu4hCDiJSpYuKL56Z/CUjA==
2
+
@@ -20,6 +20,8 @@ default: &default
20
20
  # For details on connection pooling, see Rails configuration guide
21
21
  # https://guides.rubyonrails.org/configuring.html#database-pooling
22
22
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
23
+ host: <%= ENV.fetch("PGHOST") { "localhost" } %>
24
+ port: <%= ENV.fetch("PGPORT") { "5432" } %>
23
25
 
24
26
  development:
25
27
  <<: *default
@@ -0,0 +1,64 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ require "spec_helper"
3
+ ENV["RAILS_ENV"] ||= "test"
4
+ require File.expand_path("../config/environment", __dir__)
5
+ # Prevent database truncation if the environment is production
6
+ abort("The Rails environment is running in production mode!") if Rails.env.production?
7
+ require "rspec/rails"
8
+ # Add additional requires below this line. Rails is not loaded until this point!
9
+
10
+ # Requires supporting ruby files with custom matchers and macros, etc, in
11
+ # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
12
+ # run as spec files by default. This means that files in spec/support that end
13
+ # in _spec.rb will both be required and run as specs, causing the specs to be
14
+ # run twice. It is recommended that you do not name files matching this glob to
15
+ # end with _spec.rb. You can configure this pattern with the --pattern
16
+ # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
17
+ #
18
+ # The following line is provided for convenience purposes. It has the downside
19
+ # of increasing the boot-up time by auto-requiring all files in the support
20
+ # directory. Alternatively, in the individual `*_spec.rb` files, manually
21
+ # require only the support files necessary.
22
+ #
23
+ # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
24
+
25
+ # Checks for pending migrations and applies them before tests are run.
26
+ # If you are not using ActiveRecord, you can remove these lines.
27
+ begin
28
+ ActiveRecord::Migration.maintain_test_schema!
29
+ rescue ActiveRecord::PendingMigrationError => e
30
+ puts e.to_s.strip
31
+ exit 1
32
+ end
33
+ RSpec.configure do |config|
34
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
35
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
36
+
37
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
38
+ # examples within a transaction, remove the following line or assign false
39
+ # instead of true.
40
+ config.use_transactional_fixtures = true
41
+
42
+ # You can uncomment this line to turn off ActiveRecord support entirely.
43
+ # config.use_active_record = false
44
+
45
+ # RSpec Rails can automatically mix in different behaviours to your tests
46
+ # based on their file location, for example enabling you to call `get` and
47
+ # `post` in specs under `spec/controllers`.
48
+ #
49
+ # You can disable this behaviour by removing the line below, and instead
50
+ # explicitly tag your specs with their type, e.g.:
51
+ #
52
+ # RSpec.describe UsersController, type: :controller do
53
+ # # ...
54
+ # end
55
+ #
56
+ # The different available types are documented in the features, such as in
57
+ # https://relishapp.com/rspec/rspec-rails/docs
58
+ config.infer_spec_type_from_file_location!
59
+
60
+ # Filter lines from Rails gems in backtraces.
61
+ config.filter_rails_from_backtrace!
62
+ # arbitrary gems may also be filtered via:
63
+ # config.filter_gems_from_backtrace("gem name")
64
+ end
@@ -0,0 +1,21 @@
1
+ require "rails_helper"
2
+
3
+ RSpec.configure do |config|
4
+ config.before(:each) do
5
+ TestData.load
6
+ end
7
+
8
+ config.after(:each) do
9
+ TestData.rollback
10
+ end
11
+ end
12
+
13
+ RSpec.describe "Boops", type: :request do
14
+ 30.times do |i|
15
+ it "counts the boops ##{i}" do
16
+ expect(Boop.count).to eq(10)
17
+ Boop.create!
18
+ expect(Boop.count).to eq(11)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,94 @@
1
+ # This file was generated by the `rails generate rspec:install` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+ RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
47
+ # The settings below are suggested to provide a good initial experience
48
+ # with RSpec, but feel free to customize to your heart's content.
49
+ # # This allows you to limit a spec run to individual examples or groups
50
+ # # you care about by tagging them with `:focus` metadata. When nothing
51
+ # # is tagged with `:focus`, all examples get run. RSpec also provides
52
+ # # aliases for `it`, `describe`, and `context` that include `:focus`
53
+ # # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
54
+ # config.filter_run_when_matching :focus
55
+ #
56
+ # # Allows RSpec to persist some state between runs in order to support
57
+ # # the `--only-failures` and `--next-failure` CLI options. We recommend
58
+ # # you configure your source control system to ignore this file.
59
+ # config.example_status_persistence_file_path = "spec/examples.txt"
60
+ #
61
+ # # Limits the available syntax to the non-monkey patched syntax that is
62
+ # # recommended. For more details, see:
63
+ # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
64
+ # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
65
+ # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
66
+ # config.disable_monkey_patching!
67
+ #
68
+ # # Many RSpec users commonly either run the entire suite or an individual
69
+ # # file, and it's useful to allow more verbose output when running an
70
+ # # individual spec file.
71
+ # if config.files_to_run.one?
72
+ # # Use the documentation formatter for detailed output,
73
+ # # unless a formatter has already been configured
74
+ # # (e.g. via a command-line flag).
75
+ # config.default_formatter = "doc"
76
+ # end
77
+ #
78
+ # # Print the 10 slowest examples and example groups at the
79
+ # # end of the spec run, to help surface which specs are running
80
+ # # particularly slow.
81
+ # config.profile_examples = 10
82
+ #
83
+ # # Run specs in random order to surface order dependencies. If you find an
84
+ # # order dependency and want to debug it, you can fix the order by providing
85
+ # # the seed, which is printed after each run.
86
+ # # --seed 1234
87
+ # config.order = :random
88
+ #
89
+ # # Seed global randomization in this process using the `--seed` CLI option.
90
+ # # Setting this allows you to use `--seed` to deterministically reproduce
91
+ # # test failures related to randomization by passing the same `--seed` value
92
+ # # as the one that triggered the failure.
93
+ # Kernel.srand config.seed
94
+ end
@@ -0,0 +1,4 @@
1
+ FactoryBot.define do
2
+ factory :boop do
3
+ end
4
+ end
@@ -0,0 +1,45 @@
1
+ require "test_helper"
2
+
3
+ class ActiveSupport::TestCase
4
+ def self.test_data_mode(mode)
5
+ case mode
6
+ when :factory_bot
7
+ require "factory_bot_rails"
8
+ include FactoryBot::Syntax::Methods
9
+
10
+ setup do
11
+ TestData.truncate
12
+ end
13
+
14
+ teardown do
15
+ TestData.rollback(:after_data_truncate)
16
+ end
17
+ when :test_data
18
+ setup do
19
+ TestData.load
20
+ end
21
+
22
+ teardown do
23
+ TestData.rollback
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ class SomeFactoryUsingTest < ActiveSupport::TestCase
30
+ test_data_mode :factory_bot
31
+
32
+ def test_boops
33
+ create(:boop)
34
+
35
+ assert_equal 1, Boop.count
36
+ end
37
+ end
38
+
39
+ class SomeTestDataUsingTest < ActionDispatch::IntegrationTest
40
+ test_data_mode :test_data
41
+
42
+ def test_boops
43
+ assert_equal 10, Boop.count
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ require "test_helper"
2
+
3
+ class BoopsThatBoopBoopsTest < SerializedNonTransactionalTestCase
4
+ def test_each_of_the_boops_has_a_boop
5
+ assert_equal 15, Boop.count
6
+
7
+ Boop.find_each do |boop|
8
+ assert_kind_of Boop, boop.other_boop
9
+ end
10
+ end
11
+
12
+ def test_it_wont_let_you_assign_a_nonsensical_boop
13
+ assert_raise {
14
+ Boop.last.update!(other_boop_id: 2138012)
15
+ }
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ require "test_helper"
2
+
3
+ class DontDumpTablesTest < SerializedNonTransactionalTestCase
4
+ def test_dump_includes_zero_chatty_audit_logs
5
+ assert_equal 0, ChattyAuditLog.count
6
+ end
7
+ end
@@ -0,0 +1,195 @@
1
+ require "test_helper"
2
+
3
+ class LoadRollbackTruncateTest < ActiveSupport::TestCase
4
+ LogMessage = Struct.new(:message, :level, keyword_init: true)
5
+
6
+ def setup
7
+ @last_log = nil
8
+ TestData.log.level = :debug
9
+ TestData.log.writer = ->(message, level) {
10
+ @last_log = LogMessage.new(message: message, level: level)
11
+ }
12
+ end
13
+
14
+ def teardown
15
+ TestData.log.reset
16
+ TestData.statistics.reset
17
+ TestData.rollback(:before_data_load)
18
+ end
19
+
20
+ def test_loads_data_then_truncates_then_rolls_back_etc
21
+ # Check default state
22
+ assert_equal 0, Boop.count
23
+
24
+ # Now load the dump
25
+ TestData.load
26
+ assert_equal 15, Boop.count
27
+ Boop.create!
28
+ assert_equal 16, Boop.count
29
+
30
+ # Next, truncate the boops
31
+ TestData.truncate
32
+ assert_equal 0, Boop.count
33
+ Boop.create!
34
+ assert_equal 1, Boop.count
35
+
36
+ # Now roll back to _just after truncate_
37
+ TestData.rollback(:after_data_truncate)
38
+ assert_equal 0, Boop.count
39
+ Boop.create!
40
+ assert_equal 1, Boop.count
41
+
42
+ # Verify default rollback works after truncate
43
+ TestData.rollback
44
+ assert_equal 15, Boop.count
45
+
46
+ # Verify touching non-test-data tables will also be first rollbacked when truncate is called
47
+ TestData.rollback(:before_data_load)
48
+ good = ChattyAuditLog.create!(message: "I do belong here, because now we're at the start, prior to test_data's purview")
49
+ TestData.load
50
+ bad = ChattyAuditLog.create!(message: "I won't belong here after truncate because I'm data that the truncate-calling test wouldn't expect")
51
+ assert_equal 2, ChattyAuditLog.count
52
+
53
+ TestData.truncate
54
+
55
+ assert_equal 1, ChattyAuditLog.count
56
+ refute_nil ChattyAuditLog.find_by(id: good.id)
57
+ assert_nil ChattyAuditLog.find_by(id: bad.id)
58
+
59
+ # Verify rollbacking to some nonsense savepoint errors out:
60
+ error = assert_raise(TestData::Error) { TestData.rollback(:before_nonsense) }
61
+ assert_match "No known save point named 'before_nonsense'", error.message
62
+
63
+ # Warn but load anyway if rolled back to the start and then truncated
64
+ TestData.rollback(:before_data_load)
65
+ TestData.truncate
66
+ assert_equal :debug, @last_log.level
67
+ assert_match "TestData.truncate was called, but data was not loaded. Loading data", @last_log.message
68
+ assert_equal 0, Boop.count
69
+ TestData.rollback
70
+ assert_equal 15, Boop.count
71
+
72
+ # Chaos: try rolling back outside the gem (one level of extraneous rollback) and verify load recovers
73
+ TestData.rollback(:before_data_load)
74
+ TestData.statistics.reset
75
+ assert_equal 0, TestData.statistics.load_count
76
+ TestData.load
77
+ assert_equal 1, TestData.statistics.load_count
78
+ TestData.load # Smart enough to not load again
79
+ assert_equal 1, TestData.statistics.load_count
80
+ ActiveRecord::Base.connection.rollback_transaction # Someone might do this!
81
+ TestData.load # Still smart enough to not do this
82
+ assert_equal 1, TestData.statistics.load_count
83
+ TestData.rollback # after load savepoint should have been healed with subsequent load call
84
+ assert_equal 15, Boop.count
85
+
86
+ # Chaos: try rolling back outside the gem (one level of extraneous rollback) and verify truncate recovers
87
+ TestData.rollback(:before_data_load)
88
+ TestData.statistics.reset
89
+ assert_equal 0, TestData.statistics.truncate_count
90
+ TestData.load
91
+ TestData.truncate
92
+ assert_equal 1, TestData.statistics.truncate_count
93
+ TestData.truncate
94
+ assert_equal 1, TestData.statistics.truncate_count
95
+ ActiveRecord::Base.connection.rollback_transaction # Someone might do this!
96
+ TestData.truncate # Will recover, not take the bait
97
+ assert_equal 1, TestData.statistics.truncate_count
98
+ TestData.rollback(:after_data_truncate) # after truncate savepoint should have been healed with subsequent truncate call
99
+ assert_equal 0, Boop.count
100
+ TestData.rollback
101
+ assert_equal 15, Boop.count
102
+
103
+ # Chaos: load data then call rollback two times and ensure we're still in a good spot
104
+ TestData.rollback(:before_data_load)
105
+ TestData.statistics.reset
106
+ TestData.load
107
+ assert_equal 15, Boop.count
108
+ 2.times do # Two rollbacks means we're back at before_data_load
109
+ ActiveRecord::Base.connection.rollback_transaction
110
+ end
111
+ assert_equal 0, Boop.count
112
+ TestData.load # It should successfully load again a second time
113
+ assert_equal 15, Boop.count
114
+ assert_equal 2, TestData.statistics.load_count
115
+
116
+ # Chaos: truncate data then call rollback two times and ensure we're still in a good spot
117
+ TestData.rollback(:before_data_load)
118
+ TestData.statistics.reset
119
+ TestData.truncate # will warn-and-load and then truncate
120
+ assert_equal 0, Boop.count
121
+ 2.times do # Two rollbacks means data is loaded but after_data_load savepoint has been lost
122
+ ActiveRecord::Base.connection.rollback_transaction
123
+ end
124
+ assert_equal 15, Boop.count
125
+ assert_equal 1, TestData.statistics.load_count
126
+ assert_equal 1, TestData.statistics.truncate_count
127
+ TestData.truncate # should restore the lost after_data_load savepoint and re-truncate
128
+ 3.times do # Three rollbacks means we are at before_data_load again
129
+ ActiveRecord::Base.connection.rollback_transaction
130
+ end
131
+ assert_equal 0, Boop.count
132
+ TestData.load
133
+ assert_equal 15, Boop.count
134
+ TestData.truncate
135
+ assert_equal 0, Boop.count
136
+ assert_equal 3, TestData.statistics.truncate_count
137
+ assert_equal 2, TestData.statistics.load_count
138
+ end
139
+
140
+ def test_suite_runs_different_tests_in_whatever_order
141
+ # Imagine a test-datay test runs
142
+ test_data_using_test = -> do
143
+ TestData.load # before each
144
+ Boop.create!
145
+ assert_equal 16, Boop.count
146
+ TestData.rollback # after each
147
+ end
148
+
149
+ test_data_avoiding_test = -> do
150
+ TestData.truncate # before each
151
+ Boop.create!
152
+ assert_equal 1, Boop.count
153
+ TestData.rollback(:after_data_truncate) # after each
154
+ end
155
+
156
+ # Run the tests separately:
157
+ 3.times { test_data_using_test.call }
158
+ 3.times { test_data_avoiding_test.call }
159
+
160
+ # Mix and match the tests:
161
+ test_data_using_test.call
162
+ test_data_avoiding_test.call
163
+ test_data_using_test.call
164
+ test_data_avoiding_test.call
165
+ test_data_using_test.call
166
+ test_data_avoiding_test.call
167
+ end
168
+
169
+ def test_calling_truncate_multiple_times_will_return_you_to_truncated_state
170
+ # In the interest of behaving similarly to .load, rollback in case the
171
+ # previous test doesn't have an after_each as you might hope/expect
172
+ 3.times do
173
+ TestData.truncate
174
+ Boop.create!
175
+ assert_equal 1, Boop.count
176
+ end
177
+ end
178
+
179
+ def test_other_rollbacks_mess_with_transaction_state_will_debug_you
180
+ TestData.load
181
+ ActiveRecord::Base.connection.rollback_transaction # data loaded, after_data_load save point destroyed
182
+ TestData.load
183
+ assert_equal :debug, @last_log.level # debug only b/c rails fixtures will do this on every after_each if enabled
184
+ assert_match "Recreating the :after_data_load save point", @last_log.message
185
+ assert_equal 1, TestData.statistics.load_count
186
+
187
+ TestData.truncate
188
+ ActiveRecord::Base.connection.rollback_transaction # data loaded, after_data_truncate save point destroyed
189
+ TestData.truncate
190
+ assert_equal :debug, @last_log.level # debug only b/c rails fixtures will do this on every after_each if enabled
191
+ assert_match "Recreating the :after_data_truncate save point", @last_log.message
192
+ assert_equal 1, TestData.statistics.load_count
193
+ assert_equal 1, TestData.statistics.truncate_count
194
+ end
195
+ end