test_data 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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