Clean with transaction not working for 2 databases
clemens opened this issue · 3 comments
I've used DatabaseCleaner for several years (thanks!) without issues. But my current setup seems to not work properly.
Basic setup:
- Grape 0.19.2
- ActiveRecord 5.1.3 (+ otr-activerecord 1.2.4)
- RSpec 3.6.0
- DatabaseCleaner 1.6.1
- Posgres 9.5
On noteworthy thing about the app here is that it's using multiple databases: One main database and several tenant databases. In the test environment, this currently means two databases in total (main + test tenant).
Here's the test setup:
TEST_TENANT = 'the_tenant'
TEST_TENANT_API_KEY = SecureRandom.uuid.gsub('-', '')
RSpec.configure do |config|
config.mock_with :rspec
config.expect_with :rspec
config.raise_errors_for_deprecations!
# config.order = 'random'
config.before(:suite) do
ApiApp.establish_connection # ApiApp is connected to the main DB
primary_cleaner.clean_with(:truncation)
tenant = ApiApp.where(identifier: TEST_TENANT).first_or_create!(
name: 'The tenant',
api_key: TEST_TENANT_API_KEY
)
# force full recreate of tenant DB
config = ActiveRecord::Base.configurations[tenant.identifier]
ActiveRecord::Tasks::DatabaseTasks.drop(config) rescue nil
ActiveRecord::Tasks::DatabaseTasks.create(config)
ActiveRecord::Tasks::DatabaseTasks.load_schema(config, :sql, 'db/structure.sql')
# seed it
AppSetter.with(TEST_TENANT) do
Dir["#{Dir.pwd}/spec/seeds/*.rb"].sort.each { |f| load f }
end
end
config.before(:each) do |example|
AppSetter.set(TEST_TENANT)
puts ['[RSpec] start before', "number of patients: #{Patient.count}", Patient.connection.instance_variable_get(:@config)[:database]].inspect
# primary_cleaner.start
tenant_cleaner.start
puts ['[RSpec] end before', "number of patients: #{Patient.count}", Patient.connection.instance_variable_get(:@config)[:database]].inspect
end
config.append_after(:each) do
puts ['[RSpec] start append_after', "number of patients: #{Patient.count}", Patient.connection.instance_variable_get(:@config)[:database]].inspect
# primary_cleaner.clean
tenant_cleaner.clean
puts ['[RSpec] end append_after', "number of patients: #{Patient.count}", Patient.connection.instance_variable_get(:@config)[:database]].inspect
puts '-' * 100
end
private
def primary_cleaner
@primary_cleaner ||= cleaner_for(connection: ENV['RACK_ENV'].to_sym)
end
def tenant_cleaner
@tenant_cleaner ||= cleaner_for(connection: TEST_TENANT.to_sym)
end
def cleaner_for(options)
DatabaseCleaner[:active_record, options].tap { |cleaner| cleaner.strategy = :transaction }
end
end
Now when I have 2 examples in RSpec which both use the same basic setup with a test patient (Patient.create!(email: 'john@doe.com', ...)
), the uniqueness constraint on email fails because apparently the record exists already. The debugging code in the output (from the puts
statements above) confirms this:
(1) ["[RSpec] start before", "number of patients: 0", "api_test_the_tenant"]
["[DatabaseCleaner] before start", "open transactions: 0", "api_test_the_tenant"]
(2) ["[DatabaseCleaner] after start", "open transactions: 1", "api_test_the_tenant"]
["[RSpec] end before", "number of patients: 0", "api_test_the_tenant"]
(3) ["[RSpec] start append_after", "number of patients: 1", "api_test_the_tenant"]
["[DatabaseCleaner] before clean", "open transactions: 1", "api_test_the_tenant"]
(4) ["[DatabaseCleaner] after clean", "open transactions: 0", "api_test_the_tenant"]
(5) ["[RSpec] end append_after", "number of patients: 1", "api_test_the_tenant"]
----------------------------------------------------------------------------------------------------
(6) ["[RSpec] start before", "number of patients: 1", "api_test_the_tenant"]
["[DatabaseCleaner] before start", "open transactions: 0", "api_test_the_tenant"]
["[DatabaseCleaner] after start", "open transactions: 1", "api_test_the_tenant"]
["[RSpec] end before", "number of patients: 1", "api_test_the_tenant"]
["[RSpec] start append_after", "number of patients: 1", "api_test_the_tenant"]
["[DatabaseCleaner] before clean", "open transactions: 1", "api_test_the_tenant"]
["[DatabaseCleaner] after clean", "open transactions: 0", "api_test_the_tenant"]
["[RSpec] end append_after", "number of patients: 1", "api_test_the_tenant"]
----------------------------------------------------------------------------------------------------
(I've numbered some lines to make it easier to follow. The [DatabaseCleaner]
output comes from some puts
statements I've put in the corresponding strategy in https://github.com/DatabaseCleaner/database_cleaner/blob/master/lib/database_cleaner/active_record/transaction.rb.)
(1) Before the first example (in the first before
block), there are 0 patients in the tenant database => expected.
(2) tenant_cleaner.start
has caused an open transaction in the tenant database => expected.
(3) Running the example has created a patient in the tenant database => expected.
(4) tenant_cleaner.clean
rolls back the transaction => expected.
(5) There is still 1 patient in the tenant database => this is NOT expected.
(6) The before
block of the next example confirms that 1 patient has remained in the tenant database => this is NOT expected.
Commenting in/out the cleaning of the primary database doesn't change anything (I wouldn't have expected it to either, but I tried it nonetheless).
Does anyone have any insights as to what's going wrong here?
Thanks a lot in advance.
i have the same issue in jruby 9.1.12.0-p0:
database_cleaner (1.6.1)
rspec-core (3.6.0)
activerecord (4.2.0)
activerecord-jdbc-adapter (1.3.23)
activerecord-oracle_enhanced-adapter (1.6.9)
@clemens Hi, just curious, why are these lines commented out?
# primary_cleaner.start
# primary_cleaner.clean
During my testing, I cared about the tenant database, not the primary one. In my actual test suite, obviously both should be cleaned.