How do I emulate logging in for controller tests? - ruby-on-rails-3

I have a SearchesController that requires a user to be logged in before it will do its thing.
I'd like to write an rspec helper function login to emulate logging in for controller tests. (NB: I will handle integration / requests specs separately.) My attempts so haven't worked: the logged_in? method in ApplicationController returns false.
The question: how do I write the 'login' helper?
Here's the RSpec controller test:
# file: spec/controllers/searches_controller_spec.rb
require 'spec_helper'
require 'controllers_helper'
describe SearchesController do
include ControllersHelper
describe "GET index" do
it 'without login renders login page' do
get :index
response.should redirect_to(login_path)
end
it 'with login finds searches belonging to user' do
me = FactoryGirl.create(:user)
my_searches = FactoryGirl.create_list(:search, 2, :user => me)
not_me = FactoryGirl.create(:user)
not_my_searches = FactoryGirl.create_list(:search, 2, :user => not_me)
login(me) # want to define this in spec/controllers_helper.rb
get :index
assigns(:searches).should =~ my_searches
end
end
end
Here's the Controller:
# file: app/controllers/searches_controller.rb
class SearchesController < ApplicationController
def index
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
else
#searches = Search.where(:user_id => current_user.id)
respond_to do |format|
format.html
format.json { render json: #searches }
end
end
end
end
And here's the ApplicationController code. Note that current_user = x has the effect of logging x in, and it's rather simple: it sets #current_user and session[:user_id].
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
force_ssl
protected
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
def current_user=(user)
#current_user = user
session[:user_id] = user && user.id
end
def logged_in?
!!#current_user
end
def require_login
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
end
end
helper_method :current_user, :logged_in?, :require_login
end

I may have said this before, but if Stack Overflow gave badges answering one's own questions, I'd have a LOT of badges! :)
Okay, to answer this question you need to look at the documentation for ActionController::TestCase. When you do so, you'll find that it sets up bindings for:
#controller
#request
#response
So for the specific controller given in the OP, writing the login method is trivial:
# file: spec/controllers_helper.rb
module ControllersHelper
def login(user)
#controller.send(:current_user=, user)
end
end
(Did I hear someone say RTFM again? I thought so...)

Related

Rails 4 and Devise - User registration via JSON API

I'm attempting to register a devise user via JSON but keep getting an ActiveModel::ForbiddenAttributesError
class Api::V1::RegistrationsController < ApplicationController
skip_before_filter :verify_authenticity_token
respond_to :json
def create
user = User.new(params[:user])
if user.save
render :json => user.as_json(:auth_token=>user.authentication_token, :email=>user.email), :status=>201
return
else
warden.custom_failure!
render :json => user.errors, :status=>422
end
end
def user_params
params.require(:user).permit(:email, :password)
end
end
Here's my JSON request:
Processing by Api::V1::RegistrationsController#create as JSON
Parameters: {"user"=>{"email"=>"jayson#jayson.com", "password"=>"[FILTERED]"}, "registration"=>{"user"=>{"email"=>"jayson#jayson.com", "password"=>"[FILTERED]"}}}
I realize this has something to do with Strong Parameters but haven't been able to crack it yet.
I would change:
user = User.new(params[:user])
with:
user = User.new(user_params)
From docs:
# This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
# without an explicit permit step.
def create
Person.create(params[:person])
end

Rspec controller error? expecting <"index"> but rendering with <""> or it's working?

I'm new with rspec test and maybe there are something that I dont undertand.
if can any help me, I really appreciate some help.
File Structure:
app/models/booking.rb
app/models/user.rb
app/models/role.rb
app/models/ability.rb
app/controllers/bookings_controller.rb
app/views/bookings/index.html.erb
app/views/dashboard/index.html.erb
app/spec/controllers/bookings_controller_spec.rb
I read this link with a similar problem but it isn't solved
Rspec controller error expecting <"index"> but rendering with <"">
is similar, because if I change this line:
it 'should not render index template from bookings' do
get :index
=> response.should_not render_template(:index)
end
for this other:
it 'should not render index template from bookings' do
get :index
=> response.should render_template(:index)
end
I get the same mistake that in the link
expecting <"index"> but rendering with <"">
and I don't know why?
Here's my Code:
My Spec:
describe BookingsController do
context 'as guest' do
before(:each) do
#user = User.new(:email => 'mail_admin#test.com',
:username => 'admin',
:password => 'password_admin',
:password_confirmation => 'password_admin')
#user.save
#when i save, with gem CanCan i assign a default role to #user
#with the default role the user only can see the views/dashboard/index.html.erb
end
it 'should not render index template from bookings' do
get :index
response.should_not render_template(:index)
end
end
end
Controller:
class BookingsController < ApplicationController
load_and_authorize_resource
def index
...
end
def show
...
end
end
My model:
class Booking < Activerecord::Base
paginates_per 20
def
...
end
def
...
end
end
User:
Class User < ActiveRecord::Base
after_save :set_default_role
rolify
.
.
.
.
def set_default_role
self.add_role :default
end
end
Role:
class Role < ActiveRecord::Base
ROLES = {"admin" => "Admin", "default" => "Default"}
.
.
.
.
scopify
end
Ability:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :data_consistency
can :read, Booking
end
end
end
CanCan authorizes model access not controller actions. For most other actions these two are more or less the same thing, but not for the index. On the index action CanCan adds a scope to the query for records that includes your authorization restrictions.
What this means is that your guest user will simply not be able to see any records, but the view will still render.
What you want is authentication (ie Devise) and use it from a before_filter in each controller that requires an authenticated user to access.
class BookingsController < ApplicationController
load_and_authorize_resource # Handles authorization
before_filter !authenticate_user # Handles authentication (included with Devise)
...
end
In my case, the problem was solved in before(:each) block!
My code works like this:
before :each do
#user = User.new(:email => 'mail_admin#test.com',
:username => 'admin',
:password => 'password_admin',
:password_confirmation => 'password_admin')
#user.confirm!
sign_in #user
end

Rails use not condition with before_filter

I'm using before_filter in my application. I have a method logged_in? which returns true if the user is logged in.
def logged_in?
!!current_user
end
def current_user
#current_user = (User.find(session[:user_id]) if session[:user_id]) || false
end
Now in my users controller I want an action to execute only if a user is not logged in. For this I want to use the not condition with logged_in? method in before_filter as:
before_filter :!(logged_in?)
But this gives me an error. I'm resisting creating a new method for not logged in.
Please help me figure out the correct syntax to accomplish this.
While the accepted answer seems to work, I would have done it differently.
before_filter :login_required, unless: :logged_in?
def login_required
redirect_to login_path, notice: 'Please login'
end
This will execute the method login_required unless the user is already logged in. See http://robots.thoughtbot.com/post/159805303/before-filter-wisdom for more info.
You could pass a block to before_filter:
before_filter { |c| !c.logged_in? }
But this wouldn't really do anything, since the return value from the before filter isn't going anywhere. If you want to execute an action if a user is not logged in, then you should be putting that action into the before_filter.
For example, if the action was to redirect to the login page, path, you could do this:
before_filter { |c| redirect_to login_path unless c.logged_in? }
That's actually long enough to justify a method of its own:
before_filter :login_required
def login_required
redirect_to login_path unless logged_in?
end
application_controller.rb
before_filter :authorize
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
helper_method :current_user
protected
def authorize
unless User.find_by(id: session[:user_id])
redirect_to login_url, :notice => "Not Authorize Member"
end
end

How to create current user method without using any gem or plugin?

i know it's a silly one but i want to know how can we create a current_user method to get access throughout the app without using any gem or plugin ? To test it i created an app that make a user able to share files and folders.How to create such method that a user can only access his folder and files?Here is my code sample:
Login controller:
class LoginController < ApplicationController
layout 'signup'
#to skip checking the authentication and authorization.
skip_before_filter :check_authentication, :check_authorization
def index
end
def authenticate
if request.post?
user = User.authenticate(params[:username],params[:password])
if user
session[:current_user_id]=user.id
session[:name]= user.first_name
puts "session name #{session[:name]}"
redirect_to(:subdomain => user.company.subdomain, :controller => :dashboard)
else
flash.now[:notice] = "Invalid user/password combination"
end
end
end
def destroy
session[:current_user_id] = nil
reset_session
flash[:notice] = "You have been successfully logged out."
redirect_to root_url
end
end
User model:
require 'digest/sha1'
class User < ActiveRecord::Base
#sharering method start
after_create :check_and_assign_shared_ids_to_shared_folders
#this is to make sure the new user ,of which the email addresses already used to share folders by others, to have access to those folders
def check_and_assign_shared_ids_to_shared_folders
#First checking if the new user's email exists in any of ShareFolder records
shared_folders_with_same_email = SharedFolder.find_all_by_shared_email(self.email)
if shared_folders_with_same_email
#loop and update the shared user id with this new user id
shared_folders_with_same_email.each do |shared_folder|
shared_folder.shared_user_id = self.id
shared_folder.save
end
end
end
#to check if a user has acess to this specific folder
def has_share_access?(folder)
#has share access if the folder is one of one of his own
return true if self.folders.include?(folder)
#has share access if the folder is one of the shared_folders_by_others
return true if self.shared_folders_by_others.include?(folder)
#for checking sub folders under one of the being_shared_folders
return_value = false
folder.ancestors.each do |ancestor_folder|
return_value = self.being_shared_folders.include?(ancestor_folder)
if return_value #if it's true
return true
end
end
return false
end
#sharing method end
def self.authenticate(name, password)
user = self.find_by_username(name)
if user
expected_password = encrypt_password(password, user.salt)
if user.hashed_password != expected_password
user = nil
end
end
user
end
#'password' is a virtual attribute
def password
#password
end
def password= (pwd)
#password =pwd
return if pwd.blank?
create_new_salt
self.hashed_password = User.encrypt_password( self.password, self.salt)
end
def self.users_in_company(user_id)
User.find(user_id).company.users
end
private
def password_non_blank
errors.add(:password, "Missing password, please enter your password") if hashed_password.blank?
end
def create_new_salt
self.salt = self.object_id.to_s + rand.to_s
end
def self.encrypt_password(password, salt)
string_to_hash = password +"prftnxt" + salt
Digest::SHA1.hexdigest(string_to_hash)
end
end
i want to access all files as "current_user.files" is it possible without any gem?
Application helper:
module ApplicationHelper
#for current user to use through out the app
def current_user
#current_user ||= session[:current_user_id] && User.find_by_id(session[:current_user_id]) # Use find_by_id to get nil instead of an error if user doesn't exist
end
end
Application controller:
class ApplicationController < ActionController::Base
include UrlHelper
#include ApplicationHelper
helper_method :current_user #make this method available in views
protect_from_forgery
# def current_user
# #current_user ||= session[:current_user_id] && User.find_by_id(session[:current_user_id]) # Use find_by_id to get nil instead of an error if user doesn't exist
# end
end
and in task controller:
class TasksController < ApplicationController
# GET /tasks
# GET /tasks.xml
def index
#menu = "Timesheet"
#page_name = "Manage Task"
company_id = Company.find_by_subdomain(request.subdomain)
#users = User.find_all_by_company_id(company_id)
#tasks = current_user.tasks.all#Task.all
#task = Task.new
respond_to do |format|
format.html # index.html.erb
format.html # new.html.erb
format.xml { render :xml => #tasks }
end
end
end
and my error message i got:
NameError in TasksController#index
undefined local variable or method `current_user' for #<TasksController:0xfa7e638>
that's not so hard ;) just define the method you need:
class ApplicationController < ...
def current_user
#current_user ||= session[:current_user_id] && User.find_by_id(session[:current_user_id]) # Use find_by_id to get nil instead of an error if user doesn't exist
end
helper_method :current_user #make this method available in views
end
Hi friends i have found the way to create current_user method without using any gem or plugin:
In my application_helper.rb i did this :
module ApplicationHelper
def current_user
User.find(session[:current_user_id])
end
end
and at the end in my application controller.rb i called this, because from here i can access it through the application:
class ApplicationController < ActionController::Base
include UrlHelper
include ApplicationHelper
helper_method :current_user
end
and now i can access any data related to current user:
like :
#tasks = current_user.tasks
Thanks to all my friends for their valuable answers.

rspec controller test with devise authentication

I am having problem with rspec testing controller the devise authentication.
I have a following setup
I have included
config.include Devise::TestHelpers, :type => :controller
in my spec_helper.rb
In my merchants_controller_spec.rb
describe MerchantsController do
before :each do
#user = Factory(:user)
#merchant = Factory(:merchant, :user_id => #user.id,:is_approved => false, :is_blacklisted => false)
controller.stub!(:current_user).and_return(#user)
end
describe "GET index" do
it "assigns all merchants as #merchants" do
merchant = Factory(:merchant,:is_approved => true, :is_blacklisted => false)
get :index
assigns(:merchants).should eq([merchant])
end
end
end
My merchants_controller.rb
class MerchantsController < ApplicationController
before_filter :authenticate_user!
def index
#merchants = Merchant.approved
debugger
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #merchants }
end
end
end
I have a scope approved in merchant model
scope :approved, where(:is_approved => true, :is_blacklisted => false)
Now my problem is even though i stubbed current_user and returned #user as current_user, My merchants_controller index spec is failing. But if i comment out authenticate_user! then the spec passes,
without authenticate_user! the debugger of index action is caught but with authenticate_user! debugger is not caught.
I think there is problem in subbing current_user and i am not able to figure it out.
Help me out..
Have you read through the docs on github?:
Devise includes some tests helpers for functional specs. To use them, you just need to include Devise::TestHelpers in your test class and use the sign_in and sign_out methods. Such methods have the same signature as in controllers:
sign_in :user, #user # sign_in(scope, resource)
sign_in #user # sign_in(resource)
sign_out :user # sign_out(scope)
sign_out #user # sign_out(resource)
Another alternative
RSpec.describe YourController, :type => :controller do
before do
user = FactoryGirl.create(:user)
allow(controller).to receive(:authenticate_user!).and_return(true)
allow(controller).to receive(:current_user).and_return(user)
end
# rest of the code
end