test_data 0.0.1 → 0.2.1

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +41 -0
  3. data/.standard.yml +2 -0
  4. data/CHANGELOG.md +43 -0
  5. data/Gemfile.lock +17 -15
  6. data/LICENSE.txt +1 -6
  7. data/README.md +1232 -17
  8. data/example/.gitignore +1 -4
  9. data/example/Gemfile +3 -0
  10. data/example/Gemfile.lock +100 -71
  11. data/example/README.md +2 -22
  12. data/example/config/application.rb +3 -0
  13. data/example/config/credentials.yml.enc +1 -1
  14. data/example/config/database.yml +2 -0
  15. data/example/spec/rails_helper.rb +64 -0
  16. data/example/spec/requests/boops_spec.rb +17 -0
  17. data/example/spec/requests/rails_fixtures_override_spec.rb +106 -0
  18. data/example/spec/spec_helper.rb +94 -0
  19. data/example/test/factories.rb +4 -0
  20. data/example/test/integration/better_mode_switching_demo_test.rb +41 -0
  21. data/example/test/integration/boops_that_boop_boops_test.rb +17 -0
  22. data/example/test/integration/dont_dump_tables_test.rb +7 -0
  23. data/example/test/integration/load_rollback_truncate_test.rb +190 -0
  24. data/example/test/integration/mode_switching_demo_test.rb +38 -0
  25. data/example/test/integration/parallel_boops_with_fixtures_test.rb +10 -0
  26. data/example/test/integration/parallel_boops_without_fixtures_test.rb +9 -0
  27. data/example/test/integration/rails_fixtures_double_load_test.rb +10 -0
  28. data/example/test/integration/rails_fixtures_override_test.rb +110 -0
  29. data/example/test/integration/test_data_hooks_test.rb +89 -0
  30. data/example/test/integration/transaction_committing_boops_test.rb +27 -0
  31. data/example/test/test_helper.rb +4 -31
  32. data/lib/generators/test_data/cable_yaml_generator.rb +18 -0
  33. data/lib/generators/test_data/database_yaml_generator.rb +3 -4
  34. data/lib/generators/test_data/environment_file_generator.rb +7 -14
  35. data/lib/generators/test_data/initializer_generator.rb +51 -0
  36. data/lib/generators/test_data/secrets_yaml_generator.rb +19 -0
  37. data/lib/generators/test_data/webpacker_yaml_generator.rb +4 -3
  38. data/lib/test_data.rb +42 -1
  39. data/lib/test_data/active_record_ext.rb +11 -0
  40. data/lib/test_data/config.rb +57 -4
  41. data/lib/test_data/configurators.rb +3 -0
  42. data/lib/test_data/configurators/cable_yaml.rb +25 -0
  43. data/lib/test_data/configurators/environment_file.rb +3 -2
  44. data/lib/test_data/configurators/initializer.rb +26 -0
  45. data/lib/test_data/configurators/secrets_yaml.rb +25 -0
  46. data/lib/test_data/configurators/webpacker_yaml.rb +4 -3
  47. data/lib/test_data/custom_loaders/abstract_base.rb +25 -0
  48. data/lib/test_data/custom_loaders/rails_fixtures.rb +42 -0
  49. data/lib/test_data/dumps_database.rb +55 -5
  50. data/lib/test_data/generator_support.rb +3 -0
  51. data/lib/test_data/inserts_test_data.rb +25 -0
  52. data/lib/test_data/loads_database_dumps.rb +8 -8
  53. data/lib/test_data/log.rb +76 -0
  54. data/lib/test_data/manager.rb +187 -0
  55. data/lib/test_data/rake.rb +20 -9
  56. data/lib/test_data/save_point.rb +34 -0
  57. data/lib/test_data/statistics.rb +31 -0
  58. data/lib/test_data/truncates_test_data.rb +31 -0
  59. data/lib/test_data/verifies_dumps_are_loadable.rb +4 -4
  60. data/lib/test_data/version.rb +1 -1
  61. data/script/reset_example_app +18 -0
  62. data/script/test +78 -13
  63. data/test_data.gemspec +1 -1
  64. metadata +36 -4
  65. data/lib/test_data/transactional_data_loader.rb +0 -77
@@ -0,0 +1,31 @@
1
+ module TestData
2
+ class TruncatesTestData
3
+ def initialize
4
+ @config = TestData.config
5
+ @statistics = TestData.statistics
6
+ end
7
+
8
+ def call
9
+ connection.disable_referential_integrity do
10
+ connection.execute("TRUNCATE TABLE #{tables_to_truncate.map { |t| connection.quote_table_name(t) }.join(", ")} #{"CASCADE" unless @config.truncate_these_test_data_tables.present?}")
11
+ end
12
+ @statistics.count_truncate!
13
+ end
14
+
15
+ private
16
+
17
+ def tables_to_truncate
18
+ if @config.truncate_these_test_data_tables.present?
19
+ @config.truncate_these_test_data_tables
20
+ else
21
+ @tables_to_truncate ||= IO.foreach(@config.data_dump_path).grep(/^INSERT INTO/) { |line|
22
+ line.match(/^INSERT INTO ([^\s]+)/)&.captures&.first
23
+ }.compact.uniq
24
+ end
25
+ end
26
+
27
+ def connection
28
+ ActiveRecord::Base.connection
29
+ end
30
+ end
31
+ end
@@ -8,22 +8,22 @@ module TestData
8
8
  def call(quiet: false)
9
9
  schema_dump_looks_good = Pathname.new(@config.schema_dump_full_path).readable?
10
10
  if !quiet && !schema_dump_looks_good
11
- warn "Warning: Database schema dump '#{@config.schema_dump_path}' not readable"
11
+ log.warn "Warning: Database schema dump '#{@config.schema_dump_path}' not readable"
12
12
  end
13
13
 
14
14
  data_dump_looks_good = Pathname.new(@config.data_dump_full_path).readable?
15
15
  if !quiet && !data_dump_looks_good
16
- warn "Warning: Database data dump '#{@config.data_dump_path}' not readable"
16
+ log.warn "Warning: Database data dump '#{@config.data_dump_path}' not readable"
17
17
  end
18
18
 
19
19
  non_test_data_dump_looks_good = Pathname.new(@config.non_test_data_dump_full_path).readable?
20
20
  if !quiet && !non_test_data_dump_looks_good
21
- warn "Warning: Database non-test data dump '#{@config.non_test_data_dump_path}' not readable"
21
+ log.warn "Warning: Database non-test data dump '#{@config.non_test_data_dump_path}' not readable"
22
22
  end
23
23
 
24
24
  database_empty = @detects_database_emptiness.empty?
25
25
  unless quiet || database_empty
26
- warn "Warning: Database '#{@config.database_name}' is not empty"
26
+ log.warn "Warning: Database '#{@config.database_name}' is not empty"
27
27
  end
28
28
 
29
29
  [
@@ -1,3 +1,3 @@
1
1
  module TestData
2
- VERSION = "0.0.1"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+
3
+ PS4='[script/test:${LINENO}] $ '
4
+ set -euo pipefail
5
+ set -x
6
+
7
+ cd example
8
+
9
+ # Reset database:
10
+ bin/rake db:drop
11
+ dropdb example_test_data 2>/dev/null || true
12
+
13
+ # Reset files:
14
+ git checkout app/models/boop.rb
15
+ git checkout config/application.rb
16
+ git checkout config/database.yml
17
+ git checkout db/schema.rb
18
+ git clean -xdf .
data/script/test CHANGED
@@ -1,25 +1,30 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
3
  PS4='[script/test:${LINENO}] $ '
4
- set -e
4
+ set -euo pipefail
5
5
  set -x
6
6
 
7
- # Make sure the main project installs & passes its own rake
7
+ # Install deps and make sure gem passes its own rake
8
8
  bundle
9
9
  bundle exec rake
10
-
11
- # Exercise the example app
12
10
  cd example
13
11
  bundle
14
12
 
15
13
  # Avoid test pollution by clearing out any initial state that might be lingering
16
- rm -rf test/support/test_data
17
- bin/rake db:reset
14
+ cd ..
15
+ ./script/reset_example_app
16
+
17
+ # Exercise the example app
18
+ cd example
19
+ bin/rake db:setup
18
20
 
19
21
  # Test basic initial usage
20
22
  bin/rake test_data:install
21
23
  bin/rake test_data:dump
22
24
  bin/rails test test/integration/basic_boops_test.rb
25
+ bundle exec rspec spec/requests/boops_spec.rb
26
+ bin/rails test test/integration/mode_switching_demo_test.rb
27
+ bin/rails test test/integration/better_mode_switching_demo_test.rb
23
28
  bin/rails test test/integration/parallel_boops_with_fixtures_test.rb
24
29
  bin/rails test test/integration/parallel_boops_without_fixtures_test.rb
25
30
 
@@ -30,6 +35,9 @@ bin/rails test test/integration/updated_boops_test.rb
30
35
 
31
36
  # Test a migration being added and run and an out-of-date dump being loaded
32
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
33
41
  bin/rake db:migrate
34
42
  bin/rake db:test:prepare
35
43
  bin/rake test_data:drop_database
@@ -38,12 +46,69 @@ RAILS_ENV=test_data bin/rake db:migrate
38
46
  bin/rake test_data:dump
39
47
  bin/rails test test/integration/migrated_boops_test.rb
40
48
 
41
- # Cleanup
49
+ # Run a test that commits test data thru to the database
50
+ bin/rails test test/integration/transaction_committing_boops_test.rb
42
51
 
43
- # Delete the test_data database
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
+ bundle exec rspec spec/requests/rails_fixtures_override_spec.rb
55
+
56
+ # Run a test that forgets to prevent Rails fixtures but then tries to load them in a transaction
57
+ bin/rails test test/integration/rails_fixtures_double_load_test.rb
58
+
59
+ # Add a second migration, this time without wiping the test_data db and with a table we want to ignore
60
+ cp ../test/fixtures/20210423114916_add_table_we_want_to_ignore.rb db/migrate
61
+ cp ../test/fixtures/chatty_audit_log.rb app/models
62
+ bin/rake db:migrate
63
+ RAILS_ENV=test_data bin/rake db:migrate
64
+ RAILS_ENV=test_data rails runner "50.times { ChattyAuditLog.create!(message: 'none of this matters') }"
65
+ # Gsub config file and uncomment + add table to excluded table list
66
+ ruby -e '
67
+ path = "config/initializers/test_data.rb"
68
+ IO.write(path, File.open(path) { |f|
69
+ f.read.gsub("# config.dont_dump_these_tables = []", "config.dont_dump_these_tables = [\"chatty_audit_logs\"]")
70
+ })
71
+ '
72
+ bin/rake test_data:dump
73
+ if grep -q "INSERT INTO public.chatty_audit_logs" "test/support/test_data/data.sql"; then
74
+ echo "Dump contained excluded table 'chatty_audit_logs'"
75
+ exit 1
76
+ fi
77
+ bin/rake db:test:prepare
78
+ bin/rails test test/integration/dont_dump_tables_test.rb
79
+ bin/rails test test/integration/load_rollback_truncate_test.rb
80
+
81
+ # Test circular FK constraints
82
+ cp ../test/fixtures/20210423190737_add_foreign_keys.rb db/migrate/
83
+ cp ../test/fixtures/boop_with_other_boops.rb app/models/boop.rb
84
+ RAILS_ENV=test_data bin/rake db:migrate
85
+ bin/rake test_data:dump
86
+ bin/rake db:migrate db:test:prepare
87
+ bin/rails test test/integration/boops_that_boop_boops_test.rb
88
+
89
+ # Make sure it loads cleanly again
44
90
  bin/rake test_data:drop_database
45
- # Unset file changes
46
- git checkout config/database.yml
47
- git checkout db/schema.rb
48
- # Delete all untracked files (e.g. dumps, env config)
49
- git clean -xdf .
91
+ bin/rake test_data:load
92
+ bin/rails test test/integration/boops_that_boop_boops_test.rb
93
+
94
+ # Test all the after hooks!
95
+ cp ../test/fixtures/20210729130542_add_materialized_meta_boop_view.rb db/migrate/
96
+ cp ../test/fixtures/meta_boop.rb app/models/meta_boop.rb
97
+ # Gsub config file to switch to structure.sql b/c materialized view
98
+ ruby -e '
99
+ path = "config/application.rb"
100
+ IO.write(path, File.open(path) { |f|
101
+ f.read.gsub("# config.active_record.schema_format = :sql", "config.active_record.schema_format = :sql")
102
+ })
103
+ '
104
+ rm db/schema.rb
105
+ bin/rake db:migrate db:test:prepare
106
+ RAILS_ENV=test_data bin/rake db:migrate
107
+ bin/rake test_data:dump
108
+ bin/rails test test/integration/test_data_hooks_test.rb
109
+
110
+ # Cleanup
111
+ cd ..
112
+ ./script/reset_example_app
113
+
114
+ 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/master/CHANGELOG.md"
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.1
4
+ version: 0.2.1
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-04-19 00:00:00.000000000 Z
11
+ date: 2021-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -31,7 +31,9 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
+ - ".github/workflows/ruby.yml"
34
35
  - ".gitignore"
36
+ - ".standard.yml"
35
37
  - CHANGELOG.md
36
38
  - Gemfile
37
39
  - Gemfile.lock
@@ -90,36 +92,66 @@ files:
90
92
  - example/public/apple-touch-icon.png
91
93
  - example/public/favicon.ico
92
94
  - example/public/robots.txt
95
+ - example/spec/rails_helper.rb
96
+ - example/spec/requests/boops_spec.rb
97
+ - example/spec/requests/rails_fixtures_override_spec.rb
98
+ - example/spec/spec_helper.rb
93
99
  - example/test/application_system_test_case.rb
100
+ - example/test/factories.rb
94
101
  - example/test/fixtures/boops.yml
95
102
  - example/test/integration/basic_boops_test.rb
103
+ - example/test/integration/better_mode_switching_demo_test.rb
104
+ - example/test/integration/boops_that_boop_boops_test.rb
105
+ - example/test/integration/dont_dump_tables_test.rb
106
+ - example/test/integration/load_rollback_truncate_test.rb
96
107
  - example/test/integration/migrated_boops_test.rb
108
+ - example/test/integration/mode_switching_demo_test.rb
97
109
  - example/test/integration/parallel_boops_with_fixtures_test.rb
98
110
  - example/test/integration/parallel_boops_without_fixtures_test.rb
111
+ - example/test/integration/rails_fixtures_double_load_test.rb
112
+ - example/test/integration/rails_fixtures_override_test.rb
113
+ - example/test/integration/test_data_hooks_test.rb
114
+ - example/test/integration/transaction_committing_boops_test.rb
99
115
  - example/test/integration/updated_boops_test.rb
100
116
  - example/test/test_helper.rb
117
+ - lib/generators/test_data/cable_yaml_generator.rb
101
118
  - lib/generators/test_data/database_yaml_generator.rb
102
119
  - lib/generators/test_data/environment_file_generator.rb
120
+ - lib/generators/test_data/initializer_generator.rb
121
+ - lib/generators/test_data/secrets_yaml_generator.rb
103
122
  - lib/generators/test_data/webpacker_yaml_generator.rb
104
123
  - lib/test_data.rb
124
+ - lib/test_data/active_record_ext.rb
105
125
  - lib/test_data/active_support_ext.rb
106
126
  - lib/test_data/config.rb
107
127
  - lib/test_data/configuration_verification.rb
108
128
  - lib/test_data/configurators.rb
129
+ - lib/test_data/configurators/cable_yaml.rb
109
130
  - lib/test_data/configurators/database_yaml.rb
110
131
  - lib/test_data/configurators/environment_file.rb
132
+ - lib/test_data/configurators/initializer.rb
133
+ - lib/test_data/configurators/secrets_yaml.rb
111
134
  - lib/test_data/configurators/webpacker_yaml.rb
135
+ - lib/test_data/custom_loaders/abstract_base.rb
136
+ - lib/test_data/custom_loaders/rails_fixtures.rb
112
137
  - lib/test_data/detects_database_emptiness.rb
113
138
  - lib/test_data/dumps_database.rb
114
139
  - lib/test_data/error.rb
140
+ - lib/test_data/generator_support.rb
141
+ - lib/test_data/inserts_test_data.rb
115
142
  - lib/test_data/installs_configuration.rb
116
143
  - lib/test_data/loads_database_dumps.rb
144
+ - lib/test_data/log.rb
145
+ - lib/test_data/manager.rb
117
146
  - lib/test_data/railtie.rb
118
147
  - lib/test_data/rake.rb
119
- - lib/test_data/transactional_data_loader.rb
148
+ - lib/test_data/save_point.rb
149
+ - lib/test_data/statistics.rb
150
+ - lib/test_data/truncates_test_data.rb
120
151
  - lib/test_data/verifies_configuration.rb
121
152
  - lib/test_data/verifies_dumps_are_loadable.rb
122
153
  - lib/test_data/version.rb
154
+ - script/reset_example_app
123
155
  - script/test
124
156
  - test_data.gemspec
125
157
  homepage: https://github.com/testdouble/test_data
@@ -127,7 +159,7 @@ licenses: []
127
159
  metadata:
128
160
  homepage_uri: https://github.com/testdouble/test_data
129
161
  source_code_uri: https://github.com/testdouble/test_data
130
- changelog_uri: https://github.com/testdouble/test_data/blob/master/CHANGELOG.md
162
+ changelog_uri: https://github.com/testdouble/test_data/blob/main/CHANGELOG.md
131
163
  post_install_message:
132
164
  rdoc_options: []
133
165
  require_paths:
@@ -1,77 +0,0 @@
1
- require "fileutils"
2
-
3
- module TestData
4
- def self.load_data_dump
5
- @transactional_data_loader ||= TransactionalDataLoader.new
6
- @transactional_data_loader.load_data_dump
7
- end
8
-
9
- def self.rollback(to: :after_data_load)
10
- raise Error.new("rollback called before load_data_dump") unless @transactional_data_loader.present?
11
- @transactional_data_loader.rollback(to: to)
12
- end
13
-
14
- class TransactionalDataLoader
15
- SavePoint = Struct.new(:name, :transaction, keyword_init: true)
16
-
17
- def initialize
18
- @config = TestData.config
19
- @save_points = []
20
- @dump_count = 0
21
- end
22
-
23
- def load_data_dump
24
- create_save_point(:before_data_load) unless save_point?(:before_data_load)
25
- unless save_point?(:after_data_load)
26
- execute_data_dump
27
- @dump_count += 1
28
- create_save_point(:after_data_load)
29
- end
30
- end
31
-
32
- def rollback(to:)
33
- return unless save_point?(to)
34
- rollback_save_point(to)
35
- end
36
-
37
- private
38
-
39
- def execute_data_dump
40
- search_path = execute("show search_path").first["search_path"]
41
- execute(File.read(@config.data_dump_full_path))
42
- execute <<~SQL
43
- select pg_catalog.set_config('search_path', '#{search_path}', false)
44
- SQL
45
- end
46
-
47
- def save_point?(name)
48
- purge_closed_save_points!
49
- @save_points.any? { |sp| sp.name == name }
50
- end
51
-
52
- def create_save_point(name)
53
- save_point = SavePoint.new(
54
- name: name,
55
- transaction: ActiveRecord::Base.connection.begin_transaction(joinable: false, _lazy: false)
56
- )
57
- @save_points << save_point
58
- end
59
-
60
- def rollback_save_point(name)
61
- if (save_point = @save_points.find { |sp| sp.name == name }) && save_point.transaction.open?
62
- save_point.transaction.rollback
63
- end
64
- purge_closed_save_points!
65
- end
66
-
67
- def purge_closed_save_points!
68
- @save_points = @save_points.select { |save_point|
69
- save_point.transaction.open?
70
- }
71
- end
72
-
73
- def execute(sql)
74
- ActiveRecord::Base.connection.execute(sql)
75
- end
76
- end
77
- end