test_data 0.1.0 → 0.3.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -5
  3. data/.standard.yml +2 -0
  4. data/CHANGELOG.md +38 -1
  5. data/Gemfile.lock +15 -15
  6. data/LICENSE.txt +1 -1
  7. data/README.md +701 -712
  8. data/example/.gitignore +1 -4
  9. data/example/Gemfile.lock +1 -1
  10. data/example/config/application.rb +3 -0
  11. data/example/config/credentials.yml.enc +1 -2
  12. data/example/spec/rails_helper.rb +1 -1
  13. data/example/spec/requests/boops_spec.rb +1 -5
  14. data/example/spec/requests/rails_fixtures_override_spec.rb +106 -0
  15. data/example/test/integration/better_mode_switching_demo_test.rb +2 -10
  16. data/example/test/integration/fixture_load_count_test.rb +82 -0
  17. data/example/test/integration/load_rollback_truncate_test.rb +40 -45
  18. data/example/test/integration/mode_switching_demo_test.rb +4 -14
  19. data/example/test/integration/parallel_boops_with_fixtures_test.rb +1 -5
  20. data/example/test/integration/parallel_boops_without_fixtures_test.rb +1 -5
  21. data/example/test/integration/rails_fixtures_double_load_test.rb +2 -2
  22. data/example/test/integration/rails_fixtures_override_test.rb +18 -35
  23. data/example/test/integration/test_data_hooks_test.rb +89 -0
  24. data/example/test/integration/transaction_committing_boops_test.rb +1 -10
  25. data/example/test/test_helper.rb +1 -5
  26. data/lib/generators/test_data/environment_file_generator.rb +4 -0
  27. data/lib/generators/test_data/initializer_generator.rb +19 -13
  28. data/lib/test_data/config.rb +30 -12
  29. data/lib/test_data/custom_loaders/abstract_base.rb +25 -0
  30. data/lib/test_data/custom_loaders/rails_fixtures.rb +45 -0
  31. data/lib/test_data/detects_database_existence.rb +19 -0
  32. data/lib/test_data/determines_databases_associated_dump_time.rb +13 -0
  33. data/lib/test_data/determines_when_sql_dump_was_made.rb +24 -0
  34. data/lib/test_data/dumps_database.rb +3 -0
  35. data/lib/test_data/inserts_test_data.rb +25 -0
  36. data/lib/test_data/manager.rb +187 -0
  37. data/lib/test_data/railtie.rb +4 -0
  38. data/lib/test_data/rake.rb +41 -12
  39. data/lib/test_data/records_dump_metadata.rb +9 -0
  40. data/lib/test_data/truncates_test_data.rb +31 -0
  41. data/lib/test_data/version.rb +1 -1
  42. data/lib/test_data/warns_if_database_is_newer_than_dump.rb +32 -0
  43. data/lib/test_data/warns_if_dump_is_newer_than_database.rb +36 -0
  44. data/lib/test_data.rb +43 -1
  45. data/script/reset_example_app +1 -0
  46. data/script/test +54 -6
  47. metadata +17 -3
  48. data/lib/test_data/transactional_data_loader.rb +0 -300
data/example/.gitignore CHANGED
@@ -18,7 +18,4 @@
18
18
  # Ignore master key for decrypting credentials and more.
19
19
  /config/master.key
20
20
 
21
- # Ignore files that are intentionally generated / asserted by the tests
22
- /config/environments/test_data.rb
23
- /test/support/test_data
24
- /db/migrate/20210418220133_add_beep_to_boops.rb
21
+
data/example/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- test_data (0.0.2)
4
+ test_data (0.3.0)
5
5
  railties (~> 6.0)
6
6
 
7
7
  GEM
@@ -24,6 +24,9 @@ module Example
24
24
  # Initialize configuration defaults for originally generated Rails version.
25
25
  config.load_defaults 6.1
26
26
 
27
+ # Uncomment this to switch from schema.rb to structure.sql
28
+ # config.active_record.schema_format = :sql
29
+
27
30
  # Configuration for the application, engines, and railties goes here.
28
31
  #
29
32
  # These settings can be overridden in specific environments using the files
@@ -1,2 +1 @@
1
- F/lkjSeOZwy+4IAYNV/+5sOwn9XOGjyNNvDkNyrpRG0kqoP8jmI4GE8mb73fdRIVoI9H7zefa3GxQ7R+udxqKWMLwAtSgTt+YGf5Eng0IzRBPp7hY2gIxFO0b84/+eG4Q6rD5xsg0kogzK3Ab4jj7f8Ci8vzFclSwqyUwP6TBCnaZJYsLMcNmF3tKBnWrJNKcQJ6Rj6/J2OH/nNi5x+KbsIgbARCJsZEK8PjD+cx8Qs9Cvk99Pj3VljLmjYA0L3jx1uCKahr0tzkkPkujBCKci05QHF/+vq0fcFduxc5wwlf5qMf7wGayz8i1YONyM9yORMrcnvSz/6AKC25np24+nmvZNsuVqtyoA4LRL+k3RBCMKo867FkwJmmGkhZSAFplFdQ2C0qJ+Gzem6UD6Lr70klDaUq1hITDqIc--nPRVkeqxjYlJDoKQ--Iu4hCDiJSpYuKL56Z/CUjA==
2
-
1
+ 5AstMrUHKXO5ed+i/hI/GnVm5IB525qXJOqPHvqfqhJSxEJSyfwKfQURigix3HsTcOwcPFIU6vPSC20bi5H8CQ7SHnYdHJuGB2T3WC9iiNiSBz49HaZoYX8ucHYoMMBbC+0u/6diYLW8ZTKR2Ab7bn4r1lkGzQjJe5clic5wNto7btjAbStXyAOHZCStXfnOFgQQhVjZno3iS7c279jaVYkYpZ12P5LbMxcJO4ipWfFexCzysIQu/WSPfmNMcvrIPijmPBvkVMJuhhDzd0yk1ysJu8jJMa8FJKx9aVd7Bes/k8Nywd04GDY6eSxyH6gkoG+BeQeqW3ig1XsuOWvx39qE9EQAcsgT13uxGc8M4wBKqIdSoibPl/79kTB843rnGlL8X/pKipJYczYJxazLSojwcQSto1UeIqQFWaDPQhz0--C7YAj7j4YQdWGiMG--JFTyHZbGKXiCdfZoBmF3xw==
@@ -32,7 +32,7 @@ rescue ActiveRecord::PendingMigrationError => e
32
32
  end
33
33
  RSpec.configure do |config|
34
34
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
35
- config.fixture_path = "#{::Rails.root}/spec/fixtures"
35
+ config.fixture_path = "#{::Rails.root}/test/fixtures"
36
36
 
37
37
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
38
38
  # examples within a transaction, remove the following line or assign false
@@ -2,11 +2,7 @@ require "rails_helper"
2
2
 
3
3
  RSpec.configure do |config|
4
4
  config.before(:each) do
5
- TestData.load
6
- end
7
-
8
- config.after(:each) do
9
- TestData.rollback
5
+ TestData.uses_test_data
10
6
  end
11
7
  end
12
8
 
@@ -0,0 +1,106 @@
1
+ require "rails_helper"
2
+
3
+ TestData.prevent_rails_fixtures_from_loading_automatically!
4
+
5
+ module TestDataModes
6
+ def uses(mode)
7
+ case mode
8
+ when :clean_slate
9
+ before(:each) { TestData.uses_clean_slate }
10
+ when :test_data
11
+ before(:each) { TestData.uses_test_data }
12
+ else
13
+ raise "Invalid test data mode: #{mode}"
14
+ end
15
+ end
16
+ end
17
+
18
+ RSpec.configure do |config|
19
+ config.extend(TestDataModes)
20
+ end
21
+
22
+ RSpec.describe "FixtureFreeTestData", type: :request do
23
+ fixtures :boops
24
+
25
+ uses :test_data
26
+ it "has 15 boops in the test_data" do
27
+ expect(Boop.count).to eq(15)
28
+ end
29
+ end
30
+
31
+ RSpec.describe "Clean Slate" do
32
+ uses :clean_slate
33
+
34
+ it "has no boops" do
35
+ expect(Boop.count).to eq(0)
36
+ end
37
+ end
38
+
39
+ RSpec.describe "FixturesUsingTest", type: :request do
40
+ fixtures :boops
41
+
42
+ before(:each) do
43
+ TestData.uses_rails_fixtures(self)
44
+ end
45
+
46
+ it "has_fixture_boops" do
47
+ expect(boops(:boop_1)).to be_persisted
48
+ expect(Boop.count).to eq(2)
49
+ end
50
+
51
+ it "does_not_get_the_other_fixture_accessor" do
52
+ expect { method(:pants) }.to raise_error(NameError)
53
+ end
54
+
55
+ it "even_explicitly_loading_test_data_will_truncate_and_then_load_fixtures" do
56
+ TestData.uses_test_data
57
+ TestData.uses_rails_fixtures(self)
58
+
59
+ expect(Boop.count).to eq(2)
60
+ end
61
+
62
+ it "load_and_rollback_leaves_them_as_is" do
63
+ boop = Boop.first
64
+ original_created_on = boop.created_at.to_date
65
+ a_year_ago = 1.year.ago.to_date
66
+
67
+ boop.update!(created_at: a_year_ago)
68
+
69
+ expect(Boop.find(boop.id).created_at.to_date).to eq(a_year_ago)
70
+
71
+ # Now after rollback
72
+ TestData.uses_rails_fixtures(self)
73
+
74
+ expect(Boop.find(boop.id).created_at.to_date).to eq(original_created_on)
75
+ end
76
+ end
77
+
78
+ RSpec.describe "SomeFixturesAndSomeTestDataInOneClassTest", type: :request do
79
+ fixtures :all
80
+
81
+ it "fixtures_work" do
82
+ TestData.uses_rails_fixtures(self)
83
+
84
+ expect(boops(:boop_1).updated_at.to_date).to eq(Date.civil(2020, 1, 1))
85
+ expect(pants(:pant_1).brand).to eq("Levi")
86
+ end
87
+
88
+ it "test_that_rewinds_to_test_data" do
89
+ TestData.uses_test_data
90
+
91
+ expect(Boop.count).to eq(15)
92
+ end
93
+
94
+ it "that_rewinds_to_the_very_start" do
95
+ TestData.uninitialize
96
+
97
+ expect(Boop.count).to eq(0)
98
+ end
99
+
100
+ it "fixtures_get_reloaded_because_cache_is_cleared" do
101
+ TestData.uses_rails_fixtures(self)
102
+
103
+ expect(boops(:boop_2).updated_at.to_date).to eq(Date.civil(2019, 1, 1))
104
+ expect(pants(:pant_2).brand).to eq("Wrangler")
105
+ end
106
+ end
@@ -8,19 +8,11 @@ class ActiveSupport::TestCase
8
8
  include FactoryBot::Syntax::Methods
9
9
 
10
10
  setup do
11
- TestData.truncate
12
- end
13
-
14
- teardown do
15
- TestData.rollback(:after_data_truncate)
11
+ TestData.uses_clean_slate
16
12
  end
17
13
  when :test_data
18
14
  setup do
19
- TestData.load
20
- end
21
-
22
- teardown do
23
- TestData.rollback
15
+ TestData.uses_test_data
24
16
  end
25
17
  end
26
18
  end
@@ -0,0 +1,82 @@
1
+ # Regression test to make sure we don't load fixtures too many times
2
+
3
+ class HookCounter
4
+ def self.count
5
+ @call_count || 0
6
+ end
7
+
8
+ def self.count!
9
+ @call_count ||= 0
10
+ @call_count += 1
11
+ end
12
+ end
13
+ at_exit do
14
+ if TestData.statistics.load_rails_fixtures_count > 2 # could be 1 if :all runs first, 2 if :boops only does
15
+ raise "Rails fixture load was called #{TestData.statistics.load_rails_fixtures_count} times, shouldn't be more than 2!"
16
+ end
17
+ if HookCounter.count > 2
18
+ raise "Rails fixture load hook was called #{HookCounter.count} times, shouldn't be more than 2!"
19
+ end
20
+ end
21
+
22
+ require "test_helper"
23
+
24
+ TestData.prevent_rails_fixtures_from_loading_automatically!
25
+
26
+ TestData.config do |config|
27
+ config.after_rails_fixture_load {
28
+ HookCounter.count!
29
+ }
30
+ end
31
+
32
+ class PartialFixtureTest < ActiveSupport::TestCase
33
+ fixtures :boops
34
+
35
+ setup do
36
+ TestData.uses_rails_fixtures(self)
37
+ end
38
+
39
+ def test_has_only_boops
40
+ assert boops(:boop_1)
41
+ assert_raises(NameError) { method(:pants) }
42
+ end
43
+ end
44
+
45
+ class AllFixtureTest < ActiveSupport::TestCase
46
+ fixtures :all
47
+
48
+ setup do
49
+ TestData.uses_rails_fixtures(self)
50
+ end
51
+
52
+ def test_has_both
53
+ assert boops(:boop_1)
54
+ assert pants(:pant_1)
55
+ end
56
+ end
57
+
58
+ class AllFixtureTest2 < ActiveSupport::TestCase
59
+ fixtures :all
60
+
61
+ setup do
62
+ TestData.uses_rails_fixtures(self)
63
+ end
64
+
65
+ def test_has_both
66
+ assert boops(:boop_1)
67
+ assert pants(:pant_1)
68
+ end
69
+ end
70
+
71
+ class AllFixtureTest3 < ActiveSupport::TestCase
72
+ fixtures :all
73
+
74
+ setup do
75
+ TestData.uses_rails_fixtures(self)
76
+ end
77
+
78
+ def test_has_both
79
+ assert boops(:boop_1)
80
+ assert pants(:pant_1)
81
+ end
82
+ end
@@ -14,7 +14,6 @@ class LoadRollbackTruncateTest < ActiveSupport::TestCase
14
14
  def teardown
15
15
  TestData.log.reset
16
16
  TestData.statistics.reset
17
- TestData.rollback(:before_data_load)
18
17
  end
19
18
 
20
19
  def test_loads_data_then_truncates_then_rolls_back_etc
@@ -22,101 +21,97 @@ class LoadRollbackTruncateTest < ActiveSupport::TestCase
22
21
  assert_equal 0, Boop.count
23
22
 
24
23
  # Now load the dump
25
- TestData.load
24
+ TestData.uses_test_data
26
25
  assert_equal 15, Boop.count
27
26
  Boop.create!
28
27
  assert_equal 16, Boop.count
29
28
 
30
29
  # Next, truncate the boops
31
- TestData.truncate
30
+ TestData.uses_clean_slate
32
31
  assert_equal 0, Boop.count
33
32
  Boop.create!
34
33
  assert_equal 1, Boop.count
35
34
 
36
35
  # Now roll back to _just after truncate_
37
- TestData.rollback(:after_data_truncate)
36
+ TestData.uses_clean_slate
38
37
  assert_equal 0, Boop.count
39
38
  Boop.create!
40
39
  assert_equal 1, Boop.count
41
40
 
42
41
  # Verify default rollback works after truncate
43
- TestData.rollback
42
+ TestData.uses_test_data
44
43
  assert_equal 15, Boop.count
45
44
 
46
45
  # Verify touching non-test-data tables will also be first rollbacked when truncate is called
47
- TestData.rollback(:before_data_load)
46
+ TestData.uninitialize
48
47
  good = ChattyAuditLog.create!(message: "I do belong here, because now we're at the start, prior to test_data's purview")
49
- TestData.load
48
+ TestData.uses_test_data
50
49
  bad = ChattyAuditLog.create!(message: "I won't belong here after truncate because I'm data that the truncate-calling test wouldn't expect")
51
50
  assert_equal 2, ChattyAuditLog.count
52
51
 
53
- TestData.truncate
52
+ TestData.uses_clean_slate
54
53
 
55
54
  assert_equal 1, ChattyAuditLog.count
56
55
  refute_nil ChattyAuditLog.find_by(id: good.id)
57
56
  assert_nil ChattyAuditLog.find_by(id: bad.id)
58
57
 
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
58
  # Warn but load anyway if rolled back to the start and then truncated
64
- TestData.rollback(:before_data_load)
65
- TestData.truncate
59
+ TestData.uninitialize
60
+ TestData.uses_clean_slate
66
61
  assert_equal :debug, @last_log.level
67
- assert_match "TestData.truncate was called, but data was not loaded. Loading data", @last_log.message
62
+ assert_match "TestData.uses_clean_slate was called, but data was not loaded. Loading data", @last_log.message
68
63
  assert_equal 0, Boop.count
69
- TestData.rollback
64
+ TestData.uses_test_data
70
65
  assert_equal 15, Boop.count
71
66
 
72
67
  # Chaos: try rolling back outside the gem (one level of extraneous rollback) and verify load recovers
73
- TestData.rollback(:before_data_load)
68
+ TestData.uninitialize
74
69
  TestData.statistics.reset
75
70
  assert_equal 0, TestData.statistics.load_count
76
- TestData.load
71
+ TestData.uses_test_data
77
72
  assert_equal 1, TestData.statistics.load_count
78
- TestData.load # Smart enough to not load again
73
+ TestData.uses_test_data # Smart enough to not load again
79
74
  assert_equal 1, TestData.statistics.load_count
80
75
  ActiveRecord::Base.connection.rollback_transaction # Someone might do this!
81
- TestData.load # Still smart enough to not do this
76
+ TestData.uses_test_data # Still smart enough to not do this
82
77
  assert_equal 1, TestData.statistics.load_count
83
- TestData.rollback # after load savepoint should have been healed with subsequent load call
78
+ TestData.uses_test_data # after load savepoint should have been healed with subsequent load call
84
79
  assert_equal 15, Boop.count
85
80
 
86
81
  # Chaos: try rolling back outside the gem (one level of extraneous rollback) and verify truncate recovers
87
- TestData.rollback(:before_data_load)
82
+ TestData.uninitialize
88
83
  TestData.statistics.reset
89
84
  assert_equal 0, TestData.statistics.truncate_count
90
- TestData.load
91
- TestData.truncate
85
+ TestData.uses_test_data
86
+ TestData.uses_clean_slate
92
87
  assert_equal 1, TestData.statistics.truncate_count
93
- TestData.truncate
88
+ TestData.uses_clean_slate
94
89
  assert_equal 1, TestData.statistics.truncate_count
95
90
  ActiveRecord::Base.connection.rollback_transaction # Someone might do this!
96
- TestData.truncate # Will recover, not take the bait
91
+ TestData.uses_clean_slate # Will recover, not take the bait
97
92
  assert_equal 1, TestData.statistics.truncate_count
98
- TestData.rollback(:after_data_truncate) # after truncate savepoint should have been healed with subsequent truncate call
93
+ TestData.uses_clean_slate # after truncate savepoint should have been healed with subsequent truncate call
99
94
  assert_equal 0, Boop.count
100
- TestData.rollback
95
+ TestData.uses_test_data
101
96
  assert_equal 15, Boop.count
102
97
 
103
98
  # Chaos: load data then call rollback two times and ensure we're still in a good spot
104
- TestData.rollback(:before_data_load)
99
+ TestData.uninitialize
105
100
  TestData.statistics.reset
106
- TestData.load
101
+ TestData.uses_test_data
107
102
  assert_equal 15, Boop.count
108
103
  2.times do # Two rollbacks means we're back at before_data_load
109
104
  ActiveRecord::Base.connection.rollback_transaction
110
105
  end
111
106
  assert_equal 0, Boop.count
112
- TestData.load # It should successfully load again a second time
107
+ TestData.uses_test_data # It should successfully load again a second time
113
108
  assert_equal 15, Boop.count
114
109
  assert_equal 2, TestData.statistics.load_count
115
110
 
116
111
  # Chaos: truncate data then call rollback two times and ensure we're still in a good spot
117
- TestData.rollback(:before_data_load)
112
+ TestData.uninitialize
118
113
  TestData.statistics.reset
119
- TestData.truncate # will warn-and-load and then truncate
114
+ TestData.uses_clean_slate # will warn-and-load and then truncate
120
115
  assert_equal 0, Boop.count
121
116
  2.times do # Two rollbacks means data is loaded but after_data_load savepoint has been lost
122
117
  ActiveRecord::Base.connection.rollback_transaction
@@ -124,14 +119,14 @@ class LoadRollbackTruncateTest < ActiveSupport::TestCase
124
119
  assert_equal 15, Boop.count
125
120
  assert_equal 1, TestData.statistics.load_count
126
121
  assert_equal 1, TestData.statistics.truncate_count
127
- TestData.truncate # should restore the lost after_data_load savepoint and re-truncate
122
+ TestData.uses_clean_slate # should restore the lost after_data_load savepoint and re-truncate
128
123
  3.times do # Three rollbacks means we are at before_data_load again
129
124
  ActiveRecord::Base.connection.rollback_transaction
130
125
  end
131
126
  assert_equal 0, Boop.count
132
- TestData.load
127
+ TestData.uses_test_data
133
128
  assert_equal 15, Boop.count
134
- TestData.truncate
129
+ TestData.uses_clean_slate
135
130
  assert_equal 0, Boop.count
136
131
  assert_equal 3, TestData.statistics.truncate_count
137
132
  assert_equal 2, TestData.statistics.load_count
@@ -140,17 +135,15 @@ class LoadRollbackTruncateTest < ActiveSupport::TestCase
140
135
  def test_suite_runs_different_tests_in_whatever_order
141
136
  # Imagine a test-datay test runs
142
137
  test_data_using_test = -> do
143
- TestData.load # before each
138
+ TestData.uses_test_data # before each
144
139
  Boop.create!
145
140
  assert_equal 16, Boop.count
146
- TestData.rollback # after each
147
141
  end
148
142
 
149
143
  test_data_avoiding_test = -> do
150
- TestData.truncate # before each
144
+ TestData.uses_clean_slate # before each
151
145
  Boop.create!
152
146
  assert_equal 1, Boop.count
153
- TestData.rollback(:after_data_truncate) # after each
154
147
  end
155
148
 
156
149
  # Run the tests separately:
@@ -170,23 +163,25 @@ class LoadRollbackTruncateTest < ActiveSupport::TestCase
170
163
  # In the interest of behaving similarly to .load, rollback in case the
171
164
  # previous test doesn't have an after_each as you might hope/expect
172
165
  3.times do
173
- TestData.truncate
166
+ TestData.uses_clean_slate
174
167
  Boop.create!
175
168
  assert_equal 1, Boop.count
176
169
  end
177
170
  end
178
171
 
179
172
  def test_other_rollbacks_mess_with_transaction_state_will_debug_you
180
- TestData.load
173
+ TestData.uninitialize
174
+ TestData.statistics.reset
175
+ TestData.uses_test_data
181
176
  ActiveRecord::Base.connection.rollback_transaction # data loaded, after_data_load save point destroyed
182
- TestData.load
177
+ TestData.uses_test_data
183
178
  assert_equal :debug, @last_log.level # debug only b/c rails fixtures will do this on every after_each if enabled
184
179
  assert_match "Recreating the :after_data_load save point", @last_log.message
185
180
  assert_equal 1, TestData.statistics.load_count
186
181
 
187
- TestData.truncate
182
+ TestData.uses_clean_slate
188
183
  ActiveRecord::Base.connection.rollback_transaction # data loaded, after_data_truncate save point destroyed
189
- TestData.truncate
184
+ TestData.uses_clean_slate
190
185
  assert_equal :debug, @last_log.level # debug only b/c rails fixtures will do this on every after_each if enabled
191
186
  assert_match "Recreating the :after_data_truncate save point", @last_log.message
192
187
  assert_equal 1, TestData.statistics.load_count