Austin Story

Ruby, Rails and Javascript Blog

Powered by Genesis

Testing Mulitenancy with Minitest and Capybara

February 16, 2015 By Austin Story Leave a Comment

Lately I have used an outside in approach to building my web application in ruby on rails.  What that means is that I am driving my application development by building integration tests.  I picked up this technique a year or so ago from thoughtbot.  So far, i have really enjoyed the insight it has given me and how much less code I write than when i was doing things by building models first.

Many of the applications I end up building as a consultant are multi-tenant application.  So there are many customers that have to live in strict or limited scope from each other.  I have built a really simple helper that I have been using and wanted to share it below in my TestCase class description

[code]

class ActiveSupport::TestCaseActiveRecord::Migration.check_pending!
include Warden::Test::Helpers
fixtures :all

def as_user(user)
login_as users(user.to_sym)
yield
logout
end
end

[/code]

 

It is super simple and I was a little hesitant to put it out there.  All it does is a login the user, execute the block that is passed in and then log the user out.  In a multitenant app though it is really imporant to check your scopes based on the type of user that is logging in (admin, super admin, org admin, user, anonymous, etc).  On top of that it adds to some very understandable tests.  Ozark and nixa are just two random cities in my area.

 

[code]

scenario "Employee cannot visit other organizations tickets" do
as_user(:ozark_employee) do
ozark_ticket = organizations(:ozark).tickets.first
visit ticket_path(ozark_ticket)

page.text.must_include unauthorized_error
end
end

scenario "Can edit own tickets" do
as_user(:ozark_employee) do
visit tickets_path
click_link 'Edit'
fill_in 'Description', :with => 'Some new description xxx'
click_button 'Update Ticket'

page.text.must_include 'successfully updated'
page.text.must_include 'Some new description xxx'
end
end

[/code]

This one simple helper has really tied in with my multitenant outside in development and sped up my dev time when building out how different types of users should experience an application.

Filed Under: Minitest, Programming, Testing Tagged With: bdd, minitest, ruby on Rails, tdd

Script to generate fixtures from a database

February 7, 2015 By Austin Story 1 Comment

So I have used minitest for about 8 months now for testing and all in all it turns out that I loved it more than I expected.  I just moved back to an in progress in house project written using Rspec with FactoryGirl and realized I want my minitest and  fixtures back.  So i found a great script that will generate fixturess based on my database seed.

 

[ruby]
sql = "SELECT * FROM %s"
skip_tables = ["schema_info"]
ActiveRecord::Base.establish_connection
ActiveRecord::Base.connection.tables.each do |table_name|
i = "000"
File.open("#{Rails.root}/test/fixtures/#{table_name}.yml", 'w') do |file|
data = ActiveRecord::Base.connection.select_all(sql % table_name)
file.write data.inject({}) { |hash, record|
hash["#{table_name}_#{i.succ!}"] = record
hash
}.to_yaml
end
end

[/ruby]

Filed Under: Minitest, Ruby, Ruby on Rails

Pundit and Rolify Testing with Rails

September 21, 2014 By Austin Story Leave a Comment

Just ran into an oddity that I didn’t expect (due to a false assumptions) when testing permissions and wanted to send it out to the world.

I have an app with two types of roles; :admin or :user. I am using Rolify in combination with Pundit to perform the role based authorization of REST actions.

I mixin a module containing general purpose authorization classes. I do it this way because permissions schemes tend to change alot and centralization makes it easier to change. You could easily just add these to your pundit policy classes.

[ruby]
def is_admin?
user.has_role?(:admin)
end

def is_user?
user.has_role?(:user)
end

def is_allowed?
user.has_role?(:user, record)
end
[/ruby]

Pretty straight-forward. An administrator has the role of :admin, a user has a role of :user and a :user is only allowed to access records that they are explicitly granted permission on.

My error was the assumption that user.has_role?(:user) would return true if a user had a role of :user explicitly set on any object in the system. In a pundit action it would look something like this.

[ruby]
def index?
is_admin? || is_user?
end
[/ruby]

The problem is that this was returning false unless someone had a :user role set in general, not on a specific instance of a record.

I think code clears this up further, here is the error.

[ruby]
#Assign the user role of :user on a specific object instance
User.first.add_role(:user, SomeObject.first)
User.first.has_role?(:user) #= false

#Assign the user a role of :user
User.first.add_role(:user)
User.first.has_role?(:user) #= true
[/ruby]

So i had to alter this to use see if the User had the role of :user on any object in the system.

[ruby]
#Old definition, broken
def is_user?
user.has_role?(:user)
end

#New definition, fixed!
def is_user?
SomeObject.find_roles(:user, user).any?
end
[/ruby]

The new is_user? returns true when the user has a role of :user on any SomeObject in the system. Which is exactly what we need.

Lesson learned, I assumed that the method worked a certain way and lost several hours of debugging in my unit tests. I normally will go straight to IRB when i run into anomalies but because i strongly help my assumption I didn’t.

Filed Under: Integrations, Minitest, Ruby, Ruby on Rails Tagged With: Pundit, Rails, Rolify, ruby on Rails

Categories

  • AngularJS
  • Books
  • Devise
  • Elasticsearch
  • ES6
  • Information Security
  • Integrations
  • Javascript
  • Linux
  • Minitest
  • PhoneGap
  • Programming
  • React
  • Redux
  • Ruby
  • Ruby on Rails
  • Stripe
  • Testing
  • Theory
  • TypeScript
  • Uncategorized
  • Vue
  • Webpack