test_data 0.0.1 → 0.0.2

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.
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