DatabaseCleaner / database_cleaner-active_record

Strategies for cleaning databases using ActiveRecord. Can be used to ensure a clean state for testing.
MIT License
64 stars 63 forks source link

Accidental replacement of main DB model connections in multi-database env #97

Open kaorukobo opened 9 months ago

kaorukobo commented 9 months ago

Note: I will make a pull request that follows this issue.

Summary

When we use DatabaseCleaner for a multi-database environment, the connection for "main" models (i.e., models inheriting the connection pool from ActiveRecord::Base) gets replaced with the connection for non-main models.

Reproduction

For the full reproduction code, see https://gist.github.com/kaorukobo/8dd0e5ef6c8662cc5de88aceea5043a1

setcwd_to_tmpdir
load_gems
create_config_database_yml
setup_activerecord

# defines Foo model, which use the connection pool from ApplicationRecord.
def define_foo_model
  eval(<<~RUBY, TOPLEVEL_BINDING)
    class Foo < ApplicationRecord
      connection.exec_query "CREATE TABLE foos (id INTEGER PRIMARY KEY)"
      reset_column_information
    end
  RUBY
end

# defines Bar model, which connects to alternate DB to be cleaned by DatabaseCleaner.
def define_bar_model
  eval(<<~RUBY, TOPLEVEL_BINDING)
    class Bar < ApplicationRecord
      establish_connection :bar
      connection.exec_query "CREATE TABLE bars (id INTEGER PRIMARY KEY)"
      reset_column_information
    end
  RUBY
end

RSpec.describe do
  it "works" do
    define_foo_model

    expect(Foo.connection.tables).to eq(["foos"])

    cleaner = DatabaseCleaner::Cleaners.new
    cleaner[:active_record, :db => :bar].strategy = :truncation
    cleaner.clean

    # define Bar model after `cleaner.clean`
    define_bar_model

    # Foo.connection gets replaced with Bar's connection!
    #
    # expected: ["foos"]
    #      got: ["bars"]
    expect(Foo.connection.tables).to eq(["foos"])
  end
end
BoutPure commented 2 months ago

Got this error too. After specifying cleaners for primary (AR::Base) and then to secondary (some other model) DB I got AR::Base connection to wrong DB

Quick workaround is to specify cleaning strategy for primary DB to be the last:

    DatabaseCleaner[:active_record, db: :secondary].strategy = :transaction
    DatabaseCleaner[:active_record, db: :primary].strategy = :transaction

This will return primary DB connection to ActiveRecord::Base

psparrow commented 1 week ago

Any update on this? @BoutPure's suggestion above does not solve the problem for me.