rspec-multicore-rails 0.2.0.pre1 → 0.2.0.pre3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d79cf584351309d480685d78f5a6d75a6883f1dbc7567f0711723eb631222dd3
4
- data.tar.gz: 43a198971e8fef686be3f859658f10c3038bc897053fc0252782dc2d71c46736
3
+ metadata.gz: 3c3050617c4ec5b96ff57ac77a08a685e24c0a056d9c029464ec4f950d894369
4
+ data.tar.gz: f87be93f0ba0d16fd7b55200bd8a955ff9f334d06a327c2970701204998694ba
5
5
  SHA512:
6
- metadata.gz: ece0c46dc96d6fc0d716512a7db10b96bad1216dd8f8b1cc47aadb6e5ab0a48756efd244ce1491dca91d55e91cf3d54ce4866f917c9c7b9e0c4716f186c2ff8f
7
- data.tar.gz: b01482a80460f2e5b5e3532fc210ee6c5c3c5d0e16311403934a33d1067747484f09239b92efb920da9c8585c849051e7b4eb6ce5d149613e3c314c52782cb86
6
+ metadata.gz: d01bc7b6b71ee1e1d6287f5b66ff075f10df202bd2abb69b566d91d89915a5c43c6e1645f7b55edeaa34808330b089689e08cc2cc4d85e31efad2abdbecfc61b
7
+ data.tar.gz: c423d5130794d5b5feef994b6b82a4a1aac9dea321352fe44106b6135a8c44cda8a5ebaa688f78c0689190bc0584a7a322146caaf93685daa2b5f1e2433a87d8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0.pre3
4
+
5
+ - (#11) Fix Rails database lifecycle and parallel coverage for pre3
6
+
7
+ ## 0.2.0.pre2
8
+
9
+ - Uses Rails' standard database tasks to manage worker test databases and
10
+ removes the prerelease custom task namespace.
11
+ - Supports multiple writable test configurations while leaving replicas and
12
+ `database_tasks: false` configurations unmanaged.
13
+ - Improves release validation and retry safety.
14
+
3
15
  ## 0.2.0.pre1
4
16
 
5
17
  - Uses the core worker count as the single concurrency source.
data/README.md CHANGED
@@ -2,14 +2,35 @@
2
2
 
3
3
  `rspec-multicore-rails` loads `rspec-multicore` and automatically isolates ActiveRecord connections for Rails test workers.
4
4
 
5
- Worker 1 uses the base test database. Later workers use `_2`, `_3`, and so on; SQLite suffixes are placed before the extension. The adapter refuses non-test environments and clears inherited connections before connecting.
5
+ ## Installation
6
+
7
+ ```ruby
8
+ group :test do
9
+ gem "rspec-multicore-rails", "0.2.0.pre3"
10
+ end
11
+ ```
12
+
13
+ Normal `Bundler.require` loading requires no `require: false`, initializer, or
14
+ manual require. The adapter loads the matching core gem automatically.
15
+
16
+ Worker 1 uses the base test database. Later workers use `_2`, `_3`, and so on;
17
+ SQLite suffixes are placed before the extension. The adapter refuses to connect
18
+ workers outside test and clears inherited connections before connecting.
19
+
20
+ Use Rails' standard database tasks to manage the base and worker databases:
6
21
 
7
22
  ```bash
8
- bundle exec rake db:test:multicore:prepare
9
- bundle exec rake db:test:multicore:drop
10
- bundle exec rake db:test:multicore:recreate
23
+ bin/rails db:prepare
24
+ bin/rails db:test:prepare
25
+ bin/rails db:reset
11
26
  ```
12
27
 
28
+ Create, schema-load, prepare, purge, and drop operations extend to the extra
29
+ worker databases whenever the Rails task includes test. Multiple writable test
30
+ configurations are supported; replicas and configurations with
31
+ `database_tasks: false` are not managed. No worker entries are needed in
32
+ `database.yml`, and running RSpec never mutates the database lifecycle.
33
+
13
34
  The adapter does not manage Redis, Sidekiq, caches, loggers, SimpleCov, or profilers. Configure those with `RSpec::Multicore.on_worker_fork` and `on_worker_shutdown`.
14
35
 
15
36
  Requires Ruby 3.2+, Rails/ActiveRecord 7.1–8.x, and exactly the matching `rspec-multicore` version.
@@ -6,71 +6,121 @@ require "active_record/tasks/database_tasks"
6
6
  module RSpec
7
7
  module Multicore
8
8
  module Rails
9
- # Names, prepares, and connects worker-owned ActiveRecord databases.
9
+ # Manages and connects worker-owned ActiveRecord test databases.
10
10
  class DatabaseManager
11
+ TEST_ENV = "test"
12
+
11
13
  def initialize
12
- ensure_test_environment!
13
- @base_config = ActiveRecord::Base.connection_db_config
14
+ @original_config = ActiveRecord::Base.connection_db_config
15
+ @configs = test_configs
16
+ @primary_config = canonical_config(@original_config) || @configs.first || @original_config
17
+ @configs << @primary_config unless @configs.include?(@primary_config)
18
+ @base_databases = {}.compare_by_identity
19
+ @configs.each { @base_databases[_1] = _1.database }
14
20
  end
15
21
 
16
22
  def connect_worker(slot)
23
+ ensure_test_environment!
17
24
  ActiveRecord::Base.connection_handler.clear_all_connections!
18
- ActiveRecord::Base.establish_connection(config_for(slot))
25
+ @configs.each { _1._database = database_for(slot, _1) }
26
+ ActiveRecord::Base.establish_connection(@primary_config)
19
27
  end
20
28
 
21
- def prepare_all
22
- worker_slots.each do |slot|
23
- purge(slot)
24
- ActiveRecord::Tasks::DatabaseTasks.load_schema(hash_config_for(slot))
29
+ def create_workers(name: nil) = preserve_connection { each_worker_config(name:) { database_tasks.create(_1) } }
30
+
31
+ def purge_workers(name: nil)
32
+ preserve_connection do
33
+ each_worker_config(name:) do |config|
34
+ database_tasks.purge(config)
35
+ rescue ActiveRecord::NoDatabaseError
36
+ database_tasks.create(config)
37
+ end
25
38
  end
26
39
  end
27
40
 
28
- def drop_workers = worker_slots.drop(1).each { drop(_1) }
41
+ def load_schema_workers(name: nil)
42
+ preserve_connection do
43
+ each_worker_config(name:) do |config|
44
+ establish_connection(config)
45
+ database_tasks.load_schema(config, schema_format(config))
46
+ end
47
+ end
48
+ end
49
+
50
+ def prepare_workers(name: nil)
51
+ purge_workers(name:)
52
+ load_schema_workers(name:)
53
+ end
54
+
55
+ def drop_workers(name: nil) = preserve_connection { each_worker_config(name:) { database_tasks.drop(_1) } }
29
56
 
30
57
  def workers = RSpec::Multicore.workers
31
- def database_for(slot) = slot == 1 ? base_database : suffixed_database(slot)
58
+
59
+ def database_for(slot, config = @primary_config)
60
+ return base_database(config) if slot == 1
61
+
62
+ database = base_database(config)
63
+ return "#{database}_#{slot}" unless config.adapter == "sqlite3"
64
+
65
+ extension = File.extname(database)
66
+ "#{database.delete_suffix(extension)}_#{slot}#{extension}"
67
+ end
32
68
 
33
69
  private
34
70
 
35
- def worker_slots = (1..workers)
36
- def base_database = @base_config.database
71
+ def test_configs
72
+ configurations = ActiveRecord::Base.configurations
73
+ Array(configurations.configs_for(env_name: TEST_ENV, include_hidden: true))
74
+ end
75
+
76
+ def canonical_config(config) = @configs.find { _1.env_name == config.env_name && _1.name == config.name }
37
77
 
38
- def suffixed_database(slot)
39
- if sqlite?
40
- extension = File.extname(base_database)
41
- return "#{base_database.delete_suffix(extension)}_#{slot}#{extension}"
78
+ def task_configs(name: nil)
79
+ @configs.select do |config|
80
+ config.env_name == TEST_ENV && config.database_tasks? && (!name || config.name == name)
42
81
  end
43
-
44
- "#{base_database}_#{slot}"
45
82
  end
46
83
 
47
- def sqlite? = @base_config.adapter == "sqlite3"
84
+ def each_worker_config(name:)
85
+ return enum_for(__method__, name:) unless block_given?
48
86
 
49
- def config_for(slot) = @base_config.configuration_hash.merge(database: database_for(slot))
87
+ task_configs(name:).each do |config|
88
+ 2.upto(workers) { yield derived_config(config, _1) }
89
+ end
90
+ end
50
91
 
51
- def hash_config_for(slot)
92
+ def derived_config(config, slot)
52
93
  ActiveRecord::DatabaseConfigurations::HashConfig.new(
53
- @base_config.env_name,
54
- @base_config.name,
55
- config_for(slot)
94
+ config.env_name,
95
+ config.name,
96
+ config.configuration_hash.merge(database: database_for(slot, config))
56
97
  )
57
98
  end
58
99
 
59
- def purge(slot)
60
- drop(slot)
61
- ActiveRecord::Tasks::DatabaseTasks.create(config_for(slot))
100
+ def base_database(config) = @base_databases.fetch(config)
101
+ def database_tasks = ActiveRecord::Tasks::DatabaseTasks
102
+
103
+ def preserve_connection
104
+ yield
105
+ ensure
106
+ establish_connection(@original_config)
62
107
  end
63
108
 
64
- def drop(slot)
65
- ActiveRecord::Tasks::DatabaseTasks.drop(config_for(slot))
66
- rescue ActiveRecord::NoDatabaseError
67
- nil
109
+ def establish_connection(config)
110
+ handler = ActiveRecord::Base.connection_handler
111
+ handler.establish_connection(config, clobber: true)
112
+ end
113
+
114
+ def schema_format(config)
115
+ return config.schema_format if config.respond_to?(:schema_format)
116
+
117
+ config.configuration_hash.fetch(:schema_format, ActiveRecord.schema_format)
68
118
  end
69
119
 
70
120
  def ensure_test_environment!
71
121
  return if ::Rails.env.test?
72
122
 
73
- raise RSpec::Multicore::Error, "Parallel databases can only be managed in the Rails test environment"
123
+ raise RSpec::Multicore::Error, "Parallel databases can only be connected in the Rails test environment"
74
124
  end
75
125
  end
76
126
  end
@@ -3,7 +3,7 @@
3
3
  module RSpec
4
4
  module Multicore
5
5
  module Rails
6
- VERSION = "0.2.0.pre1"
6
+ VERSION = "0.2.0.pre3"
7
7
  end
8
8
  end
9
9
  end
@@ -2,27 +2,46 @@
2
2
 
3
3
  require "rspec/multicore/rails"
4
4
 
5
- namespace :db do
6
- namespace :test do
7
- namespace :multicore do
8
- desc "Prepare all parallel test databases (create and load schema)"
9
- task prepare: :environment do
10
- manager = RSpec::Multicore::Rails::DatabaseManager.new
11
- puts "Preparing parallel test databases..."
12
- manager.prepare_all
13
- puts "Parallel test databases ready (#{manager.workers} workers)"
14
- end
5
+ manager = -> { RSpec::Multicore::Rails::DatabaseManager.new }
6
+ test_in_scope = lambda do
7
+ Rails.env.test? || (Rails.env.development? && !ENV["DATABASE_URL"] && !ENV["SKIP_TEST_DATABASE"])
8
+ end
9
+ allowed = lambda do |scope|
10
+ scope == :all || (scope == :test ? Rails.env.test? : test_in_scope.call)
11
+ end
12
+
13
+ enhance = lambda do |task_name, operation, name: nil, scope: :current|
14
+ next unless Rake::Task.task_defined?(task_name)
15
+
16
+ Rake::Task[task_name].enhance do
17
+ next unless allowed.call(scope)
18
+
19
+ manager.call.public_send(operation, name:)
20
+ end
21
+ end
15
22
 
16
- desc "Drop all parallel test databases (except base test database)"
17
- task drop: :environment do
18
- manager = RSpec::Multicore::Rails::DatabaseManager.new
19
- puts "Dropping parallel test databases..."
20
- manager.drop_workers
21
- puts "Parallel test databases dropped"
22
- end
23
+ enhance.call("db:create", :create_workers)
24
+ enhance.call("db:create:all", :create_workers, scope: :all)
25
+ enhance.call("db:prepare", :prepare_workers)
26
+ enhance.call("db:schema:load", :load_schema_workers)
27
+ enhance.call("db:test:purge", :purge_workers, scope: :all)
28
+ enhance.call("db:test:load_schema", :load_schema_workers, scope: :all)
29
+ enhance.call("db:purge", :purge_workers)
30
+ enhance.call("db:purge:all", :purge_workers, scope: :all)
31
+ enhance.call("db:drop", :drop_workers)
32
+ enhance.call("db:drop:all", :drop_workers, scope: :all)
23
33
 
24
- desc "Recreate all parallel test databases"
25
- task recreate: :prepare
26
- end
34
+ Rake::Task.tasks.map(&:name).each do |task_name|
35
+ case task_name
36
+ when /\Adb:test:purge:(.+)\z/
37
+ enhance.call(task_name, :purge_workers, name: Regexp.last_match(1), scope: :all)
38
+ when /\Adb:test:load_schema:(.+)\z/
39
+ enhance.call(task_name, :load_schema_workers, name: Regexp.last_match(1), scope: :all)
40
+ when /\Adb:create:(?!all\z)(.+)\z/
41
+ enhance.call(task_name, :create_workers, name: Regexp.last_match(1), scope: :test)
42
+ when /\Adb:schema:load:(.+)\z/
43
+ enhance.call(task_name, :load_schema_workers, name: Regexp.last_match(1), scope: :test)
44
+ when /\Adb:drop:(?!all\z|_unsafe\z)(.+)\z/
45
+ enhance.call(task_name, :drop_workers, name: Regexp.last_match(1), scope: :test)
27
46
  end
28
47
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-multicore-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0.pre1
4
+ version: 0.2.0.pre3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sveta Markovic
@@ -55,14 +55,14 @@ dependencies:
55
55
  requirements:
56
56
  - - '='
57
57
  - !ruby/object:Gem::Version
58
- version: 0.2.0.pre1
58
+ version: 0.2.0.pre3
59
59
  type: :runtime
60
60
  prerelease: false
61
61
  version_requirements: !ruby/object:Gem::Requirement
62
62
  requirements:
63
63
  - - '='
64
64
  - !ruby/object:Gem::Version
65
- version: 0.2.0.pre1
65
+ version: 0.2.0.pre3
66
66
  - !ruby/object:Gem::Dependency
67
67
  name: rake
68
68
  requirement: !ruby/object:Gem::Requirement
@@ -120,7 +120,7 @@ dependencies:
120
120
  - !ruby/object:Gem::Version
121
121
  version: '2.0'
122
122
  description: Rails adapter for rspec-multicore that assigns each worker an isolated
123
- ActiveRecord test database and provides focused prepare, drop, and recreate tasks.
123
+ ActiveRecord test database through the standard Rails database-task lifecycle.
124
124
  email:
125
125
  - svetam.sd@pm.me
126
126
  executables: []