Confusion about HTTP header content during functional tests

I’m seeing some header output from various HTTP requests and responses
that doesn’t make sense to me.

I have a couple of functional tests that appear to work ok. In the
course of writing them, I went ahead and created a couple of methods to
dump the request and response headers to help me debug and added them to
test_helper.rb. They are below:

def debug_request
puts “\nREQUEST HEADERS\n”
@request.headers.each do |header, value|
puts “Header: #{header}\tValue: #{value}”
end
end

def debug_response
puts “\nRESPONSE HEADERS\n”
@response.headers.each do |header, value|
puts “Header: #{header}\tValue: #{value}”
end
end

Most of my tests require that the user be logged in before they can do
anything, so I have a “login” method in test_helper.rb as well that does
a login - I then use this in my setup methods in my various tests.

Here’s one of my tests - note the inclusion of
debug_request/debug_response in it:

class DraftInvoiceSpreadsheetsControllerTest <
ActionController::TestCase
def setup
super
login
end

def test_get_template
filename = ‘invoice_template.xlt’
assert_not_nil(File.new(“#{RAILS_ROOT}/public/#{filename}”), “Excel
template doesn’t exist”)
get :get_template
debug_request
debug_response
assert_response(:success)
assert_equal(@response.headers[‘type’], “application/octet-stream”,
“Mime type is incorrect.”)
assert_equal(@response.headers[‘Content-Disposition’], “attachment;
filename="#{filename}"”, “Attachment should have filename
#{filename}”)
assert_equal(@response.headers[‘Content-Transfer-Encoding’],
‘binary’, “Content should be binary”)
end
end

The output of debug_request is:

REQUEST HEADERS
Header: REMOTE_ADDR Value: 0.0.0.0
Header: SERVER_PORT Value: 80
Header: REQUEST_METHOD Value: GET
Header: REQUEST_URI Value: /account/login?login=admin&password=demo

and the output of debug_response is:

RESPONSE HEADERS
Header: Cache-Control Value: private
Header: cookie Value:
Header: Status Value: 200 OK
Header: Location Value: http://test.host/user
Header: X-Runtime Value: 0.02122
Header: type Value: application/octet-stream
Header: Content-Length Value: 47104
Header: Content-Disposition Value: attachment;
filename=“invoice_template.xlt”
Header: Content-Transfer-Encoding Value: binary

When I look at this output, I end up with the following questions:

  1. Why is the request showing the first request to /account/login
    instead of the request to :get_template?

  2. Why is the method on that request to /account/login showing as a GET
    instead of a POST?

  3. Why is the Location header on the response showing to be what it
    would be right after a login?

It almost looks like the @request and @response headers are only updated
across multiple requests in the same functional tests and not reset.
The Content-Length, Content-Disposition, and Content-Transfer-Encoding
are clearly from my :get_template request, which is asking for an Excel
template file.

If I call debug_request/debug_response right after the login in addition
to where I call it above, the output is different (shows methods as POST
instead of GET for example). It definitely looks like the @request and
@response headers are added to/updated and not cleared between requests.

Can anyone shed any light on how @request and @response are
modified/handled during functional tests?

Thanks,
Wes

On 3 Oct 2008, at 16:29, Wes G. wrote:

  1. Why is the Location header on the response showing to be what it
    would be right after a login?

Does your login method actually do a request? that would explain
everything. @request, @response etc… are not reset if you perform
multiple requests from a single functional test.

Fred

Well you cab either create fresh controller/request/response objects,
or (what I would do) change your login helper to just fake up the
session to make it looked like the person is logged in (play with
@request.session )

Sent from my iPhone

On 3 Oct 2008, at 16:59, Wes G. [email protected]

Frederick C. wrote:

On 3 Oct 2008, at 16:29, Wes G. wrote:

  1. Why is the Location header on the response showing to be what it
    would be right after a login?

Does your login method actually do a request? that would explain
everything. @request, @response etc… are not reset if you perform
multiple requests from a single functional test.

Fred

Yeah it does. Here’s the login method:

def login(user = ‘blah’, password = ‘blah’)
old_controller = @controller
@controller = AccountController.new
post :login, :login => user, :password => password
assert_response(:redirect)
assert_equal(flash[:notice], “Logged in successfully”, “Should be
logged in.”)
@controller = old_controller
end

Thanks, Fred. Just verifying.

Wes

I saw that you can just manipulate the session data to handle this, and
that is simpler.

But of course, I’m stubborn and I preferred the idea of actually “doing”
the login as part of the functional test.

I have worked around it, and here’s the code that does it:

def login(user = ‘blah’, password = ‘blah’)
old_controller = @controller
@controller = AccountController.new
post :login, :login => user, :password => password
assert_response(:redirect)
assert_equal(flash[:notice], “Logged in successfully”, “Should be
logged in.”)
@controller = old_controller
reset_request_and_response
end

#This allows the debug_request and debug_response methods to be used
in a multi-request functional test
#and actually show the correct information.
def reset_request_and_response
#Store the current request
@previous_request = @request

#Make new request and response objects
@controller.request = @request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new

#Move the session data over so we don't lose any context.
@previous_request.session.data.each do |k, v|
  @request.session[k] = v
end

end

The “login” method calls “reset_request_and_response” which creates a
new TestRequest and TestResponse and moves the session data over so
that, among other things, the request knows that it is still logged in.
Then the dumps of the request and response look correct given the
request that I’m really interested in.

Also, now you could have multi-request functional tests if you wanted to
(although strictly speaking, you probably shouldn’t since each test
should test only one interaction).

Wes