Search code examples
ruby-on-railsrspeccapybara-webkitdatabase-cleanerapartment-gem

How to truncation all data in a schema different from the Public (database_cleaner)


In my project I use database multi-tenancy 'apartment' gem.

config/initializers/apartment.rb 

Apartment.configure do |config|
 config.excluded_models = %w{ User Company }
end

To clean up the database it tests I use 'database_cleaner' gem

spec/rails_helper.rb

RSpec.configure do |config|
  config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do |example|
    DatabaseCleaner.strategy= example.metadata[:js] ? :truncation : :transaction
    DatabaseCleaner.start
    Apartment::Tenant.switch!('app')
  end

  config.after(:each) do
    Apartment::Tenant.switch!
    DatabaseCleaner.clean
  end

end

In RSpec tests with Capybara truncation strategy clean after each test only public schema where only the user and company.

Before test start 
Company.count#=> 0

Other schemes are not cleared.

Before test start
SomeModelInCompanySchema.count#=> 240

How to clear data in another scheme


Solution

  • I created own class to clear test database

    class CleanTestDatabase
    
      TABLE_TO_EXCLUDE = ['spatial_ref_sys', 'schema_migrations']
      CONNECTION = ActiveRecord::Base.connection
    
      def self.clean(*tenants)
        tenants.each{ |tenant| delete_all_in_tenant(tenant) }
      end
    
      def self.drop_all_schemas
        schemas = ActiveRecord::Base.connection.select_values <<-SQL
            SELECT
              schema_name
            FROM
              information_schema.schemata
            WHERE
              schema_name NOT IN ('information_schema','public', 'postgis') AND
              schema_name NOT LIKE 'pg%'
        SQL
        schemas.each { |schema| Apartment::Tenant.drop(schema) }
      end
    
      private
    
        def self.delete_all_in_tenant(tenant)
          CONNECTION.disable_referential_integrity do
            tables_to_clean(tenant).each do |table|
              delete_from(table) if table_has_new_rows?(table)
            end
          end
        end
    
        def self.tables_to_clean(tenant)
          tables = CONNECTION.tables - TABLE_TO_EXCLUDE
          tables.map{ |table| "#{tenant}.#{table}" }
        end
    
        def self.table_has_new_rows?(table_name)
          CONNECTION.select_value("SELECT count(*) FROM #{table_name}").to_i > 0
        end
    
        def self.delete_from(table_name)
          CONNECTION.execute("DELETE FROM #{table_name}")
        end
    end
    

    spec/rails_helper.rb

      config.before(:each) do
        CleanTestDatabase.clean('public', 'app')
        Apartment::Tenant.switch!('app')
      end