I'm working on a rails project in which I use CanCan to authorize my resources. When a user is not signed in and tries to submit a "talk" (via an ajax form submission), CanCan correctly raises a 401 with {"status":"error","message":"You must be logged in to do that!"}
as the response (I have verified this in the browser using firebug). However, in my tests get a 302 response code rather than a 401:
class TalksController < ApplicationController
authorize_resource
def create
@talk = current_user.talks.build(params[:talk])
respond_to do |format|
if @talk.save
response = { :redirect => talk_path(@talk) }
format.html { redirect_to @talk, notice: 'Talk was successfully created.' }
format.json { render json: response, status: :created, }
else
format.html { render action: "new" }
format.json { render json: @talk.errors, status: :unprocessable_entity }
end
end
end
end
talks_controller_spec.rb:
describe TalksController do
describe "POST create" do
context "when not signed in" do
it "should not assign talk" do
post :create
assigns[:talk].should be_nil
end
it "should respond with a 401" do
post :create
response.response_code.should == 401
end
end
end
end
The first example included here is successful (assigns[:talk] does not get assigned), but the second is not:
1) TalksController POST create when not signed in should respond with a 401
Failure/Error: response.response_code.should == 401
expected: 401
got: 302 (using ==)
# ./spec/controllers/talks_controller_spec.rb:53:in `block (4 levels) in <top (required)>'
I'm not sure exactly what's going on. Is there a way I can test for the actual response code returned to the browser? Or a better way I can test the authorization?
As it turned out, my project rescued authorization exceptions from CanCan with the following function. Because the function only raises a 401 when the request is ajax (and redirects otherwise), I was getting a 401 in the browser, but not my tests.
# Handle authorization exceptions
rescue_from CanCan::AccessDenied do |exception|
if request.xhr?
if signed_in?
render json: {:status => :error, :message => "You don't have permission to #{exception.action} #{exception.subject.class.to_s.pluralize}"}, :status => 403
else
render json: {:status => :error, :message => "You must be logged in to do that!"}, :status => 401
end
else
render :file => "public/401.html", :status => :unauthorized
end
end
Thanks to zetetic for suggesting I check my test logs, as this revealed the difference in requests.