rack-test 0.6.3 → 0.8.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.txt → History.md} +87 -58
- data/README.md +139 -0
- data/lib/rack/mock_session.rb +6 -9
- data/lib/rack/test/cookie_jar.rb +38 -28
- 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 +41 -19
- data/lib/rack/test/utils.rb +37 -43
- data/lib/rack/test/version.rb +5 -0
- data/lib/rack/test.rb +79 -78
- metadata +139 -60
- data/.document +0 -4
- data/.gitignore +0 -6
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -41
- 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,16 +89,23 @@ 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:
|
100
|
-
|
101
109
|
# :api: private
|
102
110
|
def initialize(cookies = [], default_host = DEFAULT_HOST)
|
103
111
|
@default_host = default_host
|
@@ -108,13 +116,17 @@ module Rack
|
|
108
116
|
def [](name)
|
109
117
|
cookies = hash_for(nil)
|
110
118
|
# TODO: Should be case insensitive
|
111
|
-
cookies[name] && cookies[name].value
|
119
|
+
cookies[name.to_s] && cookies[name.to_s].value
|
112
120
|
end
|
113
121
|
|
114
122
|
def []=(name, value)
|
115
123
|
merge("#{name}=#{Rack::Utils.escape(value)}")
|
116
124
|
end
|
117
125
|
|
126
|
+
def get_cookie(name)
|
127
|
+
hash_for(nil).fetch(name, nil)
|
128
|
+
end
|
129
|
+
|
118
130
|
def delete(name)
|
119
131
|
@cookies.reject! do |cookie|
|
120
132
|
cookie.name == name
|
@@ -126,7 +138,7 @@ module Rack
|
|
126
138
|
|
127
139
|
if raw_cookies.is_a? String
|
128
140
|
raw_cookies = raw_cookies.split("\n")
|
129
|
-
raw_cookies.reject!
|
141
|
+
raw_cookies.reject!(&:empty?)
|
130
142
|
end
|
131
143
|
|
132
144
|
raw_cookies.each do |raw_cookie|
|
@@ -146,7 +158,7 @@ module Rack
|
|
146
158
|
|
147
159
|
# :api: private
|
148
160
|
def for(uri)
|
149
|
-
hash_for(uri).values.map
|
161
|
+
hash_for(uri).values.map(&:raw).join(';')
|
150
162
|
end
|
151
163
|
|
152
164
|
def to_hash
|
@@ -156,10 +168,10 @@ module Rack
|
|
156
168
|
cookies[name] = cookie.value
|
157
169
|
end
|
158
170
|
|
159
|
-
|
171
|
+
cookies
|
160
172
|
end
|
161
173
|
|
162
|
-
|
174
|
+
protected
|
163
175
|
|
164
176
|
def hash_for(uri = nil)
|
165
177
|
cookies = {}
|
@@ -173,10 +185,8 @@ module Rack
|
|
173
185
|
cookies[cookie.name] = cookie if !uri || cookie.matches?(uri)
|
174
186
|
end
|
175
187
|
|
176
|
-
|
188
|
+
cookies
|
177
189
|
end
|
178
|
-
|
179
190
|
end
|
180
|
-
|
181
191
|
end
|
182
192
|
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,14 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'tempfile'
|
2
|
+
require 'fileutils'
|
3
3
|
|
4
4
|
module Rack
|
5
5
|
module Test
|
6
|
-
|
7
6
|
# Wraps a Tempfile with a content type. Including one or more UploadedFile's
|
8
7
|
# in the params causes Rack::Test to build and issue a multipart request.
|
9
8
|
#
|
10
9
|
# Example:
|
11
10
|
# post "/photos", "file" => Rack::Test::UploadedFile.new("me.jpg", "image/jpeg")
|
12
11
|
class UploadedFile
|
13
|
-
|
14
12
|
# The filename, *not* including the path, of the "uploaded" file
|
15
13
|
attr_reader :original_filename
|
16
14
|
|
@@ -20,34 +18,58 @@ module Rack
|
|
20
18
|
# The content type of the "uploaded" file
|
21
19
|
attr_accessor :content_type
|
22
20
|
|
23
|
-
def initialize(
|
24
|
-
|
25
|
-
|
21
|
+
def initialize(content, content_type = 'text/plain', binary = false, original_filename: nil)
|
22
|
+
if content.respond_to?(:read)
|
23
|
+
initialize_from_io(content, original_filename)
|
24
|
+
else
|
25
|
+
initialize_from_file_path(content)
|
26
|
+
end
|
26
27
|
@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
28
|
@tempfile.binmode if binary
|
32
|
-
|
33
|
-
FileUtils.copy_file(path, @tempfile.path)
|
34
29
|
end
|
35
30
|
|
36
31
|
def path
|
37
|
-
|
32
|
+
tempfile.path
|
38
33
|
end
|
39
34
|
|
40
|
-
|
35
|
+
alias local_path path
|
41
36
|
|
42
37
|
def method_missing(method_name, *args, &block) #:nodoc:
|
43
|
-
|
38
|
+
tempfile.public_send(method_name, *args, &block)
|
44
39
|
end
|
45
40
|
|
46
|
-
def
|
47
|
-
|
41
|
+
def respond_to_missing?(method_name, include_private = false) #:nodoc:
|
42
|
+
tempfile.respond_to?(method_name, include_private) || super
|
48
43
|
end
|
49
44
|
|
50
|
-
|
45
|
+
def self.finalize(file)
|
46
|
+
proc { actually_finalize file }
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.actually_finalize(file)
|
50
|
+
file.close
|
51
|
+
file.unlink
|
52
|
+
end
|
51
53
|
|
54
|
+
private
|
55
|
+
|
56
|
+
def initialize_from_io(io, original_filename)
|
57
|
+
@tempfile = io
|
58
|
+
@original_filename = original_filename || raise(ArgumentError, 'Missing `original_filename` for IO')
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize_from_file_path(path)
|
62
|
+
raise "#{path} file does not exist" unless ::File.exist?(path)
|
63
|
+
|
64
|
+
@original_filename = ::File.basename(path)
|
65
|
+
|
66
|
+
@tempfile = Tempfile.new([@original_filename, ::File.extname(path)])
|
67
|
+
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
|
68
|
+
|
69
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(@tempfile))
|
70
|
+
|
71
|
+
FileUtils.copy_file(path, @tempfile.path)
|
72
|
+
end
|
73
|
+
end
|
52
74
|
end
|
53
75
|
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
|