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 +- 2 files changed, 97 insertions(+), 65 deletions(-) (limited to 'actionpack/lib/action_controller') 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 -- cgit v1.2.3