aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Peek <josh@joshpeek.com>2009-05-19 22:42:59 -0500
committerJoshua Peek <josh@joshpeek.com>2009-05-19 22:43:22 -0500
commit6761759a90c08b9db8d5720a8ee4c4329a547caf (patch)
treeb79aad2a0ebae2cd291222a25341c39cf26c1791
parentdf0faea378034b031f49995f06c8bf108a0b5530 (diff)
downloadrails-6761759a90c08b9db8d5720a8ee4c4329a547caf.tar.gz
rails-6761759a90c08b9db8d5720a8ee4c4329a547caf.tar.bz2
rails-6761759a90c08b9db8d5720a8ee4c4329a547caf.zip
Temporarily bundle rack-test while MockSession is baking
-rw-r--r--actionpack/lib/action_controller/testing/integration.rb3
-rw-r--r--actionpack/lib/action_dispatch.rb2
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb50
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb239
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb169
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb45
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb27
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb36
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb75
9 files changed, 645 insertions, 1 deletions
diff --git a/actionpack/lib/action_controller/testing/integration.rb b/actionpack/lib/action_controller/testing/integration.rb
index 717b77674c..cc157816e2 100644
--- a/actionpack/lib/action_controller/testing/integration.rb
+++ b/actionpack/lib/action_controller/testing/integration.rb
@@ -1,8 +1,9 @@
require 'stringio'
require 'uri'
require 'active_support/test_case'
-require 'rack/test'
+
require 'rack/mock_session'
+require 'rack/test/cookie_jar'
module ActionController
module Integration #:nodoc:
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 6fc4ad3f21..f9003c51ea 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -33,6 +33,8 @@ end
require 'rack'
+$:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-test"
+
module ActionDispatch
autoload :Request, 'action_dispatch/http/request'
autoload :Response, 'action_dispatch/http/response'
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb
new file mode 100644
index 0000000000..eba6226538
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb
@@ -0,0 +1,50 @@
+module Rack
+
+ class MockSession
+ attr_writer :cookie_jar
+ attr_reader :last_response
+
+ def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
+ @app = app
+ @default_host = default_host
+ end
+
+ def clear_cookies
+ @cookie_jar = Rack::Test::CookieJar.new([], @default_host)
+ end
+
+ def set_cookie(cookie, uri = nil)
+ cookie_jar.merge(cookie, uri)
+ end
+
+ def request(uri, env)
+ env["HTTP_COOKIE"] ||= cookie_jar.for(uri)
+ @last_request = Rack::Request.new(env)
+ status, headers, body = @app.call(@last_request.env)
+ @last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush)
+ cookie_jar.merge(last_response.headers["Set-Cookie"], uri)
+
+ @last_response
+ end
+
+ # Return the last request issued in the session. Raises an error if no
+ # requests have been sent yet.
+ def last_request
+ raise Rack::Test::Error.new("No request yet. Request a page first.") unless @last_request
+ @last_request
+ end
+
+ # Return the last response received in the session. Raises an error if
+ # no requests have been sent yet.
+ def last_response
+ raise Rack::Test::Error.new("No response yet. Request a page first.") unless @last_response
+ @last_response
+ end
+
+ def cookie_jar
+ @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
+ end
+
+ end
+
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb
new file mode 100644
index 0000000000..70384b1d76
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb
@@ -0,0 +1,239 @@
+unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__) + "/.."))
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/.."))
+end
+
+require "uri"
+require "rack"
+require "rack/mock_session"
+require "rack/test/cookie_jar"
+require "rack/test/mock_digest_request"
+require "rack/test/utils"
+require "rack/test/methods"
+require "rack/test/uploaded_file"
+
+module Rack
+ module Test
+
+ VERSION = "0.3.0"
+
+ DEFAULT_HOST = "example.org"
+ MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"
+
+ # The common base class for exceptions raised by Rack::Test
+ class Error < StandardError; end
+
+ class Session
+ extend Forwardable
+ include Rack::Test::Utils
+
+ def_delegators :@rack_mock_session, :clear_cookies, :set_cookie, :last_response, :last_request
+
+ # Initialize a new session for the given Rack app
+ def initialize(app, default_host = DEFAULT_HOST)
+ @headers = {}
+ @default_host = default_host
+ @rack_mock_session = Rack::MockSession.new(app, default_host)
+ end
+
+ # Issue a GET request for the given URI with the given params and Rack
+ # environment. Stores the issues request object in #last_request and
+ # the app's response in #last_response. Yield #last_response to a block
+ # if given.
+ #
+ # Example:
+ # get "/"
+ def get(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "GET", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a POST request for the given URI. See #get
+ #
+ # Example:
+ # post "/signup", "name" => "Bryan"
+ def post(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "POST", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a PUT request for the given URI. See #get
+ #
+ # Example:
+ # put "/"
+ def put(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "PUT", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a DELETE request for the given URI. See #get
+ #
+ # Example:
+ # delete "/"
+ def delete(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "DELETE", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a HEAD request for the given URI. See #get
+ #
+ # Example:
+ # head "/"
+ def head(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "HEAD", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a request to the Rack app for the given URI and optional Rack
+ # environment. Stores the issues request object in #last_request and
+ # the app's response in #last_response. Yield #last_response to a block
+ # if given.
+ #
+ # Example:
+ # request "/"
+ def request(uri, env = {}, &block)
+ env = env_for(uri, env)
+ process_request(uri, env, &block)
+ end
+
+ # Set a header to be included on all subsequent requests through the
+ # session. Use a value of nil to remove a previously configured header.
+ #
+ # Example:
+ # header "User-Agent", "Firefox"
+ def header(name, value)
+ if value.nil?
+ @headers.delete(name)
+ else
+ @headers[name] = value
+ end
+ end
+
+ # Set the username and password for HTTP Basic authorization, to be
+ # included in subsequent requests in the HTTP_AUTHORIZATION header.
+ #
+ # Example:
+ # basic_authorize "bryan", "secret"
+ def basic_authorize(username, password)
+ encoded_login = ["#{username}:#{password}"].pack("m*")
+ header('HTTP_AUTHORIZATION', "Basic #{encoded_login}")
+ end
+
+ alias_method :authorize, :basic_authorize
+
+ def digest_authorize(username, password)
+ @digest_username = username
+ @digest_password = password
+ end
+
+ # Rack::Test will not follow any redirects automatically. This method
+ # will follow the redirect returned in the last response. If the last
+ # response was not a redirect, an error will be raised.
+ def follow_redirect!
+ unless last_response.redirect?
+ raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
+ end
+
+ get(last_response["Location"])
+ end
+
+ private
+
+ def env_for(path, env)
+ uri = URI.parse(path)
+ uri.host ||= @default_host
+
+ env = default_env.merge(env)
+
+ env.update("HTTPS" => "on") if URI::HTTPS === uri
+ env["X-Requested-With"] = "XMLHttpRequest" if env[:xhr]
+
+ if (env[:method] == "POST" || env["REQUEST_METHOD"] == "POST") && !env.has_key?(:input)
+ env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
+
+ multipart = (Hash === env[:params]) &&
+ env[:params].any? { |_, v| UploadedFile === v }
+
+ if multipart
+ env[:input] = multipart_body(env.delete(:params))
+ env["CONTENT_LENGTH"] ||= env[:input].length.to_s
+ env["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
+ else
+ env[:input] = params_to_string(env.delete(:params))
+ end
+ end
+
+ params = env[:params] || {}
+ params.update(parse_query(uri.query))
+
+ uri.query = requestify(params)
+
+ if env.has_key?(:cookie)
+ set_cookie(env.delete(:cookie), uri)
+ end
+
+ Rack::MockRequest.env_for(uri.to_s, env)
+ end
+
+ def process_request(uri, env)
+ uri = URI.parse(uri)
+ uri.host ||= @default_host
+
+ @rack_mock_session.request(uri, env)
+
+ if retry_with_digest_auth?(env)
+ auth_env = env.merge({
+ "HTTP_AUTHORIZATION" => digest_auth_header,
+ "rack-test.digest_auth_retry" => true
+ })
+ auth_env.delete('rack.request')
+ process_request(uri.path, auth_env)
+ else
+ yield last_response if block_given?
+
+ last_response
+ end
+ end
+
+ def digest_auth_header
+ challenge = last_response["WWW-Authenticate"].split(" ", 2).last
+ params = Rack::Auth::Digest::Params.parse(challenge)
+
+ params.merge!({
+ "username" => @digest_username,
+ "nc" => "00000001",
+ "cnonce" => "nonsensenonce",
+ "uri" => last_request.path_info,
+ "method" => last_request.env["REQUEST_METHOD"],
+ })
+
+ params["response"] = MockDigestRequest.new(params).response(@digest_password)
+
+ "Digest #{params}"
+ end
+
+ def retry_with_digest_auth?(env)
+ last_response.status == 401 &&
+ digest_auth_configured? &&
+ !env["rack-test.digest_auth_retry"]
+ end
+
+ def digest_auth_configured?
+ @digest_username
+ end
+
+ def default_env
+ { "rack.test" => true, "REMOTE_ADDR" => "127.0.0.1" }.merge(@headers)
+ end
+
+ def params_to_string(params)
+ case params
+ when Hash then requestify(params)
+ when nil then ""
+ else params
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb
new file mode 100644
index 0000000000..d58c914c9b
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb
@@ -0,0 +1,169 @@
+require "uri"
+module Rack
+ module Test
+
+ class Cookie
+ include Rack::Utils
+
+ # :api: private
+ attr_reader :name, :value
+
+ # :api: private
+ def initialize(raw, uri = nil, default_host = DEFAULT_HOST)
+ @default_host = default_host
+ uri ||= default_uri
+
+ # separate the name / value pair from the cookie options
+ @name_value_raw, options = raw.split(/[;,] */n, 2)
+
+ @name, @value = parse_query(@name_value_raw, ';').to_a.first
+ @options = parse_query(options, ';')
+
+ @options["domain"] ||= (uri.host || default_host)
+ @options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "")
+ end
+
+ def replaces?(other)
+ [name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
+ end
+
+ # :api: private
+ def raw
+ @name_value_raw
+ end
+
+ # :api: private
+ def empty?
+ @value.nil? || @value.empty?
+ end
+
+ # :api: private
+ def domain
+ @options["domain"]
+ end
+
+ def secure?
+ @options.has_key?("secure")
+ end
+
+ # :api: private
+ def path
+ @options["path"].strip || "/"
+ end
+
+ # :api: private
+ def expires
+ Time.parse(@options["expires"]) if @options["expires"]
+ end
+
+ # :api: private
+ def expired?
+ expires && expires < Time.now
+ end
+
+ # :api: private
+ def valid?(uri)
+ uri ||= default_uri
+
+ if uri.host.nil?
+ uri.host = @default_host
+ end
+
+ (!secure? || (secure? && uri.scheme == "https")) &&
+ uri.host =~ Regexp.new("#{Regexp.escape(domain)}$", Regexp::IGNORECASE) &&
+ uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
+ end
+
+ # :api: private
+ def matches?(uri)
+ ! expired? && valid?(uri)
+ end
+
+ # :api: private
+ def <=>(other)
+ # Orders the cookies from least specific to most
+ [name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
+ end
+
+ protected
+
+ def default_uri
+ URI.parse("//" + @default_host + "/")
+ end
+
+ end
+
+ class CookieJar
+
+ # :api: private
+ def initialize(cookies = [], default_host = DEFAULT_HOST)
+ @default_host = default_host
+ @cookies = cookies
+ @cookies.sort!
+ end
+
+ def [](name)
+ cookies = hash_for(nil)
+ # TODO: Should be case insensitive
+ cookies[name] && cookies[name].value
+ end
+
+ def []=(name, value)
+ # TODO: needs proper escaping
+ merge("#{name}=#{value}")
+ end
+
+ def merge(raw_cookies, uri = nil)
+ return unless raw_cookies
+
+ raw_cookies.each_line do |raw_cookie|
+ cookie = Cookie.new(raw_cookie, uri, @default_host)
+ self << cookie if cookie.valid?(uri)
+ end
+ end
+
+ def <<(new_cookie)
+ @cookies.reject! do |existing_cookie|
+ new_cookie.replaces?(existing_cookie)
+ end
+
+ @cookies << new_cookie
+ @cookies.sort!
+ end
+
+ # :api: private
+ def for(uri)
+ hash_for(uri).values.map { |c| c.raw }.join(';')
+ end
+
+ def to_hash
+ cookies = {}
+
+ hash_for(nil).each do |name, cookie|
+ cookies[name] = cookie.value
+ end
+
+ return cookies
+ end
+
+ protected
+
+ def hash_for(uri = nil)
+ cookies = {}
+
+ # The cookies are sorted by most specific first. So, we loop through
+ # all the cookies in order and add it to a hash by cookie name if
+ # the cookie can be sent to the current URI. It's added to the hash
+ # so that when we are done, the cookies will be unique by name and
+ # we'll have grabbed the most specific to the URI.
+ @cookies.each do |cookie|
+ cookies[cookie.name] = cookie if cookie.matches?(uri)
+ end
+
+ return cookies
+ end
+
+ end
+
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb
new file mode 100644
index 0000000000..a191fa23d8
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb
@@ -0,0 +1,45 @@
+require "forwardable"
+
+module Rack
+ module Test
+ module Methods
+ extend Forwardable
+
+ def rack_test_session
+ @_rack_test_session ||= Rack::Test::Session.new(app)
+ end
+
+ def rack_mock_session
+ @_rack_mock_session ||= Rack::MockSession.new(app)
+ end
+
+ METHODS = [
+ :request,
+
+ # HTTP verbs
+ :get,
+ :post,
+ :put,
+ :delete,
+ :head,
+
+ # Redirects
+ :follow_redirect!,
+
+ # Header-related features
+ :header,
+ :set_cookie,
+ :clear_cookies,
+ :authorize,
+ :basic_authorize,
+ :digest_authorize,
+
+ # Expose the last request and response
+ :last_response,
+ :last_request
+ ]
+
+ def_delegators :rack_test_session, *METHODS
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb
new file mode 100644
index 0000000000..81c398ba51
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb
@@ -0,0 +1,27 @@
+module Rack
+ module Test
+
+ class MockDigestRequest
+ def initialize(params)
+ @params = params
+ end
+
+ def method_missing(sym)
+ if @params.has_key? k = sym.to_s
+ return @params[k]
+ end
+
+ super
+ end
+
+ def method
+ @params['method']
+ end
+
+ def response(password)
+ Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
+ end
+ end
+
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb
new file mode 100644
index 0000000000..239302fbe4
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb
@@ -0,0 +1,36 @@
+require "tempfile"
+
+module Rack
+ 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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb
new file mode 100644
index 0000000000..d25b849709
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb
@@ -0,0 +1,75 @@
+module Rack
+ module Test
+
+ module Utils
+ include Rack::Utils
+
+ 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}[#{escape(k)}]" : escape(k))
+ end.join("&")
+ else
+ "#{prefix}=#{escape(value)}"
+ end
+ end
+
+ module_function :requestify
+
+ 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
+
+ module_function :multipart_requestify
+
+ 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="#{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
+
+ module_function :multipart_body
+
+ end
+
+ end
+end