test_data 0.2.0 → 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.
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.2.0)
4
+ test_data (0.2.1)
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
@@ -2,18 +2,40 @@ require "rails_helper"
2
2
 
3
3
  TestData.prevent_rails_fixtures_from_loading_automatically!
4
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
+
5
22
  RSpec.describe "FixtureFreeTestData", type: :request do
6
23
  fixtures :boops
7
24
 
8
- before(:each) do
9
- TestData.uses_test_data
10
- end
11
-
25
+ uses :test_data
12
26
  it "has 15 boops in the test_data" do
13
27
  expect(Boop.count).to eq(15)
14
28
  end
15
29
  end
16
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
+
17
39
  RSpec.describe "FixturesUsingTest", type: :request do
18
40
  fixtures :boops
19
41
 
@@ -0,0 +1,89 @@
1
+ require "test_helper"
2
+
3
+ TestData.config do |config|
4
+ config.after_test_data_load { MetaBoop.refresh_materialized_view }
5
+ config.after_test_data_truncate(-> { MetaBoop.refresh_materialized_view })
6
+ config.after_rails_fixture_load { MetaBoop.refresh_materialized_view }
7
+ end
8
+
9
+ TestData.prevent_rails_fixtures_from_loading_automatically!
10
+ MetaBoop.refresh_materialized_view # count = 1
11
+
12
+ class TestDataHooksTest < ActiveSupport::TestCase
13
+ fixtures :all
14
+ i_suck_and_my_tests_are_order_dependent!
15
+
16
+ def test_uses_test_data_hook
17
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
18
+ MetaBoop.reset_refresh_materialized_view_count
19
+
20
+ # Materialized view is refreshed and called 1 time
21
+ TestData.uses_test_data
22
+ assert_equal 15, Boop.count
23
+ assert_equal 15, MetaBoop.count
24
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
25
+ MetaBoop.reset_refresh_materialized_view_count
26
+
27
+ # Rollbacks also rollback to materialized view changes without calling again
28
+ Boop.create!(other_boop: Boop.new)
29
+ assert_equal 16, Boop.count
30
+ assert_equal 15, MetaBoop.count
31
+ MetaBoop.refresh_materialized_view
32
+ assert_equal 16, MetaBoop.count
33
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
34
+ TestData.uses_test_data
35
+ assert_equal 15, Boop.count
36
+ assert_equal 15, MetaBoop.count
37
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
38
+ MetaBoop.reset_refresh_materialized_view_count
39
+
40
+ # The same hook also works when cleaning slates
41
+ TestData.uses_clean_slate
42
+ assert_equal 0, Boop.count
43
+ assert_equal 0, MetaBoop.count
44
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
45
+ Boop.create!(other_boop: Boop.new)
46
+ MetaBoop.refresh_materialized_view
47
+ assert_equal 1, Boop.count
48
+ assert_equal 1, MetaBoop.count
49
+ assert_equal 2, MetaBoop.refresh_materialized_view_count
50
+ TestData.uses_clean_slate
51
+ assert_equal 0, Boop.count
52
+ assert_equal 0, MetaBoop.count
53
+ assert_equal 2, MetaBoop.refresh_materialized_view_count
54
+ MetaBoop.reset_refresh_materialized_view_count
55
+
56
+ # The same hook works with fixtures
57
+ TestData.uses_rails_fixtures(self)
58
+ assert_equal 2, Boop.count
59
+ assert_equal 2, MetaBoop.count
60
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
61
+ Boop.first.delete
62
+ assert_equal 1, Boop.count
63
+ assert_equal 2, MetaBoop.count
64
+ MetaBoop.refresh_materialized_view
65
+ assert_equal 1, MetaBoop.count
66
+ assert_equal 2, MetaBoop.refresh_materialized_view_count
67
+ TestData.uses_rails_fixtures(self)
68
+ assert_equal 2, Boop.count
69
+ assert_equal 2, MetaBoop.count
70
+ assert_equal 2, MetaBoop.refresh_materialized_view_count
71
+ MetaBoop.reset_refresh_materialized_view_count
72
+
73
+ # Rewinding two steps will not call refresh materialized views
74
+ TestData.uses_test_data
75
+ assert_equal 15, Boop.count
76
+ assert_equal 15, MetaBoop.count
77
+ assert_equal 0, MetaBoop.refresh_materialized_view_count
78
+ end
79
+
80
+ def test_that_hooks_require_valid_settings
81
+ foo = Struct.new(:thing)
82
+ assert_raises(TestData::Error) { TestData.config.after_test_data_load(nil) }
83
+ assert_raises(TestData::Error) { TestData.config.after_test_data_truncate(nil) }
84
+ assert_raises(TestData::Error) { TestData.config.after_rails_fixture_load(nil) }
85
+ assert_raises(TestData::Error) { TestData.config.after_test_data_load(foo) }
86
+ assert_raises(TestData::Error) { TestData.config.after_test_data_truncate(foo) }
87
+ assert_raises(TestData::Error) { TestData.config.after_rails_fixture_load(foo) }
88
+ end
89
+ end
@@ -8,14 +8,17 @@ module TestData
8
8
  return unless defined?(TestData)
9
9
 
10
10
  TestData.config do |config|
11
- # Where to store SQL dumps of the test_data database schema
12
- # config.schema_dump_path = "test/support/test_data/schema.sql"
11
+ # Hook run after test data is loaded by `TestData.uses_test_data`,
12
+ # but before a savepoint is taken
13
+ # config.after_test_data_load { }
13
14
 
14
- # Where to store SQL dumps of the test_data database test data
15
- # config.data_dump_path = "test/support/test_data/data.sql"
15
+ # Hook run after test data is truncated by `TestData.uses_clean_slate`,
16
+ # but before a savepoint is taken
17
+ # config.after_test_data_truncate { }
16
18
 
17
- # Where to store SQL dumps of the test_data database non-test data
18
- # config.non_test_data_dump_path = "test/support/test_data/non_test_data.sql"
19
+ # Hook run after test data is truncated by `TestData.uses_rails_fixtures`,
20
+ # but before a savepoint is taken
21
+ # config.after_rails_fixture_load { }
19
22
 
20
23
  # Tables whose data shouldn't be loaded into tests.
21
24
  # ("ar_internal_metadata" and "schema_migrations" are always excluded)
@@ -24,6 +27,15 @@ module TestData
24
27
  # Tables whose data should be excluded from SQL dumps (still dumps their schema DDL)
25
28
  # config.dont_dump_these_tables = []
26
29
 
30
+ # Where to store SQL dumps of the test_data database schema
31
+ # config.schema_dump_path = "test/support/test_data/schema.sql"
32
+
33
+ # Where to store SQL dumps of the test_data database test data
34
+ # config.data_dump_path = "test/support/test_data/data.sql"
35
+
36
+ # Where to store SQL dumps of the test_data database non-test data
37
+ # config.non_test_data_dump_path = "test/support/test_data/non_test_data.sql"
38
+
27
39
  # Tables whose data should be truncated by TestData.uses_clean_slate
28
40
  # If left as `nil`, all tables inserted into by the SQL file at
29
41
  # `data_dump_path` will be truncated
@@ -41,7 +41,8 @@ module TestData
41
41
  TestData.log.level = level
42
42
  end
43
43
 
44
- attr_reader :pwd, :cable_yaml_path, :database_yaml_path, :secrets_yaml_path
44
+ attr_reader :pwd, :cable_yaml_path, :database_yaml_path, :secrets_yaml_path,
45
+ :after_test_data_load_hook, :after_test_data_truncate_hook, :after_rails_fixture_load_hook
45
46
 
46
47
  def self.full_path_reader(*relative_path_readers)
47
48
  relative_path_readers.each do |relative_path_reader|
@@ -64,6 +65,33 @@ module TestData
64
65
  @non_test_data_tables = []
65
66
  @dont_dump_these_tables = []
66
67
  @truncate_these_test_data_tables = nil
68
+ @after_test_data_load_hook = -> {}
69
+ @after_test_data_truncate_hook = -> {}
70
+ @after_rails_fixture_load_hook = -> {}
71
+ end
72
+
73
+ def after_test_data_load(callable = nil, &blk)
74
+ hook = callable || blk
75
+ if !hook.respond_to?(:call)
76
+ raise Error.new("after_test_data_load must be passed a callable (e.g. a Proc) or called with a block")
77
+ end
78
+ @after_test_data_load_hook = hook
79
+ end
80
+
81
+ def after_test_data_truncate(callable = nil, &blk)
82
+ hook = callable || blk
83
+ if !hook.respond_to?(:call)
84
+ raise Error.new("after_test_data_truncate must be passed a callable (e.g. a Proc) or called with a block")
85
+ end
86
+ @after_test_data_truncate_hook = hook
87
+ end
88
+
89
+ def after_rails_fixture_load(callable = nil, &blk)
90
+ hook = callable || blk
91
+ if !hook.respond_to?(:call)
92
+ raise Error.new("after_rails_fixture_load must be passed a callable (e.g. a Proc) or called with a block")
93
+ end
94
+ @after_rails_fixture_load_hook = hook
67
95
  end
68
96
 
69
97
  def database_yaml
@@ -2,6 +2,7 @@ module TestData
2
2
  module CustomLoaders
3
3
  class RailsFixtures < AbstractBase
4
4
  def initialize
5
+ @config = TestData.config
5
6
  @statistics = TestData.statistics
6
7
  @already_loaded_rails_fixtures = {}
7
8
  end
@@ -34,6 +35,7 @@ module TestData
34
35
  test_instance.__test_data_gem_setup_fixtures
35
36
  @already_loaded_rails_fixtures[test_instance.class] = test_instance.instance_variable_get(:@loaded_fixtures)
36
37
  @statistics.count_load_rails_fixtures!
38
+ @config.after_rails_fixture_load_hook.call
37
39
  end
38
40
  end
39
41
  end
@@ -14,6 +14,7 @@ module TestData
14
14
 
15
15
  create_save_point(:before_data_load)
16
16
  @inserts_test_data.call
17
+ @config.after_test_data_load_hook.call
17
18
  record_ar_internal_metadata_that_test_data_is_loaded
18
19
  create_save_point(:after_data_load)
19
20
  end
@@ -41,6 +42,7 @@ module TestData
41
42
  end
42
43
 
43
44
  @truncates_test_data.call
45
+ @config.after_test_data_truncate_hook.call
44
46
  record_ar_internal_metadata_that_test_data_is_truncated
45
47
  create_save_point(:after_data_truncate)
46
48
  end
@@ -52,8 +52,7 @@ task "test_data:initialize" => ["test_data:verify_config", :environment] do
52
52
  end
53
53
 
54
54
  TestData.log.info <<~MSG
55
- Your test_data environment and database are ready for use! You can now run
56
- your server (or any command) to create some test data like so:
55
+ Your test_data environment and database are ready for use! You can now run your server (or any command) to create some test data like so:
57
56
 
58
57
  $ RAILS_ENV=test_data bin/rails server
59
58
 
@@ -83,7 +82,7 @@ task "test_data:load" => ["test_data:verify_config", :environment] do
83
82
  TestData::LoadsDatabaseDumps.new.call
84
83
 
85
84
  if ActiveRecord::Base.connection.migration_context.needs_migration?
86
- TestData.log.warn "There are pending migrations for database '#{TestData.config.database_name}'. To run them, run:\n\n RAILS_ENV=test_data bin/rake db:migrate\n\n"
85
+ TestData.log.warn "There are pending migrations for database '#{TestData.config.database_name}'. To run them, run:\n\n $ RAILS_ENV=test_data bin/rake db:migrate\n\n"
87
86
  end
88
87
  end
89
88
 
@@ -1,3 +1,3 @@
1
1
  module TestData
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -12,6 +12,7 @@ dropdb example_test_data 2>/dev/null || true
12
12
 
13
13
  # Reset files:
14
14
  git checkout app/models/boop.rb
15
+ git checkout config/application.rb
15
16
  git checkout config/database.yml
16
17
  git checkout db/schema.rb
17
18
  git clean -xdf .
data/script/test CHANGED
@@ -83,18 +83,30 @@ cp ../test/fixtures/20210423190737_add_foreign_keys.rb db/migrate/
83
83
  cp ../test/fixtures/boop_with_other_boops.rb app/models/boop.rb
84
84
  RAILS_ENV=test_data bin/rake db:migrate
85
85
  bin/rake test_data:dump
86
- bin/rake db:migrate
87
- bin/rake db:test:prepare
86
+ bin/rake db:migrate db:test:prepare
88
87
  bin/rails test test/integration/boops_that_boop_boops_test.rb
89
88
 
90
89
  # Make sure it loads cleanly again
91
90
  bin/rake test_data:drop_database
92
- if [ ! bin/rake test_data:load | grep -q "ERROR" ]; then
93
- echo "Running test_data:load after adding FK constraints led to errors"
94
- exit 1
95
- fi
91
+ bin/rake test_data:load
96
92
  bin/rails test test/integration/boops_that_boop_boops_test.rb
97
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
+
98
110
  # Cleanup
99
111
  cd ..
100
112
  ./script/reset_example_app
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.2.0
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-07-24 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
@@ -33,6 +33,7 @@ extra_rdoc_files: []
33
33
  files:
34
34
  - ".github/workflows/ruby.yml"
35
35
  - ".gitignore"
36
+ - ".standard.yml"
36
37
  - CHANGELOG.md
37
38
  - Gemfile
38
39
  - Gemfile.lock
@@ -109,6 +110,7 @@ files:
109
110
  - example/test/integration/parallel_boops_without_fixtures_test.rb
110
111
  - example/test/integration/rails_fixtures_double_load_test.rb
111
112
  - example/test/integration/rails_fixtures_override_test.rb
113
+ - example/test/integration/test_data_hooks_test.rb
112
114
  - example/test/integration/transaction_committing_boops_test.rb
113
115
  - example/test/integration/updated_boops_test.rb
114
116
  - example/test/test_helper.rb