From ebec9d43e262d28d742ff10acd828bad6cbb28ed Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 7 Dec 2008 16:37:48 -0600 Subject: Make integration test runner more Rack friendly and clean out old CGI cruft --- actionpack/lib/action_controller/integration.rb | 153 ++++++++++++++--------- actionpack/lib/action_controller/rack_process.rb | 9 +- actionpack/test/controller/integration_test.rb | 47 ++----- actionpack/test/controller/rack_test.rb | 8 +- 4 files changed, 110 insertions(+), 107 deletions(-) diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index abec04aea6..eeabe5b845 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -9,13 +9,17 @@ module ActionController # multiple sessions and run them side-by-side, you can also mimic (to some # limited extent) multiple simultaneous users interacting with your system. # - # Typically, you will instantiate a new session using IntegrationTest#open_session, - # rather than instantiating Integration::Session directly. + # Typically, you will instantiate a new session using + # IntegrationTest#open_session, rather than instantiating + # Integration::Session directly. class Session include Test::Unit::Assertions include ActionController::TestCase::Assertions include ActionController::TestProcess + # Rack application to use + attr_accessor :application + # The integer HTTP status code of the last request. attr_reader :status @@ -57,7 +61,8 @@ module ActionController end # Create and initialize a new Session instance. - def initialize + def initialize(app) + @application = app reset! end @@ -76,11 +81,13 @@ module ActionController self.host = "www.example.com" self.remote_addr = "127.0.0.1" - self.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" + self.accept = "text/xml,application/xml,application/xhtml+xml," + + "text/html;q=0.9,text/plain;q=0.8,image/png," + + "*/*;q=0.5" unless defined? @named_routes_configured # install the named routes in this session instance. - klass = class<application/x-www-form-urlencoded or multipart/form-data). + # (application/x-www-form-urlencoded or + # multipart/form-data). # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will # automatically be upcased, with the prefix 'HTTP_' added if needed. # - # This method returns an AbstractResponse object, which one can use to inspect - # the details of the response. Furthermore, if this method was called from an - # ActionController::IntegrationTest object, then that object's @response - # instance variable will point to the same response object. + # This method returns an AbstractResponse object, which one can use to + # inspect the details of the response. Furthermore, if this method was + # called from an ActionController::IntegrationTest object, then that + # object's @response instance variable will point to the same + # response object. # # You can also perform POST, PUT, DELETE, and HEAD requests with +post+, # +put+, +delete+, and +head+. @@ -182,22 +193,26 @@ module ActionController process :get, path, parameters, headers end - # Performs a POST request with the given parameters. See get() for more details. + # Performs a POST request with the given parameters. See get() for more + # details. def post(path, parameters = nil, headers = nil) process :post, path, parameters, headers end - # Performs a PUT request with the given parameters. See get() for more details. + # Performs a PUT request with the given parameters. See get() for more + # details. def put(path, parameters = nil, headers = nil) process :put, path, parameters, headers end - # Performs a DELETE request with the given parameters. See get() for more details. + # Performs a DELETE request with the given parameters. See get() for + # more details. def delete(path, parameters = nil, headers = nil) process :delete, path, parameters, headers end - # Performs a HEAD request with the given parameters. See get() for more details. + # Performs a HEAD request with the given parameters. See get() for more + # details. def head(path, parameters = nil, headers = nil) process :head, path, parameters, headers end @@ -212,7 +227,8 @@ module ActionController def xml_http_request(request_method, path, parameters = nil, headers = nil) headers ||= {} headers['X-Requested-With'] = 'XMLHttpRequest' - headers['Accept'] ||= 'text/javascript, text/html, application/xml, text/xml, */*' + headers['Accept'] ||= 'text/javascript, text/html, application/xml, ' + + 'text/xml, */*' process(request_method, path, parameters, headers) end @@ -221,7 +237,9 @@ module ActionController # Returns the URL for the given options, according to the rules specified # in the application's routes. def url_for(options) - controller ? controller.url_for(options) : generic_url_rewriter.rewrite(options) + controller ? + controller.url_for(options) : + generic_url_rewriter.rewrite(options) end private @@ -247,17 +265,33 @@ module ActionController data = nil end + env["QUERY_STRING"] ||= "" + + data = data.is_a?(IO) ? data : StringIO.new(data || '') + env.update( - "REQUEST_METHOD" => method.to_s.upcase, + "REQUEST_METHOD" => method.to_s.upcase, + "SERVER_NAME" => host, + "SERVER_PORT" => (https? ? "443" : "80"), + "HTTPS" => https? ? "on" : "off", + "rack.url_scheme" => https? ? "https" : "http", + "SCRIPT_NAME" => "", + "REQUEST_URI" => path, "HTTP_HOST" => host, "REMOTE_ADDR" => remote_addr, - "SERVER_PORT" => (https? ? "443" : "80"), "CONTENT_TYPE" => "application/x-www-form-urlencoded", "CONTENT_LENGTH" => data ? data.length.to_s : nil, "HTTP_COOKIE" => encode_cookies, - "HTTPS" => https? ? "on" : "off", "HTTP_ACCEPT" => accept, + + "rack.version" => [0,1], + "rack.input" => data, + "rack.errors" => StringIO.new, + "rack.multithread" => true, + "rack.multiprocess" => true, + "rack.run_once" => false, + "action_controller.test" => true ) @@ -273,48 +307,43 @@ module ActionController ActionController::Base.clear_last_instantiation! - env['rack.input'] = data.is_a?(IO) ? data : StringIO.new(data || '') - @status, @headers, result_body = ActionController::Dispatcher.new.call(env) + app = Rack::Lint.new(@application) + + status, headers, body = app.call(env) @request_count += 1 - @controller = ActionController::Base.last_instantiation - @request = @controller.request - @response = @controller.response + if @controller = ActionController::Base.last_instantiation + @request = @controller.request + @response = @controller.response - # Decorate the response with the standard behavior of the TestResponse - # so that things like assert_response can be used in integration - # tests. - @response.extend(TestResponseBehavior) + # Decorate the response with the standard behavior of the + # TestResponse so that things like assert_response can be + # used in integration tests. + @response.extend(TestResponseBehavior) + end @html_document = nil - # Inject status back in for backwords compatibility with CGI - @headers['Status'] = @status - - @status, @status_message = @status.split(/ /) - @status = @status.to_i + @status = status.to_i + @status_message = StatusCodes::STATUS_CODES[@status] - cgi_headers = Hash.new { |h,k| h[k] = [] } - @headers.each do |key, value| - cgi_headers[key.downcase] << value - end - cgi_headers['set-cookie'] = cgi_headers['set-cookie'].first - @headers = cgi_headers + @headers = Rack::Utils::HeaderHash.new(headers) - @response.headers['cookie'] ||= [] - (@headers['set-cookie'] || []).each do |cookie| + (@headers['Set-Cookie'] || []).each do |cookie| name, value = cookie.match(/^([^=]*)=([^;]*);/)[1,2] @cookies[name] = value - - # Fake CGI cookie header - # DEPRECATE: Use response.headers["Set-Cookie"] instead - @response.headers['cookie'] << CGI::Cookie::new("name" => name, "value" => value) end - return status + @body = "" + body.each { |part| @body << part } + + return @status rescue MultiPartNeededException boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1" - status = process(method, path, multipart_body(parameters, boundary), (headers || {}).merge({"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"})) + status = process(method, path, + multipart_body(parameters, boundary), + (headers || {}).merge( + {"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"})) return status end @@ -336,7 +365,7 @@ module ActionController "SERVER_PORT" => https? ? "443" : "80", "HTTPS" => https? ? "on" : "off" } - ActionController::UrlRewriter.new(ActionController::RackRequest.new(env), {}) + UrlRewriter.new(RackRequest.new(env), {}) end def name_with_prefix(prefix, name) @@ -350,9 +379,13 @@ module ActionController raise MultiPartNeededException elsif Hash === parameters return nil if parameters.empty? - parameters.map { |k,v| requestify(v, name_with_prefix(prefix, k)) }.join("&") + parameters.map { |k,v| + requestify(v, name_with_prefix(prefix, k)) + }.join("&") elsif Array === parameters - parameters.map { |v| requestify(v, name_with_prefix(prefix, "")) }.join("&") + parameters.map { |v| + requestify(v, name_with_prefix(prefix, "")) + }.join("&") elsif prefix.nil? parameters else @@ -459,7 +492,8 @@ EOF # can use this method to open multiple sessions that ought to be tested # simultaneously. def open_session - session = Integration::Session.new + application = ActionController::Dispatcher.new + session = Integration::Session.new(application) # delegate the fixture accessors back to the test instance extras = Module.new { attr_accessor :delegate, :test_result } @@ -467,12 +501,16 @@ EOF self.class.fixture_table_names.each do |table_name| name = table_name.tr(".", "_") next unless respond_to?(name) - extras.__send__(:define_method, name) { |*args| delegate.send(name, *args) } + extras.__send__(:define_method, name) { |*args| + delegate.send(name, *args) + } end end # delegate add_assertion to the test case - extras.__send__(:define_method, :add_assertion) { test_result.add_assertion } + extras.__send__(:define_method, :add_assertion) { + test_result.add_assertion + } session.extend(extras) session.delegate = self session.test_result = @_result @@ -600,7 +638,8 @@ EOF # would potentially have to set their values for both Test::Unit::TestCase # ActionController::IntegrationTest, since by the time the value is set on # TestCase, IntegrationTest has already been defined and cannot inherit - # changes to those variables. So, we make those two attributes copy-on-write. + # changes to those variables. So, we make those two attributes + # copy-on-write. class << self def use_transactional_fixtures=(flag) #:nodoc: diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb index 6fbac1fbeb..568f893c6c 100644 --- a/actionpack/lib/action_controller/rack_process.rb +++ b/actionpack/lib/action_controller/rack_process.rb @@ -55,14 +55,7 @@ module ActionController #:nodoc: end def cookies - return {} unless @env["HTTP_COOKIE"] - - unless @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] - @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] - @env["rack.request.cookie_hash"] = CGI::Cookie::parse(@env["rack.request.cookie_string"]) - end - - @env["rack.request.cookie_hash"] + Rack::Request.new(@env).cookies end def server_port diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index b39d35930d..4735b2927b 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -2,19 +2,13 @@ require 'abstract_unit' uses_mocha 'integration' do -module IntegrationSessionStubbing - def stub_integration_session(session) - session.stubs(:process) - session.stubs(:generic_url_rewriter) - end -end - class SessionTest < Test::Unit::TestCase - include IntegrationSessionStubbing + StubApp = lambda { |env| + [200, {"Content-Type" => "text/html"}, "Hello, World!"] + } def setup - @session = ActionController::Integration::Session.new - stub_integration_session(@session) + @session = ActionController::Integration::Session.new(StubApp) end def test_https_bang_works_and_sets_truth_by_default @@ -207,13 +201,10 @@ class SessionTest < Test::Unit::TestCase end class IntegrationTestTest < Test::Unit::TestCase - include IntegrationSessionStubbing - def setup @test = ::ActionController::IntegrationTest.new(:default_test) @test.class.stubs(:fixture_table_names).returns([]) @session = @test.open_session - stub_integration_session(@session) end def test_opens_new_session @@ -231,15 +222,15 @@ end # Tests that integration tests don't call Controller test methods for processing. # Integration tests have their own setup and teardown. class IntegrationTestUsesCorrectClass < ActionController::IntegrationTest - include IntegrationSessionStubbing - def self.fixture_table_names [] end def test_integration_methods_called reset! - stub_integration_session(@integration_session) + @integration_session.stubs(:generic_url_rewriter) + @integration_session.stubs(:process) + %w( get post head put delete ).each do |verb| assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') } end @@ -281,13 +272,9 @@ class IntegrationProcessTest < ActionController::IntegrationTest get '/get' assert_equal 200, status assert_equal "OK", status_message - assert_equal "200 OK", response.headers["Status"] - assert_equal ["200 OK"], headers["status"] assert_response 200 assert_response :success assert_response :ok - assert_equal [], response.headers["cookie"] - assert_equal [], headers["cookie"] assert_equal({}, cookies) assert_equal "OK", response.body assert_kind_of HTML::Document, html_document @@ -300,13 +287,9 @@ class IntegrationProcessTest < ActionController::IntegrationTest post '/post' assert_equal 201, status assert_equal "Created", status_message - assert_equal "201 Created", response.headers["Status"] - assert_equal ["201 Created"], headers["status"] assert_response 201 assert_response :success assert_response :created - assert_equal [], response.headers["cookie"] - assert_equal [], headers["cookie"] assert_equal({}, cookies) assert_equal "Created", response.body assert_kind_of HTML::Document, html_document @@ -321,17 +304,9 @@ class IntegrationProcessTest < ActionController::IntegrationTest get '/cookie_monster' assert_equal 410, status assert_equal "Gone", status_message - assert_equal "410 Gone", response.headers["Status"] - assert_equal ["410 Gone"], headers["status"] assert_response 410 assert_response :gone - assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], response.headers["Set-Cookie"] - assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], headers['set-cookie'] - assert_equal [ - CGI::Cookie::new("name" => "cookie_1", "value" => ""), - CGI::Cookie::new("name" => "cookie_3", "value" => "chocolate") - ], response.headers["cookie"] - assert_equal [], headers["cookie"] + assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], headers["Set-Cookie"] assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies) assert_equal "Gone", response.body end @@ -342,8 +317,6 @@ class IntegrationProcessTest < ActionController::IntegrationTest get '/redirect' assert_equal 302, status assert_equal "Found", status_message - assert_equal "302 Found", response.headers["Status"] - assert_equal ["302 Found"], headers["status"] assert_response 302 assert_response :redirect assert_response :found @@ -358,8 +331,6 @@ class IntegrationProcessTest < ActionController::IntegrationTest xhr :get, '/get' assert_equal 200, status assert_equal "OK", status_message - assert_equal "200 OK", response.headers["Status"] - assert_equal ["200 OK"], headers["status"] assert_response 200 assert_response :success assert_response :ok @@ -372,7 +343,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest get '/get_with_params?foo=bar' assert_equal '/get_with_params?foo=bar', request.env["REQUEST_URI"] assert_equal '/get_with_params?foo=bar', request.request_uri - assert_equal nil, request.env["QUERY_STRING"] + assert_equal "", request.env["QUERY_STRING"] assert_equal 'foo=bar', request.query_string assert_equal 'bar', request.parameters['foo'] diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb index 6a2c8a7a2a..641ef9626e 100644 --- a/actionpack/test/controller/rack_test.rb +++ b/actionpack/test/controller/rack_test.rb @@ -153,12 +153,12 @@ class RackRequestTest < BaseRackTest def test_cookie_syntax_resilience cookies = @request.cookies - assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect - assert_equal ["yes"], cookies["is_admin"], cookies.inspect + assert_equal "c84ace84796670c052c6ceb2451fb0f2", cookies["_session_id"], cookies.inspect + assert_equal "yes", cookies["is_admin"], cookies.inspect alt_cookies = @alt_cookie_fmt_request.cookies - assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect - assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect + #assert_equal "c84ace847,96670c052c6ceb2451fb0f2", alt_cookies["_session_id"], alt_cookies.inspect + assert_equal "yes", alt_cookies["is_admin"], alt_cookies.inspect end end -- cgit v1.2.3