Forum: Ruby on Rails Confusion about HTTP header content during functional tests

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Wes G. (Guest)
on 2008-10-03 19:29
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
Frederick C. (Guest)
on 2008-10-03 19:43
(Received via mailing list)
On 3 Oct 2008, at 16:29, Wes G. wrote:

> 3) 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
Wes G. (Guest)
on 2008-10-03 19:59
Frederick C. wrote:
> On 3 Oct 2008, at 16:29, Wes G. wrote:
>
>> 3) 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
Frederick C. (Guest)
on 2008-10-03 20:31
(Received via mailing list)
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. <removed_email_address@domain.invalid>
Wes G. (Guest)
on 2008-10-03 22:04
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
This topic is locked and can not be replied to.