Skip to content

Commit

Permalink
Make integration test runner more Rack friendly and clean out old CGI…
Browse files Browse the repository at this point in the history
… cruft
  • Loading branch information
josh committed Dec 8, 2008
1 parent e4eadf3 commit ebec9d4
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 107 deletions.
153 changes: 96 additions & 57 deletions actionpack/lib/action_controller/integration.rb
Expand Up @@ -9,13 +9,17 @@ module Integration #:nodoc:
# 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

Expand Down Expand Up @@ -57,7 +61,8 @@ class MultiPartNeededException < Exception
end

# Create and initialize a new Session instance.
def initialize
def initialize(app)
@application = app
reset!
end

Expand All @@ -76,11 +81,13 @@ def reset!

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<<self; self; end
klass = class << self; self; end
Routing::Routes.install_helpers(klass)

# the helpers are made protected by default--we make them public for
Expand All @@ -94,7 +101,7 @@ def reset!
#
# session.https!
# session.https!(false)
def https!(flag=true)
def https!(flag = true)
@https = flag
end

Expand Down Expand Up @@ -164,40 +171,48 @@ def redirect?

# Performs a GET request with the given parameters.
#
# - +path+: The URI (as a String) on which you want to perform a GET request.
# - +parameters+: The HTTP parameters that you want to pass. This may be +nil+,
# - +path+: The URI (as a String) on which you want to perform a GET
# request.
# - +parameters+: The HTTP parameters that you want to pass. This may
# be +nil+,
# a Hash, or a String that is appropriately encoded
# (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
# (<tt>application/x-www-form-urlencoded</tt> or
# <tt>multipart/form-data</tt>).
# - +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 <tt>@response</tt>
# 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 <tt>@response</tt> 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+.
def get(path, parameters = nil, headers = nil)
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
Expand All @@ -212,7 +227,8 @@ def head(path, parameters = nil, headers = nil)
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
Expand All @@ -221,7 +237,9 @@ def xml_http_request(request_method, path, parameters = nil, headers = nil)
# 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
Expand All @@ -247,17 +265,33 @@ def process(method, path, parameters = nil, headers = nil)
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
)

Expand All @@ -273,48 +307,43 @@ def process(method, path, parameters = nil, headers = nil)

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

Expand All @@ -336,7 +365,7 @@ def generic_url_rewriter
"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)
Expand All @@ -350,9 +379,13 @@ def requestify(parameters, prefix=nil)
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
Expand Down Expand Up @@ -459,20 +492,25 @@ def reset!
# 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 }
if self.class.respond_to?(:fixture_table_names)
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
Expand Down Expand Up @@ -600,7 +638,8 @@ def run(*args) #:nodoc:
# 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:
Expand Down
9 changes: 1 addition & 8 deletions actionpack/lib/action_controller/rack_process.rb
Expand Up @@ -55,14 +55,7 @@ def key?(key)
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
Expand Down

0 comments on commit ebec9d4

Please sign in to comment.