Testing With config.require_tenant = true

Jason Ackerman
I am getting ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet on tests related to a model I have called Entity.

In acts_as_tenant.rb I have

ActsAsTenant.configure do |config|
  config.require_tenant = true
end

In entities_controller.rb I have: acts_as_tenant :account

How do I set up my tests to have an account created, set as the current_tenant, and then assigned to new entities created in my Entity fixture?

This is my Entity fixture (pretty basic):
one:
  name: MyString
  description: MyString

Any help on this would be much appreciated! Thanks!
Chris Oliver
Hey Jason!

The multitenancy test file has some examples you can use for the various ways of selecting a tenant. https://gitlab.com/gorails/jumpstart-pro/-/blob/master/test/integration/multitenancy_test.rb

Depending what you have enabled, you can use whatever you need. The `switch_account` method uses the session cookie, which is always enabled so you can use that by default if you need.

You'll want to make sure your Entity fixture is associated with a tenant so that it is in the query results for the current tenant.
Jason Ackerman
Thanks for the quick response Chris, I'll give this a try!
Jason Ackerman
While I've had some luck with this:
class OwnershipTest < ActiveSupport::TestCase
  setup do
    ActsAsTenant.current_tenant = accounts(:company)
  end
...
end

I cannot seem to replicate this in an integration test:

class EntitiesControllerTest < ActionDispatch::IntegrationTest
  setup do
    @user = users(:one)
    sign_in @user
    @account = accounts(:company)
    ActsAsTenant.current_tenant = @account
    @entity = entities(:main)
  end
...
  test "should show entity" do
    get entity_url(@entity)
    assert_response :success
  end

test "should create entity" do assert_difference('Entity.count') do post entities_url, params: { entity: { description: @entity.description, name: @entity.name, account: @account } } end assert_redirected_to entity_url(Entity.last) end ... end

When I run the EntitiesControllerTest I get the following errors on the two tests shown above (amongst others):
Error:EntitiesControllerTest#test_should_create_entity: ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet    
    test/controllers/entities_controller_test.rb:24:in `block in <class:EntitiesControllerTest>' 
    test/controllers/entities_controller_test.rb:24:in `block in <class:EntitiesControllerTest>'

EntitiesControllerTest#test_should_show_entity:
    ActiveRecord::RecordNotFound: Couldn't find Entity with 'id'=1059638630 [WHERE "entities"."account_id" = $1]
    app/controllers/entities_controller.rb:60:in `set_entity'    
    lib/jumpstart/lib/jumpstart/account_middleware.rb:30:in `call'    
    test/controllers/entities_controller_test.rb:33:in `block in <class:EntitiesControllerTest>'

Using byebug, I inspected the value of @entity and @account and found that:
@entity = #<Entity id: 1059638630, name: "main", description: "main", created_at: "2020-05-14 01:58:26", updated_at: "2020-05-14 01:58:26", account_id: 264178000>

@account = <Account id: 264178000, name: "Company", owner_id: 980190962, personal: false, created_at: "2020-05-14 01:58:26", updated_at: "2020-05-14 01:58:26", processor: nil, processor_id: nil, trial_ends_at: nil, card_type: nil, card_last4: nil, card_exp_month: nil, card_exp_year: nil, extra_billing_info: nil, domain: "company.com", subdomain: "company", plan: nil, quantity: nil, card_token: nil>

I'm not sure, but it seems like it is trying to find an entity where entity.account_id = 1, but the account_id is actually 264178000. That said, I don't know why it is looking for any entity where entity.account_id is 1. Any help getting these tests to pass would be much appreciated! I'm trying to do TDD, but I can't even get the basic tests to pass and am starting to pull my hair out! Thanks!
Chris Oliver
I would recommend using the same mechanisms I have in the test file example like switch_account(@account). That way you're going through the same setup that Rails uses to set the current tenant and you don't have to do that directly in your tests.

You also may want to work backwards from the entity to make sure that you're logging in a user that's actually connected to the account. It's easy for fixtures to change and the user ends up not having access to the account, breaking your tests.

class EntitiesControllerTest < ActionDispatch::IntegrationTest
  setup do
    @entity = entities(:main)
    @account = @entity.account
    @user = @account.users.first

    sign_in @user
    switch_account(@account)
  end
...
  test "should show entity" do
    get entity_url(@entity)
    assert_response :success
  end
end

This would more reliably set the account and users since it's using the associations. And then by calling switch_account, you'll let Jumpstart / Rails set the current tenant as it expects it to be set.

Without that, Rails may not know if you're signed into an account or which one.
Jason Ackerman
Thanks Chris! So this worked for most of the integration tests (including the show test above), but not for the create or destroy tests. It seems like it may be an issue with assert_difference being used in both of these, but I'm not sure.

test "should create entity" do
    assert_difference('Entity.count') do
      post entities_url, params: { entity: { description: @entity.description, name: @entity.name, account: @account } }
    end

    assert_redirected_to entity_url(Entity.last)
  end

test "should destroy entity" do
    assert_difference('Entity.count', -1) do
      delete entity_url(@entity)
    end

    assert_redirected_to entities_url
  end

I'm still getting the no tenant error on both:

EntitiesControllerTest#test_should_create_entity:ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet 
    test/controllers/entities_controller_test.rb:24:in `block in <class:EntitiesControllerTest>'
    test/controllers/entities_controller_test.rb:24:in `block in <class:EntitiesControllerTest>'

EntitiesControllerTest#test_should_destroy_entity:ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet
    test/controllers/entities_controller_test.rb:47:in `block in <class:EntitiesControllerTest>'
    test/controllers/entities_controller_test.rb:47:in `block in <class:EntitiesControllerTest>'

Thanks again for your help with this!

P.S. Since these use devise's integration test helpers, they don't seem to work in a model test, which inherits from ActiveSupport::TestCase. In that test I used ActsAsTenant.current_tenant = accounts(:company) and it seems to work. Is that the recommended method?
Chris Oliver
switch_account actually makes a HTTP request, so it's only available in controller tests. 

Setting the current tenant would be what you want for model tests. It would make sense to add a helper so we can set the Current.account and current_tenant together for model tests. I'll have to look into adding that. 
Jason Ackerman
Ah, makes sense, thanks! Is there any reason that switch_account would not work for the create/destroy tests (above), which are in the integration test not the model test? Maybe I'm missing something else... Thanks again!
Jason Ackerman
So it looks like (per digging in with byebug) the issue is with Entity.count in the assert_difference, which is throwing the no tenant set error. Would this have to be handled differently than the other tests? Thanks!
Jake Smith
 would we need to set our `Jumpstart.config.multitenancy` in our tests separately from how we set it up in the admin pages while running the app?  I believe my app is set to session, but my tests are configured to path, and I'm not sure how to change that other than the `[...].stub` approach I see in multitenancy_test.rb. 
Jake Smith
Actually, I'm wrong.  I am using path.  I think my problem is that after using `switch_account @account` in my test, the CurrentHelper::current_account is still pointing to the previously logged in user.  So my before_action ensuring that certain actions are only accessible to the non-personal account is redirecting me in my tests when they shouldn't be.
Jake Smith
It looks like my problem is actually the path helpers.  They aren't putting the account_id at the beginning of the path after I call switch_account.  It looks like I have to provide the `script_name: "/#{@account.id}"` in my controller tests.  Is there a reason why this isn't required when using the path helpers in erb?
Jason Ackerman
Any update on this? I'm running into this issue on a lot of tests now and it's making it difficult to provide adequate test coverage. Any suggestions? Thanks!
Chris Oliver
, it should use the same Jumpstart.config in tests as in dev/prod. Toss a byebug in and print out the Jumpstart.config.multitenancy to make sure.

One thing you have to keep in mind is that the tests are not running a real browser, so the script_name won't necessarily persist the same way that it does in the browser.

I wonder if this is related to what you're seeing in the path helpers for tests.
https://github.com/rails/rails/issues/26173

For setting accounts manually in tests, you can do this which will replicate what the SetCurrentRequestDetails does.
Current.account = account
ActsAsTenant.current_tenant = account

If you've got time, could you guys create a private repo that reproduces the problem with a test that I can look at?
Jason Ackerman
 You should have access to my private repo for OrgChart. You can see the commented out tests in entities_controller_test.rb. I have been able to get most of what I need to work in the model test (entity_test.rb), but the integration tests have been a problem. Thanks!
Jason Ackerman
Here's another example of this issue cropping up (this time in a system test):

class EntitiesTest < ApplicationSystemTestCase
  setup do
    @entity = entities(:main)
    @account = @entity.account
    @user = @account.users.first
    login_as @user, scope: :user
  end

 test "updating an Entity" do
    ActsAsTenant.current_tenant = @account ### I added this line because putting out the current_tenant was returning nil before using this
    puts ActsAsTenant.current_tenant.inspect
    puts @account.inspect
    puts @entity.inspect
    visit entity_url(@entity)
    click_on "Edit", match: :first

    fill_in "Description", with: @entity.description
    fill_in "Name", with: @entity.name
    click_on "Update Entity"

    assert_text "Entity was successfully updated"
    assert_text "Entities"
  end
end

The output in the log is:
#<Account id: 264178000, name: "Company", owner_id: 980190962, personal: false, created_at: "2020-06-24 17:36:30", updated_at: "2020-06-24 17:36:30", processor: nil, processor_id: nil, trial_ends_at: nil, card_type: nil, card_last4: nil, card_exp_month: nil, card_exp_year: nil, extra_billing_info: nil, domain: "company.com", subdomain: "company", plan: nil, quantity: nil, card_token: nil>

#<Account id: 264178000, name: "Company", owner_id: 980190962, personal: false, created_at: "2020-06-24 17:36:30", updated_at: "2020-06-24 17:36:30", processor: nil, processor_id: nil, trial_ends_at: nil, card_type: nil, card_last4: nil, card_exp_month: nil, card_exp_year: nil, extra_billing_info: nil, domain: "company.com", subdomain: "company", plan: nil, quantity: nil, card_token: nil>

#<Entity id: 1059638630, name: "main", description: "main", created_at: "2020-06-24 17:36:30", updated_at: "2020-06-24 17:36:30", account_id: 264178000, tag_list: nil>

The entity's account_id matches the ID of @account and matches the ID of the account returned by ActsAsTenant.current_tenant, but I'm still getting this error:
ActiveRecord::RecordNotFound: Couldn't find Entity with 'id'=1059638630 [WHERE "entities"."account_id" = $1]
Chris Oliver
Using assert_difference('Entity.count') doesn't seem to retain the current tenant. That's likely a bug with ActsAsTenant. It's executing that query but in a context that doesn't know about the current tenant (maybe like a sub-process or something).
Chris Oliver
Actually, it's being cleared out after the post request in your test since it's a thread local variable. Seems like a bug in ActsAsTenant.

This works, for example:

  test "should create entity" do
    ActsAsTenant.current_tenant = @account
    assert_difference('Entity.count') do
      post entities_url, params: { entity: { description: @entity.description, name: @entity.name, account: @account } }
      ActsAsTenant.current_tenant = @account
    end
    assert_redirected_to entity_url(Entity.last)
  end

Another situation where relying on gems is a bad idea. We may have to rewrite ActsAsTenant and build our own that works better.
Jason Ackerman
 That worked for the controller test, thanks! Any idea on the system test?
Chris Oliver
System tests run in a real browser, so you'll want to run the same steps the user would have to in the browser. Have them switch to the correct account, then execute your test.

Basically:
1. click_on account in navbar
2. run test as normal

You can't use the switch_account helper there because you're running a real browser and you need to simulate real user interactions. They would click on the account in the navbar to switch, then fill out forms and click buttons.
Jason Ackerman
sorry for the delayed response here, and thanks for your help! It got me most of the way there. For anyone else looking to do this, here is what I ended up with (after much fiddling):

    visit entities_url #or some index page on your app where the nav_bar is visible
    find_by_id('navbar-dropdown-toggle').click #the css id of the div that contains the avatar and toggles the dropdown menu
    click_link "#{@account.name}", {wait: true}
    assert_text "Dashboard"

Without the assert_text there it was not waiting for the page to load and was running the tests before the account change was processed.
Calvin Walzel
I solved this tenancy issue for the tests by adding these two helpers to the `test_helper.rb`:

  def assert_difference(expression, *args, &block)
    wrapped_block = proc do
      ActsAsTenant.without_tenant do
        yield
      end
    end

    super(expression, *args, &wrapped_block)
  end

  def assert_difference_with_tenant(account, expression, *args, &block)
    ActsAsTenant.current_tenant = account

    wrapped_block = proc do
      result = yield
      ActsAsTenant.current_tenant = account
      result
    end

    assert_difference(expression, *args, &wrapped_block)

    ActsAsTenant.current_tenant = account
  end

Just posting this here in case anyone else still runs into this issue.
Donal O Duibhir
 thank you for this! 
Notifications
You’re not receiving notifications from this thread.
© 2022 Jumpstart Pro by GoRails, LLC