提交 dd2ed324 编写于 作者: J Joshua Peek

Start to integrate some of the features in Rack::Test.

Eventually commit ActionDispatch::Test::MockRequest and ActionDispatch::Test:: UploadedFile upstream.
上级 cbcc0ca5
......@@ -17,9 +17,6 @@ class Session
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
......@@ -60,12 +57,9 @@ class Session
# A running counter of the number of requests processed.
attr_accessor :request_count
class MultiPartNeededException < Exception
end
# Create and initialize a new Session instance.
def initialize(app = nil)
@application = app || ActionController::Dispatcher.new
@app = app || ActionController::Dispatcher.new
reset!
end
......@@ -255,103 +249,65 @@ def interpret_uri(path)
# Performs the actual request.
def process(method, path, parameters = nil, headers = nil)
data = requestify(parameters)
path = interpret_uri(path) if path =~ %r{://}
path = "/#{path}" unless path[0] == ?/
@path = path
env = {}
if method == :get
env["QUERY_STRING"] = data
data = nil
[ControllerCapture, ActionController::ProcessWithTest].each do |mod|
unless ActionController::Base < mod
ActionController::Base.class_eval { include mod }
end
end
env["QUERY_STRING"] ||= ""
ActionController::Base.clear_last_instantiation!
data = data.is_a?(IO) ? data : StringIO.new(data || '')
opts = {
:method => method.to_s.upcase,
:params => parameters,
:headers => headers,
env.update(
"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,
"PATH_INFO" => path,
"HTTP_HOST" => host,
"REMOTE_ADDR" => remote_addr,
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
"CONTENT_LENGTH" => data ? data.length.to_s : nil,
"HTTP_COOKIE" => encode_cookies,
"HTTP_ACCEPT" => accept,
"HTTP_COOKIE" => cookies.inject("") { |string, (name, value)|
string << "#{name}=#{value}; "
},
"rack.version" => [0,1],
"rack.input" => data,
"rack.errors" => StringIO.new,
"rack.multithread" => true,
"rack.multiprocess" => true,
"rack.run_once" => false,
"rack.test" => true
)
(headers || {}).each do |key, value|
key = key.to_s.upcase.gsub(/-/, "_")
key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/
env[key] = value
end
[ControllerCapture, ActionController::ProcessWithTest].each do |mod|
unless ActionController::Base < mod
ActionController::Base.class_eval { include mod }
end
end
ActionController::Base.clear_last_instantiation!
app = @application
# Rack::Lint doesn't accept String headers or bodies in Ruby 1.9
unless RUBY_VERSION >= '1.9.0' && Rack.release <= '0.9.0'
app = Rack::Lint.new(app)
end
"rack.test" => true
}
env = ActionDispatch::Test::MockRequest.env_for(@path, opts)
app = Rack::Lint.new(@app)
status, headers, body = app.call(env)
response = ::Rack::MockResponse.new(status, headers, body)
@request_count += 1
@html_document = nil
@status = status.to_i
@status = response.status
@status_message = ActionDispatch::StatusCodes::STATUS_CODES[@status]
@headers = Rack::Utils::HeaderHash.new(headers)
@headers = response.headers
@body = response.body
(@headers['Set-Cookie'] || "").split("\n").each do |cookie|
name, value = cookie.match(/^([^=]*)=([^;]*);/)[1,2]
@cookies[name] = value
end
if body.is_a?(String)
@body_parts = [body]
@body = body
else
@body_parts = []
body.each { |part| @body_parts << part.to_s }
@body = @body_parts.join
end
if @controller = ActionController::Base.last_instantiation
@request = @controller.request
@response = @controller.response
@controller.send(:set_test_assigns)
else
# Decorate responses from Rack Middleware and Rails Metal
# as an Response for the purposes of integration testing
@response = ActionDispatch::Response.new
@response.status = status.to_s
@response.headers.replace(@headers)
@response.body = @body_parts
@request = ::Rack::Request.new(env)
@response = response
end
# Decorate the response with the standard behavior of the
......@@ -360,21 +316,6 @@ def process(method, path, parameters = nil, headers = nil)
@response.extend(TestResponseBehavior)
return @status
rescue MultiPartNeededException
boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
status = process(method, path,
multipart_body(parameters, boundary),
(headers || {}).merge(
{"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"}))
return status
end
# Encode the cookies hash in a format suitable for passing to a
# request.
def encode_cookies
cookies.inject("") do |string, (name, value)|
string << "#{name}=#{value}; "
end
end
# Get a temporary URL writer object
......@@ -389,72 +330,6 @@ def generic_url_rewriter
}
UrlRewriter.new(ActionDispatch::Request.new(env), {})
end
def name_with_prefix(prefix, name)
prefix ? "#{prefix}[#{name}]" : name.to_s
end
# Convert the given parameters to a request string. The parameters may
# be a string, +nil+, or a Hash.
def requestify(parameters, prefix=nil)
if TestUploadedFile === parameters
raise MultiPartNeededException
elsif Hash === parameters
return nil if parameters.empty?
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("&")
elsif prefix.nil?
parameters
else
"#{CGI.escape(prefix)}=#{CGI.escape(parameters.to_s)}"
end
end
def multipart_requestify(params, first=true)
returning Hash.new do |p|
params.each do |key, value|
k = first ? CGI.escape(key.to_s) : "[#{CGI.escape(key.to_s)}]"
if Hash === value
multipart_requestify(value, false).each do |subkey, subvalue|
p[k + subkey] = subvalue
end
else
p[k] = value
end
end
end
end
def multipart_body(params, boundary)
multipart_requestify(params).map do |key, value|
if value.respond_to?(:original_filename)
File.open(value.path, "rb") do |f|
f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
<<-EOF
--#{boundary}\r
Content-Disposition: form-data; name="#{key}"; filename="#{CGI.escape(value.original_filename)}"\r
Content-Type: #{value.content_type}\r
Content-Length: #{File.stat(value.path).size}\r
\r
#{f.read}\r
EOF
end
else
<<-EOF
--#{boundary}\r
Content-Disposition: form-data; name="#{key}"\r
\r
#{value}\r
EOF
end
end.join("")+"--#{boundary}--\r"
end
end
# A module used to extend ActionController::Base, so that integration tests
......@@ -513,8 +388,8 @@ def reset!
# By default, a single session is automatically created for you, but you
# can use this method to open multiple sessions that ought to be tested
# simultaneously.
def open_session(application = nil)
session = Integration::Session.new(application)
def open_session(app = nil)
session = Integration::Session.new(app)
# delegate the fixture accessors back to the test instance
extras = Module.new { attr_accessor :delegate, :test_result }
......
......@@ -363,34 +363,7 @@ def replace_attributes(attributes = nil)
#
# Pass a true third parameter to ensure the uploaded file is opened in binary mode (only required for Windows):
# post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png', :binary)
require 'tempfile'
class TestUploadedFile
# The filename, *not* including the path, of the "uploaded" file
attr_reader :original_filename
# The content type of the "uploaded" file
attr_accessor :content_type
def initialize(path, content_type = Mime::TEXT, binary = false)
raise "#{path} file does not exist" unless File.exist?(path)
@content_type = content_type
@original_filename = path.sub(/^.*#{File::SEPARATOR}([^#{File::SEPARATOR}]+)$/) { $1 }
@tempfile = Tempfile.new(@original_filename)
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
@tempfile.binmode if binary
FileUtils.copy_file(path, @tempfile.path)
end
def path #:nodoc:
@tempfile.path
end
alias local_path path
def method_missing(method_name, *args, &block) #:nodoc:
@tempfile.__send__(method_name, *args, &block)
end
end
TestUploadedFile = ActionDispatch::Test::UploadedFile
module TestProcess
def self.included(base)
......
......@@ -60,6 +60,11 @@ module Session
autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
end
module Test
autoload :UploadedFile, 'action_dispatch/test/uploaded_file'
autoload :MockRequest, 'action_dispatch/test/mock'
end
end
autoload :Mime, 'action_dispatch/http/mime_type'
module ActionDispatch
module Test
class MockRequest < Rack::MockRequest
MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"
class << self
def env_for(path, opts)
headers = opts.delete(:headers)
method = (opts[:method] || opts["REQUEST_METHOD"]).to_s.upcase
opts[:method] = opts["REQUEST_METHOD"] = method
path = "/#{path}" unless path[0] == ?/
uri = URI.parse(path)
uri.host ||= "example.org"
if URI::HTTPS === uri
opts.update("SERVER_PORT" => "443", "HTTPS" => "on")
end
if method == "POST" && !opts.has_key?(:input)
opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
multipart = (opts[:params] || {}).any? do |k, v|
UploadedFile === v
end
if multipart
opts[:input] = multipart_body(opts.delete(:params))
opts["CONTENT_LENGTH"] ||= opts[:input].length.to_s
opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
else
params = opts.delete(:params)
opts[:input] = case params
when Hash then requestify(params)
when nil then ""
else params
end
end
end
params = opts[:params] || {}
if params.is_a?(String)
if method == "GET"
uri.query = params
else
opts[:input] = params
end
else
params.update(::Rack::Utils.parse_query(uri.query))
uri.query = requestify(params)
end
env = ::Rack::MockRequest.env_for(uri.to_s, opts)
(headers || {}).each do |key, value|
key = key.to_s.upcase.gsub(/-/, "_")
key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/
env[key] = value
end
env
end
private
def requestify(value, prefix = nil)
case value
when Array
value.map do |v|
requestify(v, "#{prefix}[]")
end.join("&")
when Hash
value.map do |k, v|
requestify(v, prefix ? "#{prefix}[#{::Rack::Utils.escape(k)}]" : ::Rack::Utils.escape(k))
end.join("&")
else
"#{prefix}=#{::Rack::Utils.escape(value)}"
end
end
def multipart_requestify(params, first=true)
p = Hash.new
params.each do |key, value|
k = first ? key.to_s : "[#{key}]"
if Hash === value
multipart_requestify(value, false).each do |subkey, subvalue|
p[k + subkey] = subvalue
end
else
p[k] = value
end
end
return p
end
def multipart_body(params)
multipart_requestify(params).map do |key, value|
if value.respond_to?(:original_filename)
::File.open(value.path, "rb") do |f|
f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
<<-EOF
--#{MULTIPART_BOUNDARY}\r
Content-Disposition: form-data; name="#{key}"; filename="#{::Rack::Utils.escape(value.original_filename)}"\r
Content-Type: #{value.content_type}\r
Content-Length: #{::File.stat(value.path).size}\r
\r
#{f.read}\r
EOF
end
else
<<-EOF
--#{MULTIPART_BOUNDARY}\r
Content-Disposition: form-data; name="#{key}"\r
\r
#{value}\r
EOF
end
end.join("")+"--#{MULTIPART_BOUNDARY}--\r"
end
end
end
end
end
require "tempfile"
module ActionDispatch
module Test
class UploadedFile
# The filename, *not* including the path, of the "uploaded" file
attr_reader :original_filename
# The content type of the "uploaded" file
attr_accessor :content_type
def initialize(path, content_type = "text/plain", binary = false)
raise "#{path} file does not exist" unless ::File.exist?(path)
@content_type = content_type
@original_filename = ::File.basename(path)
@tempfile = Tempfile.new(@original_filename)
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
@tempfile.binmode if binary
FileUtils.copy_file(path, @tempfile.path)
end
def path
@tempfile.path
end
alias_method :local_path, :path
def method_missing(method_name, *args, &block) #:nodoc:
@tempfile.__send__(method_name, *args, &block)
end
end
end
end
......@@ -337,7 +337,7 @@ def test_get_with_query_string
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 "", request.env["QUERY_STRING"]
assert_equal "foo=bar", request.env["QUERY_STRING"]
assert_equal 'foo=bar', request.query_string
assert_equal 'bar', request.parameters['foo']
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册