Stubbing Grape helper - ruby-on-rails

I have Rails app with Grape API.
The interface is done with Backbone and Grape API provides it all data.
All it returns is user-specific stuff, so i need reference to currently logged in user.
Simplified version looks like this:
API initialization:
module MyAPI
class API < Grape::API
format :json
helpers MyAPI::APIHelpers
mount MyAPI::Endpoints::Notes
end
end
Endpoint:
module MyAPI
module Endpoints
class Notes < Grape::API
before do
authenticate!
end
# (...) Api methods
end
end
end
API helper:
module MyAPI::APIHelpers
# #return [User]
def current_user
env['warden'].user
end
def authenticate!
unless current_user
error!('401 Unauthorized', 401)
end
end
end
So, as you can see, i get the current user from Warden and it works fine. But the problem is with testing.
describe MyAPI::Endpoints::Notes do
describe 'GET /notes' do
it 'it renders all notes when no keyword is given' do
Note.expects(:all).returns(#notes)
get '/notes'
it_presents(#notes)
end
end
end
How can I stub helpers's method *current_user* with some specific user?
I tried:
setting env/request, but it doesn't exist before calling get method.
stubbing MyAPI::APIHelpers#current_user method with Mocha
stubbing MyAPI::Endpoints::Notes.any_instance.stub with Mocha
Edit:
At the moment, it's stubbed this way:
spec:
# (...)
before :all do
load 'patches/api_helpers'
#user = STUBBED_USER
end
# (...)
spec/patches/api_helpers.rb:
STUBBED_USER = FactoryGirl.create(:user)
module MyAPI::APIHelpers
def current_user
STUBBED_USER
end
end
But it's definitely not the answer :).

comments mentioned in this issue should help you, It's how even Grape tests it's helpers,
https://github.com/intridea/grape/blob/master/spec/grape/endpoint_spec.rb#L475
(If the code is not there on the same line due to changes, just do a ctrl+f & look for helpers)
Here's some code from the same file
it 'resets all instance variables (except block) between calls' do
subject.helpers do
def memoized
#memoized ||= params[:howdy]
end
end
subject.get('/hello') do
memoized
end
get '/hello?howdy=hey'
last_response.body.should == 'hey'
get '/hello?howdy=yo'
last_response.body.should == 'yo'
end

Option 1
The recommended way is to use Grape::Endpoint.before_each:
context 'when user is logged in' do
before do
Grape::Endpoint.before_each do |endpoint|
allow(endpoint).to receive(:current_user).and_return(user)
end
end
after { Grape::Endpoint.before_each nil }
end
But this is quite verbose. It can live in a shared context, but you can't pass user as a parameter explicitly so you'd end up with:
let(:user) { create(:user) }
# ...
include_context 'signed in user'
Option 2
My preferred way is a more RSpec-like stubbing:
# helper
module AuthHelper
def current_user
# ...
end
end
# api
module API
module V1
class Auth < Grape::API
helpers AuthHelper
end
end
end
# spec
before do
allow_any_instance_of(AuthHelper).to receive(:current_user).and_return(user)
end
Option 3
You can also define helpers:
API::V1::User.helpers do
def current_user
user
end
end

Related

calling a helper on Grape API name space

Working with on an API using Grape API and was wondering if it was possible to call a function in the namespace before an api gets called. There are two ways that we validate permission within our API and instead of calling them in each API call I would like to do it in the namespace before hand.
api.rb looks a little something like this:
Module MyModule
class API < Grape::API
.
.
.
helpers do
.
.
.
def authenticate!
raise CanCan::AccessDenied unless authenticated?
end
def authorize!(*args)
# Not a super user and trying to access the API of a company other than your own?
if !current_user.super_user? && current_user.company != #company
raise CanCan::AccessDenied
end
Ability.new(current_user, host_parts).authorize!(*args)
end
end
################################################
# Clerk API
namespace :clerk do
authenticate!
resource: myResource do
**API code here**
end
end
end
end
Is it possible to call authenticate! for a whole namespace like i have below instead of calling it in each api?
You can just use a before block inside your namespace. Your code would be something like this:
Module MyModule
class API < Grape::API
helpers do
def authenticate!
raise CanCan::AccessDenied unless authenticated?
end
def authorize!(*args)
if !current_user.super_user? && current_user.company != #company
raise CanCan::AccessDenied
end
Ability.new(current_user, host_parts).authorize!(*args)
end
end
# Clerk API
namespace :clerk do
resource: myResource do
before do
authenticate!
end
end
end
end
end
The before block will be called before every call to your API.

How to handle authentication token string in Grape API and token_and_options?

I'm using Ember front-end with Grape API and to authenticate a page I have something like:
def current_user
return nil if headers['Authorization'].nil?
#current_user ||= User.find_by_authentication_token(
headers['Authorization']
)
end
At the moment I'm using ember-simple-auth which sends something like as a header:
Token token="n2Rs5tokenLH", email="my#email.com"
Check out the Grape logger:
User Load (38.1ms) SELECT "users".* FROM "users" WHERE "users"."authentication_token" = 'Token token="n2Rs5tokenLH", email="my#email.com"' LIMIT 1
Now in rails I usually use authenticate_or_request_with_http_token to handle this. and I don't want to handle this manually using stuff like gsub or regular expressions. What is the best way to handle this in Grape?
Please note that the Grape API is mounted inside a rails project and inside app/api/backend/v1/default.rb:
module Backend
module V1
module Defaults
extend ActiveSupport::Concern
included do
version 'v1', using: :path
format :json
prefix :api
rescue_from :all do |e|
Backend::Base.logger.error e
end
helpers do
def current_user
return nil if headers['Authorization'].nil?
#current_user ||= User.find_by_authentication_token(
headers['Authorization']
)
end
def authenticate!
error!('401 Unauthorized', 401) unless current_user
end
end
end
end
end
end
Edit:
I just found out that I can use ActionController::HttpAuthentication::Token.token_and_options to do the job, however I'm not sure how should I include this in Grape project.
The structure of the app looks like:
⇒ tree app/api
app/api
└── backend
├── base.rb
└── v1
├── defaults.rb
└── ....
I tried to include ActionController::HttpAuthentication::Token in defaults.rbsomething like:
helpers do
include ActionController::HttpAuthentication::Token
def current_user
return nil if headers['Authorization'].nil?
#current_user ||= User.find_by_authentication_token(
token_and_options(headers['Authorization'])
)
end
def authenticate!
error!('401 Unauthorized', 401) unless current_user
end
end
But now I'm getting:
undefined method `authorization' for #<String:0x007ff51cdb85f8>
You're on the right path: use token_params_from and pass in the Authorization header directly.
helpers do
include ActionController::HttpAuthentication::Token
def current_user
return nil if headers['Authorization'].nil?
#current_user ||= User.find_by_authentication_token(token)
end
def token
token_params_from(headers['Authorization']).shift[1]
end
def authenticate!
error!('401 Unauthorized', 401) unless current_user
end
end
The undefined method exception is due to token_and_options expecting a ActionDispatch::Request object where you're providing a String.
I had to add a customized token_and_options function which looks like:
def token_and_options(auth_string)
if header = auth_string.to_s[/^Token (.*)/]
values = $1.split(',').
inject({}) do |memo, value|
value.strip!
key, value = value.split(/\=\"?/)
value.chomp!('"')
value.gsub!(/\\\"/, '"')
memo.update(key => value)
end
values.delete("token")
end
end
And then parse token with something like: token_and_options(headers['Authorization'])

What's the right way to test rails controllers in isolation?

Even a seemingly simple index action feels incredibly complicated to test in isolation.
I find myself having to mock out several of my User and Tenant methods just to get through the before_filters. Then I need to mock out Kaminari and Tenant#users for the action.
This feels excessive for testing a controller action with no control flow.
TDD principle would say that an excessive need for mocking is a sign of poor design, but then I'm not sure how I would extract this functionality into a domain object.
Is this sort of painful mocking standard for testing Rails controllers? Is there better way to do this that I'm simply not aware of?
For instance, perhaps skipping before_filters would make this less painful, but as they are consequential private methods, I feel that skipping them is missing the point.
class UsersController < AdminController
before_filter :check_auth
before_filter :check_admin
around_filter :set_tenant_time_zone, if: current_tenant
def index
Kaminari.paginate(current_tenant.users).page(params[:page])
end
private
def current_user
# gets user from session
end
def current_tenant
current_user.tenant if current_user
end
def set_tenant_time_zone
Time.use_zone(current_tenant.time_zone, &block)
end
def check_auth
redirect_to login_url unless AuthChecker.new(current_user, request.remote_ip).has_access?
end
def check_admin
redirect_to root_url unless current_user.is_admin?
end
end
You have to do all those mocks/stubs if you want to run those before_filters but I think, that, for those cases, is better to use some spec helper method to create a logged in user so, on your spec, you only need to call that method on a "before(:each)" block of your controller where you want a user.
In spec_helper.rb:
def current_user(stubs = {})
unless #current_user
u = FactoryGirl.build(:user, stubs)
u.save(:validate => false)
#current_user = u
end
#current_user
end
def current_user_session(stubs = {}, user_stubs = {})
#current_session ||= mock_model("Session", {:record => nil, :user => current_user(user_stubs)}.merge(stubs))
end
def login(session_stubs = {}, user_stubs = {})
UserSession.stub(:find).and_return(current_user_session(session_stubs, user_stubs))
controller.stub(:current_user => #current_user)
end
so, on the controller specs that require a logged in user with some special stub I can do
describe 'GET index' do
before(:each) do
login #this does all you need to pass the filters
end
it 'does something' do
current_user.stub(:some_method)
get :index
expect(response).to something
end
end
that way the test only has stubs, instances and expectations for the actual code of the action and not the filters

Rails4 + Authlogic + rspec

Using Rails 4, I am having issues getting Authlogic to see my faked UserSession.
I have set up pages#whoami to render the current user's email address, as a simplistic test.
class PagesController < ApplicationController
# before_filter :require_user
def whoami
render :text => current_user.try(:email) || 'anonymous'
end
end
in spec/spec_helper.rb:
require "authlogic/test_case"
include Authlogic::TestCase
and my rspec test:
require 'spec_helper'
describe '/whoami' do
setup :activate_authlogic
it "should tell me who I am" do
user = FactoryGirl.create(:user)
user.should be_valid
session = UserSession.create(user)
session.should be_valid
get '/whoami'
response.body.should == user.email
end
end
I updated my application controller to show the current session:
def require_user
unless current_user
raise "Current User Session is: #{ current_user_session.inspect}"
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to new_user_session_url
return false
end
end
With before_filter :require_user commented, I correctly get "anonymous". When I uncomment it, I see that my user session is nil. I tried looking through the authlogic code but got lost in Authlogic::Session:Persistence::InstanceMethods#persisting?
I'm trying to debug. Here's where I am so far.
Here, we try to set Authlogic::Session::Base.controller to the test's mock controller:
https://github.com/binarylogic/authlogic/blob/master/lib/authlogic/test_case.rb#L109
in my spec, I see that #controller is a Authlogic::TestCase::MockController
and in my spec, I see that Authlogic::Session::Base.controller is set to that Mock Controller.
However, I then check this:
class ApplicationController < ActionController::Base
...
def current_user_session
raise Authlogic::Session::Base.controller.inspect
...
end
end
and I see Authlogic::ControllerAdapters::RailsAdapter ... so somehow the controller is being set but isn't persisting. I'm wondering whether this has to do with the switch from Rails3 to Rails4?
Any insight into this would be appreciated.
Gem versions for those who are interested:
gem rspec-core (2.14.5)
gem authlogic (3.3.0)
gem rails (4.0.0)
Per https://stackoverflow.com/a/5803121, a request spec is just a thin wrapper around ActionDispatch::IntegrationTest. As such, there is no direct access to the session, unlike a normal controller spec.
Due to this, it isn't directly possible to log a user in directly with AuthLogic, which does rely on the session and cookies:
It first authenticates, then it sets up the proper session values and cookies to persist the session.
For request/integration/api/feature specs, a request directly to the login path will be necessary to set the proper session / cookies behind the scenes. The integration session will then be sent back (just like a normal web request) with the proper values.
To make life easier you can add a helper method, which you can include for request/integration/api/feature specs:
# spec/support/auth_logic_helpers.rb
module Authlogic
module TestHelper
# You can call this anything you want, I chose this name as it was similar
# to how AuthLogic calls it's objects and methods
def create_user_session(user)
# Assuming you have this defined in your routes, otherwise just use:
# '/your_login_path'
post user_session_path, login: user.login, password: user.password
end
end
end
# Make this available to just the request and feature specs
RSpec.configure do |config|
config.include Authlogic::TestHelper, type: :request
config.include Authlogic::TestHelper, type: :feature
end

How to refactor helper methods in RSpec files?

I am using Ruby on Rails 3.1.0 and the rspec-rails 2gem. I would like to refactor the following code (I have intentionally omitted some code and I have given meaningful names in order to highlight the structure):
describe "D1" do
# Helper method
def D1_Method_1
...
end
context "C1" do
# Helper methods
def D1_C1_Method_1
session.should be_nil # Note: I am using the RoR 'session' hash
D1_Method_1 # Note: I am calling the 'D1_Method_1' helper method
...
end
def D1_C1_Method_2
...
end
it "I1" do
D1_Method_1
...
end
it "I2" do
...
D1_C1_Method_1
D1_C1_Method_2
end
end
context "C2" do
# Helper methods
def D1_C2_Method_1
...
end
def D1_C2_Method_2
...
end
it "I1" do
D1_Method_1
...
end
it "I2" do
...
D1_C2_Method_1
D1_C2_Method_2
end
end
end
What can\should I make in order to refactor the above code?
P.S.: I have tried to extract helper methods in an external module (named Sample) but, for example relating to the D1_C1_Method_1 method (that contains the RoR session), I get the following error when I run the spec file:
Failure/Error: session.should be_nil
NameError:
undefined local variable or method `session' for Sample:Module
Have you tried to include the helpers as an external module?
require 'path/to/my_spec_helper'
describe "D1" do
include MySpecHelper
...
end
And now the helper:
# my_spec_helper.rb
module MySpecHelper
def D1_C1_Method_1
session.should be_nil
...
end
end

Resources