Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/database_cleaner/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
ActiveSupport.on_load(:active_record) do
require 'database_cleaner/active_record/base'
require 'database_cleaner/active_record/transaction'
require 'database_cleaner/active_record/serializable_transaction'
require 'database_cleaner/active_record/truncation'
require 'database_cleaner/active_record/deletion'

Expand Down
13 changes: 13 additions & 0 deletions lib/database_cleaner/active_record/serializable_transaction.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# See: https://github.com/DatabaseCleaner/database_cleaner/pull/681
return if Gem::Version.new(DatabaseCleaner::VERSION) < Gem::Version.new("2.0.2")

# See: https://github.com/rails/rails/pull/55542
return if ActiveRecord.gem_version < Gem::Version.new("8.1.0")

module DatabaseCleaner
module ActiveRecord
class SerializableTransaction < Transaction
TRANSACTION_PARAMETERS = { isolation: :serializable }.freeze
end
end
end
10 changes: 9 additions & 1 deletion lib/database_cleaner/active_record/transaction.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module DatabaseCleaner
module ActiveRecord
class Transaction < Base
TRANSACTION_PARAMETERS = {}.freeze

def start
connection = if ::ActiveRecord.version >= Gem::Version.new("7.2")
connection_class.lease_connection
Expand All @@ -9,7 +11,7 @@ def start
end

# Hack to make sure that the connection is properly set up before cleaning
connection.transaction {}
connection.transaction(**transaction_parameters) { nil }

connection.begin_transaction joinable: false
end
Expand All @@ -25,6 +27,12 @@ def clean

connection_class.connection_pool.release_connection
end

private

def transaction_parameters
self.class.const_get(:TRANSACTION_PARAMETERS)
end
end
end
end
109 changes: 109 additions & 0 deletions spec/database_cleaner/active_record/serializable_transaction_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
require 'support/database_helper'
require 'database_cleaner/active_record/serializable_transaction'

return unless defined?(DatabaseCleaner::ActiveRecord::SerializableTransaction)

RSpec.describe DatabaseCleaner::ActiveRecord::SerializableTransaction do
subject(:strategy) { described_class.new }

DatabaseHelper.with_all_dbs do |helper|
next if helper.db == :sqlite3

context "using a #{helper.db} connection" do
around do |example|
helper.setup
example.run
helper.teardown
end

describe "#clean" do
context "after an initial #start" do
before do
strategy.start

ActiveRecord::Base.transaction(isolation: :serializable) do
2.times { User.create! }
2.times { Agent.create! }
end
end

it "should clean all tables" do
expect { strategy.clean }
.to change { [User.count, Agent.count] }
.from([2,2])
.to([0,0])
end
end

context "with fixtures before an initial #start" do
before do
2.times { User.create! }
strategy.start
ActiveRecord::Base.transaction(isolation: :serializable) do
2.times { Agent.create! }
end
end

it "should not clean fixtures" do
expect { strategy.clean }
.to change { [User.count, Agent.count] }
.from([2,2])
.to([2,0])
end
end

context "without an initial start" do
before do
ActiveRecord::Base.transaction(isolation: :serializable) do
2.times { User.create! }
2.times { Agent.create! }
end
end

it "does nothing" do
expect { strategy.clean }
.to_not change { [User.count, Agent.count] }
end
end
end

describe "#cleaning" do
context "with records" do
it "should clean all tables" do
strategy.cleaning do
ActiveRecord::Base.transaction(isolation: :serializable) do
2.times { User.create! }
2.times { Agent.create! }
end
expect([User.count, Agent.count]).to eq [2,2]
end
expect([User.count, Agent.count]).to eq [0,0]
end
end

context "with fixtures" do
it "should not clean fixtures" do
2.times { User.create! }
strategy.cleaning do
ActiveRecord::Base.transaction(isolation: :serializable) do
2.times { Agent.create! }
end
expect([User.count, Agent.count]).to eq [2,2]
end
expect([User.count, Agent.count]).to eq [2,0]
end
end

context "without an initial start" do
it "does nothing" do
2.times { User.create! }
2.times { Agent.create! }
expect { strategy.cleaning {} }
.to_not change { [User.count, Agent.count] }
.from([2,2])
end
end
end
end
end
end