Sudden failure of redirect in ActionController +verify+


#1

= SUMMARY

An ActionController +verify+ directive redirects to an :action if
certain actions are not done by POST. In one case, the redirect is to an
empty hash instead; in another, nearly identical, it is to the correct
hash. This is seen in a functional test.

Will someone please tell me what is going wrong, and how I can correct
it?

= ENVIRONMENT

$ ruby --version
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]
$ rails --version
Rails 1.2.6

= DETAIL

(Much of this may be easier to see in the “CODE” section, below.)

AdminController is a subclass of ActionController, acting on instances
of Permission, an ActiveRecord subclass. Permission#approve and
Permission#deny set the :approval attribute to one string or another.
admin/approve/:id and admin/deny/:id pass the respective methods to the
id’ed Permission.

AdminController has a +verify+ directive to require that #approve and
#deny be done by POST. If they are not, the session is to :redirect_to
admin/list.

Class AdminControllerTest, a Test::Unit::TestCase, challenges the
+verify+ by using GET requests instead. For many iterations of the test,
AdminController passed. I then added some tests to AdminControllerTest;
I did not change test_deny_not_post or test_accept_not_post.

test_deny_not_post then started failing at assert_redirected_to. The
fail message is:

“response is not a redirection to all of the options supplied
(redirection is <{}>), difference: <{“action”=>:list}>”

test_approve_not_post, which is nearly identical, DOES NOT fail.

Changing the +verify+'s :redirect_to to { :controller => :admin, :action
=> :list } gets the tests past the assert_redirected_to, but fails the
follow_redirect (Can’t follow redirects outside of current controller
(from admin to admin)). This failure is in BOTH test_deny_not_post and
test_approve_not_post.

My controller is failing a functional test, and I don’t understand why.
Will someone please tell me what is going wrong, and how I can correct
it?

= CODE (abridged)

class AdminController < ApplicationController
verify :method => :post, :only => [ :destroy, :create, :update,
:deny, :approve ],
:redirect_to => { :action => :list },
:add_flash => { :notice => ‘You cannot do this directly.’ }

def approve
# before_filter sets @permission
@permission.approve
if @permission.save
flash[:notice] = ‘This request was approved.’
else
flash[:notice] = ‘Could not approve the request (internal
error).’
end
redirect_to :action => :list
end

def deny
# before_filter sets @permission
@permission.deny
if @permission.save
flash[:notice] = ‘This request was denied.’
else
flash[:notice] = ‘Could not deny the request (internal error).’
end
redirect_to :action => :list
end

class AdminControllerTest < Test::Unit::TestCase

#setup puts user credentials in @session

def test_approve_not_post
get :approve, { :id => 3 }, @session
assert_response :redirect
assert_redirected_to :action => :list
assert_equal ‘You cannot do this directly.’, flash[:notice]
end

def test_deny_not_post
get :deny, { :id => 3 }, @session
assert_response :redirect
assert_redirected_to :action => :list
assert_equal ‘You cannot do this directly.’, flash[:notice]
end
end


#2

I’ve narrowed the problem a bit. It now seems that follow_redirect in
one test will break an ActionController’s +verify+ directive in another,
later test.

I am ashamed to say my code fragment was not accurate. Here is the
correct code:

======
class AdminControllerTest < Test::Unit::TestCase
# #setup puts user credentials in @session

def test_approve_not_post
    get :approve, { :id => 3 }, @session
    assert_response :redirect
    assert_redirected_to :action => :list
    assert_equal 'You cannot do this directly.', flash[:notice]

    ### OMITTED IN ORIGINAL - SORRY ###
    follow_redirect
    assert_response :success
end

def test_deny_not_post
    get :deny, { :id => 3 }, @session
    assert_response :redirect
    assert_redirected_to :action => :list
    assert_equal 'You cannot do this directly.', flash[:notice]

    ### OMITTED IN ORIGINAL - SORRY ###
    follow_redirect
    assert_response :success
end

end

Note that I have the tests follow the redirect_to arising from the
failed +verify+.

I have discovered that if I comment-out the follow_redirect and
assert_response calls in test_approve_not_post, then +verify+ will
redirect correctly in test_deny_not_post. Remember it was the
assert_redirected_to, and the redirection hash, that broke.

So now: follow_redirect breaks an ActionController’s +verify+ handling.
before_filter and after_filter don’t have this problem. Is this a
peculiarity of functional tests – and can I therefore ignore it? I’d
rather be able to test the redirection.

Should I update beyond Rails 1.2.6? 2.x is not feasible for my
deployment.

Again, I’m sorry about mischaracterizing the code.