influitive / apartment

Database multi-tenancy for Rack (and Rails) applications
2.67k stars 463 forks source link

Tenant name with dot invalid in test framework #577

Open yowzadave opened 5 years ago

yowzadave commented 5 years ago

I'm using an elevator that switches on the first two subdomains to result in a Tenant name like "foo.bar". This works in my development and production environments, but not in the test environment: any get request from my controller tests returns an error like:

Error:
OrdersControllerTest#test_should_get_index:
ActiveRecord::StatementInvalid: PG::InvalidSchemaName: ERROR:  schema "foo" does not exist
LINE 1: SELECT setval('"foo"."bar"', 1, true)
                      ^
: SELECT setval('"foo"."bar"', 1, true)

My config/initializers/apartment.rb looks like this:

require 'apartment/elevators/generic'

Apartment.configure do |config|
  config.excluded_models = %w{ Site }
  config.tenant_names = lambda { Site.pluck :slug }
end

Rails.application.config.middleware.insert_before Warden::Manager, Apartment::Elevators::Generic, lambda { |request|
  request.host.split('.')[0] + '.' + request.host.split('.')[1]
}
bastianwegge commented 5 years ago

@yowzadave could you provide an example-project (maybe on GitHub) as reference? The content you provided does not reveal any implementation details, I'm afraid.

Btw. it seems the error you encounter does relate to a schema not being where it should be. Did you do an initial setup of apartment in your Tests like this:

config.before(:suite) do
  # Clean all tables to start
  DatabaseCleaner.clean_with :truncation
  # Use transactions for tests
  DatabaseCleaner.strategy = :transaction
  # Truncating doesn't drop schemas, ensure we're clean here, app *may not* exist
  Apartment::Tenant.drop('rspecseed') rescue nil
  # Create the default tenant for tests
  FactoryBot.create(:tenant, title: 'Primary Testing Corp.', subdomain: 'rspecseed')
end

config.before(:each) do
  # Start transaction for this test
  DatabaseCleaner.start
  # Switch into the default tenant
  Apartment::Tenant.switch! 'rspecseed'
end

config.after(:each) do
  # Reset tentant back to `public`
  Apartment::Tenant.reset
  # Rollback transaction
  DatabaseCleaner.clean
end
yowzadave commented 5 years ago

Yes, I am setting up apartment in my test_helper.rb, and the schema "foo.bar" exists—what the above error message suggests to me is that the app is looking for the schema "foo", when it should really be looking for "foo.bar".

Here's a minimal example that duplicates the issue that I see: https://github.com/yowzadave/apt-test

If I create a new Site with slug "foo.bar", and then do Apartment::Tenant.create("foo.bar"), I can successfully switch to the tenant by visiting foo.bar.lvh.me:3000/. But running any of the tests results in the above error.

bastianwegge commented 5 years ago

@yowzadave in your minimal example, you're not setting the base domain for any of the tests. Usually when doing integration-testing you want to do something like the following in a support-file and use the set_subdomain('foo.bar') before a spec-execution.

def set_subdomain(subdomain)
  domain = "http://#{subdomain}.lvh.me"

  Capybara.default_host = domain
  Capybara.app_host = domain

  default_url_options[:host] = domain
end

But I'm really not experienced using the bare minitest framework in combination with a subdomain solution like this.

mikecmpbll commented 5 years ago

can you post the trace for the exception in your test? i would like to know where that query is from, i'm not familiar with that kind of statement.

presumably it's simply a quoting issue in that statement, but i don't know where it's being generated.