rack-test 0.6.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/History.md +243 -0
- data/README.md +148 -0
- data/lib/rack/mock_session.rb +6 -9
- data/lib/rack/test/cookie_jar.rb +39 -27
- data/lib/rack/test/methods.rb +22 -22
- data/lib/rack/test/mock_digest_request.rb +1 -5
- data/lib/rack/test/uploaded_file.rb +51 -19
- data/lib/rack/test/utils.rb +37 -43
- data/lib/rack/test/version.rb +5 -0
- data/lib/rack/test.rb +94 -85
- metadata +139 -60
- data/.document +0 -4
- data/.gitignore +0 -6
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -41
- data/History.txt +0 -179
- data/README.rdoc +0 -85
- data/Rakefile +0 -33
- data/Thorfile +0 -114
- data/rack-test.gemspec +0 -77
- data/spec/fixtures/bar.txt +0 -1
- data/spec/fixtures/config.ru +0 -3
- data/spec/fixtures/fake_app.rb +0 -143
- data/spec/fixtures/foo.txt +0 -1
- data/spec/rack/test/cookie_spec.rb +0 -219
- data/spec/rack/test/digest_auth_spec.rb +0 -46
- data/spec/rack/test/multipart_spec.rb +0 -145
- data/spec/rack/test/uploaded_file_spec.rb +0 -24
- data/spec/rack/test/utils_spec.rb +0 -193
- data/spec/rack/test_spec.rb +0 -550
- data/spec/spec_helper.rb +0 -69
- data/spec/support/matchers/body.rb +0 -9
- data/spec/support/matchers/challenge.rb +0 -11
data/lib/rack/test/cookie_jar.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'uri'
|
2
|
+
require 'time'
|
3
3
|
|
4
4
|
module Rack
|
5
5
|
module Test
|
6
|
-
|
7
6
|
class Cookie # :nodoc:
|
8
7
|
include Rack::Utils
|
9
8
|
|
@@ -21,8 +20,8 @@ module Rack
|
|
21
20
|
@name, @value = parse_query(@name_value_raw, ';').to_a.first
|
22
21
|
@options = parse_query(options, ';')
|
23
22
|
|
24
|
-
@options[
|
25
|
-
@options[
|
23
|
+
@options['domain'] ||= (uri.host || default_host)
|
24
|
+
@options['path'] ||= uri.path.sub(/\/[^\/]*\Z/, '')
|
26
25
|
end
|
27
26
|
|
28
27
|
def replaces?(other)
|
@@ -41,21 +40,25 @@ module Rack
|
|
41
40
|
|
42
41
|
# :api: private
|
43
42
|
def domain
|
44
|
-
@options[
|
43
|
+
@options['domain']
|
45
44
|
end
|
46
45
|
|
47
46
|
def secure?
|
48
|
-
@options.
|
47
|
+
@options.key?('secure')
|
48
|
+
end
|
49
|
+
|
50
|
+
def http_only?
|
51
|
+
@options.key?('HttpOnly')
|
49
52
|
end
|
50
53
|
|
51
54
|
# :api: private
|
52
55
|
def path
|
53
|
-
|
56
|
+
([*@options['path']].first.split(',').first || '/').strip
|
54
57
|
end
|
55
58
|
|
56
59
|
# :api: private
|
57
60
|
def expires
|
58
|
-
Time.parse(@options[
|
61
|
+
Time.parse(@options['expires']) if @options['expires']
|
59
62
|
end
|
60
63
|
|
61
64
|
# :api: private
|
@@ -67,19 +70,17 @@ module Rack
|
|
67
70
|
def valid?(uri)
|
68
71
|
uri ||= default_uri
|
69
72
|
|
70
|
-
if uri.host.nil?
|
71
|
-
uri.host = @default_host
|
72
|
-
end
|
73
|
+
uri.host = @default_host if uri.host.nil?
|
73
74
|
|
74
75
|
real_domain = domain =~ /^\./ ? domain[1..-1] : domain
|
75
|
-
(!secure? || (secure? && uri.scheme ==
|
76
|
-
|
77
|
-
|
76
|
+
(!secure? || (secure? && uri.scheme == 'https')) &&
|
77
|
+
uri.host =~ Regexp.new("#{Regexp.escape(real_domain)}$", Regexp::IGNORECASE) &&
|
78
|
+
uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
|
78
79
|
end
|
79
80
|
|
80
81
|
# :api: private
|
81
82
|
def matches?(uri)
|
82
|
-
!
|
83
|
+
!expired? && valid?(uri)
|
83
84
|
end
|
84
85
|
|
85
86
|
# :api: private
|
@@ -88,15 +89,24 @@ module Rack
|
|
88
89
|
[name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
|
89
90
|
end
|
90
91
|
|
91
|
-
|
92
|
+
def to_h
|
93
|
+
@options.merge(
|
94
|
+
'value' => @value,
|
95
|
+
'HttpOnly' => http_only?,
|
96
|
+
'secure' => secure?
|
97
|
+
)
|
98
|
+
end
|
99
|
+
alias to_hash to_h
|
100
|
+
|
101
|
+
protected
|
92
102
|
|
93
103
|
def default_uri
|
94
|
-
URI.parse(
|
104
|
+
URI.parse('//' + @default_host + '/')
|
95
105
|
end
|
96
|
-
|
97
106
|
end
|
98
107
|
|
99
108
|
class CookieJar # :nodoc:
|
109
|
+
DELIMITER = '; '.freeze
|
100
110
|
|
101
111
|
# :api: private
|
102
112
|
def initialize(cookies = [], default_host = DEFAULT_HOST)
|
@@ -108,13 +118,17 @@ module Rack
|
|
108
118
|
def [](name)
|
109
119
|
cookies = hash_for(nil)
|
110
120
|
# TODO: Should be case insensitive
|
111
|
-
cookies[name] && cookies[name].value
|
121
|
+
cookies[name.to_s] && cookies[name.to_s].value
|
112
122
|
end
|
113
123
|
|
114
124
|
def []=(name, value)
|
115
125
|
merge("#{name}=#{Rack::Utils.escape(value)}")
|
116
126
|
end
|
117
127
|
|
128
|
+
def get_cookie(name)
|
129
|
+
hash_for(nil).fetch(name, nil)
|
130
|
+
end
|
131
|
+
|
118
132
|
def delete(name)
|
119
133
|
@cookies.reject! do |cookie|
|
120
134
|
cookie.name == name
|
@@ -126,7 +140,7 @@ module Rack
|
|
126
140
|
|
127
141
|
if raw_cookies.is_a? String
|
128
142
|
raw_cookies = raw_cookies.split("\n")
|
129
|
-
raw_cookies.reject!
|
143
|
+
raw_cookies.reject!(&:empty?)
|
130
144
|
end
|
131
145
|
|
132
146
|
raw_cookies.each do |raw_cookie|
|
@@ -146,7 +160,7 @@ module Rack
|
|
146
160
|
|
147
161
|
# :api: private
|
148
162
|
def for(uri)
|
149
|
-
hash_for(uri).values.map
|
163
|
+
hash_for(uri).values.map(&:raw).join(DELIMITER)
|
150
164
|
end
|
151
165
|
|
152
166
|
def to_hash
|
@@ -156,10 +170,10 @@ module Rack
|
|
156
170
|
cookies[name] = cookie.value
|
157
171
|
end
|
158
172
|
|
159
|
-
|
173
|
+
cookies
|
160
174
|
end
|
161
175
|
|
162
|
-
|
176
|
+
protected
|
163
177
|
|
164
178
|
def hash_for(uri = nil)
|
165
179
|
cookies = {}
|
@@ -173,10 +187,8 @@ module Rack
|
|
173
187
|
cookies[cookie.name] = cookie if !uri || cookie.matches?(uri)
|
174
188
|
end
|
175
189
|
|
176
|
-
|
190
|
+
cookies
|
177
191
|
end
|
178
|
-
|
179
192
|
end
|
180
|
-
|
181
193
|
end
|
182
194
|
end
|
data/lib/rack/test/methods.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require 'forwardable'
|
2
2
|
|
3
3
|
module Rack
|
4
4
|
module Test
|
5
|
-
|
6
5
|
# This module serves as the primary integration point for using Rack::Test
|
7
6
|
# in a testing environment. It depends on an app method being defined in the
|
8
7
|
# same context, and provides the Rack::Test API methods (see Rack::Test::Session
|
@@ -56,26 +55,27 @@ module Rack
|
|
56
55
|
@_current_session_names ||= [:default]
|
57
56
|
end
|
58
57
|
|
59
|
-
METHODS = [
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
58
|
+
METHODS = %i[
|
59
|
+
request
|
60
|
+
get
|
61
|
+
post
|
62
|
+
put
|
63
|
+
patch
|
64
|
+
delete
|
65
|
+
options
|
66
|
+
head
|
67
|
+
custom_request
|
68
|
+
follow_redirect!
|
69
|
+
header
|
70
|
+
env
|
71
|
+
set_cookie
|
72
|
+
clear_cookies
|
73
|
+
authorize
|
74
|
+
basic_authorize
|
75
|
+
digest_authorize
|
76
|
+
last_response
|
77
|
+
last_request
|
78
|
+
].freeze
|
79
79
|
|
80
80
|
def_delegators :current_session, *METHODS
|
81
81
|
end
|
@@ -1,14 +1,12 @@
|
|
1
1
|
module Rack
|
2
2
|
module Test
|
3
|
-
|
4
3
|
class MockDigestRequest # :nodoc:
|
5
|
-
|
6
4
|
def initialize(params)
|
7
5
|
@params = params
|
8
6
|
end
|
9
7
|
|
10
8
|
def method_missing(sym)
|
11
|
-
if @params.
|
9
|
+
if @params.key? k = sym.to_s
|
12
10
|
return @params[k]
|
13
11
|
end
|
14
12
|
|
@@ -22,8 +20,6 @@ module Rack
|
|
22
20
|
def response(password)
|
23
21
|
Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
|
24
22
|
end
|
25
|
-
|
26
23
|
end
|
27
|
-
|
28
24
|
end
|
29
25
|
end
|
@@ -1,16 +1,15 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pathname'
|
3
|
+
require 'tempfile'
|
3
4
|
|
4
5
|
module Rack
|
5
6
|
module Test
|
6
|
-
|
7
7
|
# Wraps a Tempfile with a content type. Including one or more UploadedFile's
|
8
8
|
# in the params causes Rack::Test to build and issue a multipart request.
|
9
9
|
#
|
10
10
|
# Example:
|
11
11
|
# post "/photos", "file" => Rack::Test::UploadedFile.new("me.jpg", "image/jpeg")
|
12
12
|
class UploadedFile
|
13
|
-
|
14
13
|
# The filename, *not* including the path, of the "uploaded" file
|
15
14
|
attr_reader :original_filename
|
16
15
|
|
@@ -20,34 +19,67 @@ module Rack
|
|
20
19
|
# The content type of the "uploaded" file
|
21
20
|
attr_accessor :content_type
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
# Creates a new UploadedFile instance.
|
23
|
+
#
|
24
|
+
# @param content [IO, Pathname, String, StringIO] a path to a file, or an {IO} or {StringIO} object representing the
|
25
|
+
# file.
|
26
|
+
# @param content_type [String]
|
27
|
+
# @param binary [Boolean] an optional flag that indicates whether the file should be open in binary mode or not.
|
28
|
+
# @param original_filename [String] an optional parameter that provides the original filename if `content` is a StringIO
|
29
|
+
# object. Not used for other kind of `content` objects.
|
30
|
+
def initialize(content, content_type = 'text/plain', binary = false, original_filename: nil)
|
31
|
+
if original_filename
|
32
|
+
initialize_from_stringio(content, original_filename)
|
33
|
+
else
|
34
|
+
initialize_from_file_path(content)
|
35
|
+
end
|
26
36
|
@content_type = content_type
|
27
|
-
@original_filename = ::File.basename(path)
|
28
|
-
|
29
|
-
@tempfile = Tempfile.new(@original_filename)
|
30
|
-
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
|
31
37
|
@tempfile.binmode if binary
|
32
|
-
|
33
|
-
FileUtils.copy_file(path, @tempfile.path)
|
34
38
|
end
|
35
39
|
|
36
40
|
def path
|
37
|
-
|
41
|
+
tempfile.path
|
38
42
|
end
|
39
43
|
|
40
|
-
|
44
|
+
alias local_path path
|
41
45
|
|
42
46
|
def method_missing(method_name, *args, &block) #:nodoc:
|
43
|
-
|
47
|
+
tempfile.public_send(method_name, *args, &block)
|
44
48
|
end
|
45
49
|
|
46
|
-
def
|
47
|
-
|
50
|
+
def respond_to_missing?(method_name, include_private = false) #:nodoc:
|
51
|
+
tempfile.respond_to?(method_name, include_private) || super
|
48
52
|
end
|
49
53
|
|
50
|
-
|
54
|
+
def self.finalize(file)
|
55
|
+
proc { actually_finalize file }
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.actually_finalize(file)
|
59
|
+
file.close
|
60
|
+
file.unlink
|
61
|
+
end
|
51
62
|
|
63
|
+
private
|
64
|
+
|
65
|
+
def initialize_from_stringio(stringio, original_filename)
|
66
|
+
@tempfile = stringio
|
67
|
+
@original_filename = original_filename || raise(ArgumentError, 'Missing `original_filename` for StringIO object')
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize_from_file_path(path)
|
71
|
+
raise "#{path} file does not exist" unless ::File.exist?(path)
|
72
|
+
|
73
|
+
@original_filename = ::File.basename(path)
|
74
|
+
extension = ::File.extname(@original_filename)
|
75
|
+
|
76
|
+
@tempfile = Tempfile.new([::File.basename(@original_filename, extension), extension])
|
77
|
+
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
|
78
|
+
|
79
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(@tempfile))
|
80
|
+
|
81
|
+
FileUtils.copy_file(path, @tempfile.path)
|
82
|
+
end
|
83
|
+
end
|
52
84
|
end
|
53
85
|
end
|
data/lib/rack/test/utils.rb
CHANGED
@@ -1,38 +1,36 @@
|
|
1
1
|
module Rack
|
2
2
|
module Test
|
3
|
-
|
4
3
|
module Utils # :nodoc:
|
5
4
|
include Rack::Utils
|
5
|
+
extend Rack::Utils
|
6
6
|
|
7
7
|
def build_nested_query(value, prefix = nil)
|
8
8
|
case value
|
9
9
|
when Array
|
10
|
-
value.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
if value.empty?
|
11
|
+
"#{prefix}[]="
|
12
|
+
else
|
13
|
+
value.map do |v|
|
14
|
+
prefix = "#{prefix}[]" unless unescape(prefix) =~ /\[\]$/
|
15
|
+
build_nested_query(v, prefix.to_s)
|
16
|
+
end.join('&')
|
17
|
+
end
|
16
18
|
when Hash
|
17
19
|
value.map do |k, v|
|
18
20
|
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
19
|
-
end.join(
|
21
|
+
end.join('&')
|
20
22
|
when NilClass
|
21
23
|
prefix.to_s
|
22
24
|
else
|
23
25
|
"#{prefix}=#{escape(value)}"
|
24
26
|
end
|
25
27
|
end
|
26
|
-
|
27
28
|
module_function :build_nested_query
|
28
29
|
|
29
|
-
def build_multipart(params, first = true)
|
30
|
+
def build_multipart(params, first = true, multipart = false)
|
30
31
|
if first
|
31
|
-
unless params.is_a?(Hash)
|
32
|
-
raise ArgumentError, "value must be a Hash"
|
33
|
-
end
|
32
|
+
raise ArgumentError, 'value must be a Hash' unless params.is_a?(Hash)
|
34
33
|
|
35
|
-
multipart = false
|
36
34
|
query = lambda { |value|
|
37
35
|
case value
|
38
36
|
when Array
|
@@ -47,7 +45,7 @@ module Rack
|
|
47
45
|
return nil unless multipart
|
48
46
|
end
|
49
47
|
|
50
|
-
flattened_params =
|
48
|
+
flattened_params = {}
|
51
49
|
|
52
50
|
params.each do |key, value|
|
53
51
|
k = first ? key.to_s : "[#{key}]"
|
@@ -55,23 +53,21 @@ module Rack
|
|
55
53
|
case value
|
56
54
|
when Array
|
57
55
|
value.map do |v|
|
58
|
-
|
59
|
-
if (v.is_a?(Hash))
|
56
|
+
if v.is_a?(Hash)
|
60
57
|
nested_params = {}
|
61
|
-
build_multipart(v, false).each
|
58
|
+
build_multipart(v, false).each do |subkey, subvalue|
|
62
59
|
nested_params[subkey] = subvalue
|
63
|
-
|
60
|
+
end
|
64
61
|
flattened_params["#{k}[]"] ||= []
|
65
62
|
flattened_params["#{k}[]"] << nested_params
|
66
63
|
else
|
67
64
|
flattened_params["#{k}[]"] = value
|
68
65
|
end
|
69
|
-
|
70
66
|
end
|
71
67
|
when Hash
|
72
|
-
build_multipart(value, false).each
|
68
|
+
build_multipart(value, false).each do |subkey, subvalue|
|
73
69
|
flattened_params[k + subkey] = subvalue
|
74
|
-
|
70
|
+
end
|
75
71
|
else
|
76
72
|
flattened_params[k] = value
|
77
73
|
end
|
@@ -83,27 +79,28 @@ module Rack
|
|
83
79
|
flattened_params
|
84
80
|
end
|
85
81
|
end
|
86
|
-
|
87
82
|
module_function :build_multipart
|
88
83
|
|
89
|
-
|
84
|
+
private
|
85
|
+
|
90
86
|
def build_parts(parameters)
|
91
87
|
get_parts(parameters).join + "--#{MULTIPART_BOUNDARY}--\r"
|
92
88
|
end
|
89
|
+
module_function :build_parts
|
93
90
|
|
94
91
|
def get_parts(parameters)
|
95
|
-
parameters.map
|
96
|
-
if name =~ /\[\]\Z/ && value.is_a?(Array) && value.all? {|v| v.is_a?(Hash)}
|
97
|
-
value.map
|
92
|
+
parameters.map do |name, value|
|
93
|
+
if name =~ /\[\]\Z/ && value.is_a?(Array) && value.all? { |v| v.is_a?(Hash) }
|
94
|
+
value.map do |hash|
|
98
95
|
new_value = {}
|
99
|
-
hash.each { |k, v| new_value[name+k] = v }
|
96
|
+
hash.each { |k, v| new_value[name + k] = v }
|
100
97
|
get_parts(new_value).join
|
101
|
-
|
98
|
+
end.join
|
102
99
|
else
|
103
100
|
if value.respond_to?(:original_filename)
|
104
101
|
build_file_part(name, value)
|
105
102
|
|
106
|
-
elsif value.is_a?(Array)
|
103
|
+
elsif value.is_a?(Array) && value.all? { |v| v.respond_to?(:original_filename) }
|
107
104
|
value.map do |v|
|
108
105
|
build_file_part(name, v)
|
109
106
|
end.join
|
@@ -113,15 +110,14 @@ module Rack
|
|
113
110
|
Rack::Test.encoding_aware_strings? ? primitive_part.force_encoding('BINARY') : primitive_part
|
114
111
|
end
|
115
112
|
end
|
116
|
-
|
113
|
+
end
|
117
114
|
end
|
115
|
+
module_function :get_parts
|
118
116
|
|
119
117
|
def build_primitive_part(parameter_name, value)
|
120
|
-
unless value.is_a? Array
|
121
|
-
value = [value]
|
122
|
-
end
|
118
|
+
value = [value] unless value.is_a? Array
|
123
119
|
value.map do |v|
|
124
|
-
<<-EOF
|
120
|
+
<<-EOF
|
125
121
|
--#{MULTIPART_BOUNDARY}\r
|
126
122
|
Content-Disposition: form-data; name="#{parameter_name}"\r
|
127
123
|
\r
|
@@ -129,22 +125,20 @@ Content-Disposition: form-data; name="#{parameter_name}"\r
|
|
129
125
|
EOF
|
130
126
|
end.join
|
131
127
|
end
|
128
|
+
module_function :build_primitive_part
|
132
129
|
|
133
130
|
def build_file_part(parameter_name, uploaded_file)
|
134
|
-
|
135
|
-
|
136
|
-
<<-EOF
|
131
|
+
uploaded_file.set_encoding(Encoding::BINARY) if uploaded_file.respond_to?(:set_encoding)
|
132
|
+
<<-EOF
|
137
133
|
--#{MULTIPART_BOUNDARY}\r
|
138
134
|
Content-Disposition: form-data; name="#{parameter_name}"; filename="#{escape(uploaded_file.original_filename)}"\r
|
139
135
|
Content-Type: #{uploaded_file.content_type}\r
|
140
|
-
Content-Length: #{
|
136
|
+
Content-Length: #{uploaded_file.size}\r
|
141
137
|
\r
|
142
|
-
#{
|
138
|
+
#{uploaded_file.read}\r
|
143
139
|
EOF
|
144
|
-
end
|
145
140
|
end
|
146
|
-
|
141
|
+
module_function :build_file_part
|
147
142
|
end
|
148
|
-
|
149
143
|
end
|
150
144
|
end
|