test_data 0.0.1 → 0.2.1

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