rack-test 0.7.0 → 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 +4 -4
- data/History.md +17 -0
- data/README.md +40 -11
- data/lib/rack/mock_session.rb +6 -9
- data/lib/rack/test.rb +73 -77
- data/lib/rack/test/cookie_jar.rb +25 -31
- 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 +32 -21
- data/lib/rack/test/utils.rb +24 -36
- data/lib/rack/test/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c2c9dca5557f9f1d5894f9379e38e660698a178
|
4
|
+
data.tar.gz: aabbb52783cc359935090e319d462076cc0ce95c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed423ebaf4bbdb79a77804f3ed9314f88b5b9e97e9a0eeee0d596c43718bac918f62d4cfd8c1adaa72a0e8fce28f093f5bb7bc3268eec5025c6e47b7ce2556b9
|
7
|
+
data.tar.gz: a4ef34a8cb3e12bc432012444485af04c50c2780ec658ead424c337dbd3ecd3805a957065bb804b2b1dd73d495819094dfb018c0784cd9766a4f1be6110d7815
|
data/History.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 0.8.0 / 2017-11-20
|
2
|
+
|
3
|
+
* Minor enhancements
|
4
|
+
* Add a required_ruby_version of >= 2.2.2, similar to rack 2.0.1.
|
5
|
+
(Samuel Giddins #194)
|
6
|
+
* Remove new line from basic auth. (Felix Kleinschmidt #185)
|
7
|
+
* Rubocop fixes (Per Lundberg #196)
|
8
|
+
* Add how to install rack-test from github to README. (Jun Aruga #189)
|
9
|
+
* Update CodeClimate badges (Toshimaru #195)
|
10
|
+
* Add the ability to create Test::UploadedFile instances without
|
11
|
+
the file system (Adam Milligan #149)
|
12
|
+
* Add custom_request, remove duplication (Johannes Barre #184)
|
13
|
+
* README.md: Added note about how to post JSON (Per Lundberg #198)
|
14
|
+
* README.md: Added version badge (Per Lundberg #199)
|
15
|
+
* Bug fixes
|
16
|
+
* Bugfix for Cookies with multiple paths (Kyle Welsby #197)
|
17
|
+
|
1
18
|
## 0.7.0 / 2017-07-10
|
2
19
|
|
3
20
|
* Major enhancements
|
data/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# Rack::Test
|
2
|
+
[](https://badge.fury.io/rb/rack-test)
|
2
3
|
[<img src="https://travis-ci.org/rack-test/rack-test.svg?branch=master" />](https://travis-ci.org/rack-test/rack-test)
|
3
|
-
[
|
4
|
-
[
|
4
|
+
[](https://codeclimate.com/github/codeclimate/codeclimate)
|
5
|
+
[](https://codeclimate.com/github/codeclimate/codeclimate/coverage)
|
5
6
|
|
6
7
|
Code: https://github.com/rack-test/rack-test
|
7
8
|
|
@@ -18,7 +19,18 @@ to build on.
|
|
18
19
|
* Set request headers to be used by all subsequent requests
|
19
20
|
* Small footprint. Approximately 200 LOC
|
20
21
|
|
22
|
+
## Supported platforms
|
23
|
+
|
24
|
+
* 2.2.2+
|
25
|
+
* 2.3
|
26
|
+
* 2.4
|
27
|
+
* JRuby 9.1.+
|
28
|
+
|
29
|
+
If you are using Ruby 1.8, 1.9 or JRuby 1.7, use rack-test 0.6.3.
|
30
|
+
|
21
31
|
## Examples
|
32
|
+
(The examples use `Test::Unit` but it's equally possible to use `rack-test` with other testing frameworks like `rspec`.)
|
33
|
+
|
22
34
|
```ruby
|
23
35
|
require "test/unit"
|
24
36
|
require "rack/test"
|
@@ -33,25 +45,32 @@ class HomepageTest < Test::Unit::TestCase
|
|
33
45
|
end
|
34
46
|
|
35
47
|
def test_response_is_ok
|
36
|
-
get
|
48
|
+
get '/'
|
37
49
|
|
38
50
|
assert last_response.ok?
|
39
|
-
assert_equal last_response.body,
|
51
|
+
assert_equal last_response.body, 'All responses are OK'
|
40
52
|
end
|
41
|
-
|
53
|
+
|
42
54
|
def set_request_headers
|
43
55
|
headers 'Accept-Charset', 'utf-8'
|
44
|
-
get
|
56
|
+
get '/'
|
45
57
|
|
46
58
|
assert last_response.ok?
|
47
|
-
assert_equal last_response.body,
|
59
|
+
assert_equal last_response.body, 'All responses are OK'
|
48
60
|
end
|
49
61
|
|
50
62
|
def test_response_is_ok_for_other_paths
|
51
|
-
get
|
63
|
+
get '/other_paths'
|
52
64
|
|
53
65
|
assert last_response.ok?
|
54
|
-
assert_equal last_response.body,
|
66
|
+
assert_equal last_response.body, 'All responses are OK'
|
67
|
+
end
|
68
|
+
|
69
|
+
def post_with_json
|
70
|
+
# No assertion in this, we just demonstrate how you can post a JSON-encoded string.
|
71
|
+
# By default, Rack::Test will use HTTP form encoding if you pass in a Hash as the
|
72
|
+
# parameters, so make sure that `json` below is already a JSON-serialized string.
|
73
|
+
post(uri, json, { 'CONTENT_TYPE' => 'application/json' })
|
55
74
|
end
|
56
75
|
end
|
57
76
|
```
|
@@ -80,11 +99,21 @@ end
|
|
80
99
|
|
81
100
|
To install the latest release as a gem:
|
82
101
|
|
83
|
-
|
102
|
+
```
|
103
|
+
gem install rack-test
|
104
|
+
```
|
84
105
|
|
85
106
|
Or via Bundler:
|
86
107
|
|
87
|
-
|
108
|
+
```
|
109
|
+
gem 'rack-test', require: 'rack/test'
|
110
|
+
```
|
111
|
+
|
112
|
+
Or to install unreleased version via Bundler:
|
113
|
+
|
114
|
+
```
|
115
|
+
gem 'rack-test', github: 'rack-test', branch: 'master'
|
116
|
+
```
|
88
117
|
|
89
118
|
## Authors
|
90
119
|
|
data/lib/rack/mock_session.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Rack
|
2
|
-
|
3
2
|
class MockSession # :nodoc:
|
4
3
|
attr_writer :cookie_jar
|
5
4
|
attr_reader :default_host
|
@@ -25,16 +24,16 @@ module Rack
|
|
25
24
|
end
|
26
25
|
|
27
26
|
def request(uri, env)
|
28
|
-
env[
|
27
|
+
env['HTTP_COOKIE'] ||= cookie_jar.for(uri)
|
29
28
|
@last_request = Rack::Request.new(env)
|
30
29
|
status, headers, body = @app.call(@last_request.env)
|
31
30
|
|
32
|
-
@last_response = MockResponse.new(status, headers, body, env[
|
31
|
+
@last_response = MockResponse.new(status, headers, body, env['rack.errors'].flush)
|
33
32
|
body.close if body.respond_to?(:close)
|
34
33
|
|
35
|
-
cookie_jar.merge(last_response.headers[
|
34
|
+
cookie_jar.merge(last_response.headers['Set-Cookie'], uri)
|
36
35
|
|
37
|
-
@after_request.each
|
36
|
+
@after_request.each(&:call)
|
38
37
|
|
39
38
|
if @last_response.respond_to?(:finish)
|
40
39
|
@last_response.finish
|
@@ -46,21 +45,19 @@ module Rack
|
|
46
45
|
# Return the last request issued in the session. Raises an error if no
|
47
46
|
# requests have been sent yet.
|
48
47
|
def last_request
|
49
|
-
raise Rack::Test::Error
|
48
|
+
raise Rack::Test::Error, 'No request yet. Request a page first.' unless @last_request
|
50
49
|
@last_request
|
51
50
|
end
|
52
51
|
|
53
52
|
# Return the last response received in the session. Raises an error if
|
54
53
|
# no requests have been sent yet.
|
55
54
|
def last_response
|
56
|
-
raise Rack::Test::Error
|
55
|
+
raise Rack::Test::Error, 'No response yet. Request a page first.' unless @last_response
|
57
56
|
@last_response
|
58
57
|
end
|
59
58
|
|
60
59
|
def cookie_jar
|
61
60
|
@cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
|
62
61
|
end
|
63
|
-
|
64
62
|
end
|
65
|
-
|
66
63
|
end
|
data/lib/rack/test.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
1
|
+
require 'uri'
|
2
|
+
require 'rack'
|
3
|
+
require 'rack/mock_session'
|
4
|
+
require 'rack/test/cookie_jar'
|
5
|
+
require 'rack/test/mock_digest_request'
|
6
|
+
require 'rack/test/utils'
|
7
|
+
require 'rack/test/methods'
|
8
|
+
require 'rack/test/uploaded_file'
|
9
|
+
require 'rack/test/version'
|
10
10
|
|
11
11
|
module Rack
|
12
12
|
module Test
|
13
|
-
DEFAULT_HOST =
|
14
|
-
MULTIPART_BOUNDARY =
|
13
|
+
DEFAULT_HOST = 'example.org'.freeze
|
14
|
+
MULTIPART_BOUNDARY = '----------XnJLe9ZIbbGUYtzPQJ16u1'.freeze
|
15
15
|
|
16
16
|
# The common base class for exceptions raised by Rack::Test
|
17
17
|
class Error < StandardError; end
|
@@ -38,10 +38,10 @@ module Rack
|
|
38
38
|
@digest_username = nil
|
39
39
|
@digest_password = nil
|
40
40
|
|
41
|
-
if mock_session.is_a?(MockSession)
|
42
|
-
|
41
|
+
@rack_mock_session = if mock_session.is_a?(MockSession)
|
42
|
+
mock_session
|
43
43
|
else
|
44
|
-
|
44
|
+
MockSession.new(mock_session)
|
45
45
|
end
|
46
46
|
|
47
47
|
@default_host = @rack_mock_session.default_host
|
@@ -55,8 +55,7 @@ module Rack
|
|
55
55
|
# Example:
|
56
56
|
# get "/"
|
57
57
|
def get(uri, params = {}, env = {}, &block)
|
58
|
-
|
59
|
-
process_request(uri, env, &block)
|
58
|
+
custom_request('GET', uri, params, env, &block)
|
60
59
|
end
|
61
60
|
|
62
61
|
# Issue a POST request for the given URI. See #get
|
@@ -64,8 +63,7 @@ module Rack
|
|
64
63
|
# Example:
|
65
64
|
# post "/signup", "name" => "Bryan"
|
66
65
|
def post(uri, params = {}, env = {}, &block)
|
67
|
-
|
68
|
-
process_request(uri, env, &block)
|
66
|
+
custom_request('POST', uri, params, env, &block)
|
69
67
|
end
|
70
68
|
|
71
69
|
# Issue a PUT request for the given URI. See #get
|
@@ -73,8 +71,7 @@ module Rack
|
|
73
71
|
# Example:
|
74
72
|
# put "/"
|
75
73
|
def put(uri, params = {}, env = {}, &block)
|
76
|
-
|
77
|
-
process_request(uri, env, &block)
|
74
|
+
custom_request('PUT', uri, params, env, &block)
|
78
75
|
end
|
79
76
|
|
80
77
|
# Issue a PATCH request for the given URI. See #get
|
@@ -82,8 +79,7 @@ module Rack
|
|
82
79
|
# Example:
|
83
80
|
# patch "/"
|
84
81
|
def patch(uri, params = {}, env = {}, &block)
|
85
|
-
|
86
|
-
process_request(uri, env, &block)
|
82
|
+
custom_request('PATCH', uri, params, env, &block)
|
87
83
|
end
|
88
84
|
|
89
85
|
# Issue a DELETE request for the given URI. See #get
|
@@ -91,8 +87,7 @@ module Rack
|
|
91
87
|
# Example:
|
92
88
|
# delete "/"
|
93
89
|
def delete(uri, params = {}, env = {}, &block)
|
94
|
-
|
95
|
-
process_request(uri, env, &block)
|
90
|
+
custom_request('DELETE', uri, params, env, &block)
|
96
91
|
end
|
97
92
|
|
98
93
|
# Issue an OPTIONS request for the given URI. See #get
|
@@ -100,8 +95,7 @@ module Rack
|
|
100
95
|
# Example:
|
101
96
|
# options "/"
|
102
97
|
def options(uri, params = {}, env = {}, &block)
|
103
|
-
|
104
|
-
process_request(uri, env, &block)
|
98
|
+
custom_request('OPTIONS', uri, params, env, &block)
|
105
99
|
end
|
106
100
|
|
107
101
|
# Issue a HEAD request for the given URI. See #get
|
@@ -109,8 +103,7 @@ module Rack
|
|
109
103
|
# Example:
|
110
104
|
# head "/"
|
111
105
|
def head(uri, params = {}, env = {}, &block)
|
112
|
-
|
113
|
-
process_request(uri, env, &block)
|
106
|
+
custom_request('HEAD', uri, params, env, &block)
|
114
107
|
end
|
115
108
|
|
116
109
|
# Issue a request to the Rack app for the given URI and optional Rack
|
@@ -121,10 +114,21 @@ module Rack
|
|
121
114
|
# Example:
|
122
115
|
# request "/"
|
123
116
|
def request(uri, env = {}, &block)
|
117
|
+
uri = parse_uri(uri, env)
|
124
118
|
env = env_for(uri, env)
|
125
119
|
process_request(uri, env, &block)
|
126
120
|
end
|
127
121
|
|
122
|
+
# Issue a request using the given verb for the given URI. See #get
|
123
|
+
#
|
124
|
+
# Example:
|
125
|
+
# custom_request "LINK", "/"
|
126
|
+
def custom_request(verb, uri, params = {}, env = {}, &block)
|
127
|
+
uri = parse_uri(uri, env)
|
128
|
+
env = env_for(uri, env.merge(method: verb.to_s.upcase, params: params))
|
129
|
+
process_request(uri, env, &block)
|
130
|
+
end
|
131
|
+
|
128
132
|
# Set a header to be included on all subsequent requests through the
|
129
133
|
# session. Use a value of nil to remove a previously configured header.
|
130
134
|
#
|
@@ -160,11 +164,11 @@ module Rack
|
|
160
164
|
# Example:
|
161
165
|
# basic_authorize "bryan", "secret"
|
162
166
|
def basic_authorize(username, password)
|
163
|
-
encoded_login = ["#{username}:#{password}"].pack(
|
167
|
+
encoded_login = ["#{username}:#{password}"].pack('m0')
|
164
168
|
header('Authorization', "Basic #{encoded_login}")
|
165
169
|
end
|
166
170
|
|
167
|
-
|
171
|
+
alias authorize basic_authorize
|
168
172
|
|
169
173
|
# Set the username and password for HTTP Digest authorization, to be
|
170
174
|
# included in subsequent requests in the HTTP_AUTHORIZATION header.
|
@@ -182,48 +186,52 @@ module Rack
|
|
182
186
|
# a redirect, an error will be raised.
|
183
187
|
def follow_redirect!
|
184
188
|
unless last_response.redirect?
|
185
|
-
raise Error
|
189
|
+
raise Error, 'Last response was not a redirect. Cannot follow_redirect!'
|
186
190
|
end
|
187
191
|
if last_response.status == 307
|
188
|
-
send(last_request.request_method.downcase.to_sym, last_response[
|
192
|
+
send(last_request.request_method.downcase.to_sym, last_response['Location'], last_request.params, 'HTTP_REFERER' => last_request.url)
|
189
193
|
else
|
190
|
-
get(last_response[
|
194
|
+
get(last_response['Location'], {}, 'HTTP_REFERER' => last_request.url)
|
191
195
|
end
|
192
196
|
end
|
193
197
|
|
194
|
-
|
198
|
+
private
|
195
199
|
|
196
|
-
def
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
+
def parse_uri(path, env)
|
201
|
+
URI.parse(path).tap do |uri|
|
202
|
+
uri.path = "/#{uri.path}" unless uri.path[0] == '/'
|
203
|
+
uri.host ||= @default_host
|
204
|
+
uri.scheme ||= 'https' if env['HTTPS'] == 'on'
|
205
|
+
end
|
206
|
+
end
|
200
207
|
|
208
|
+
def env_for(uri, env)
|
201
209
|
env = default_env.merge(env)
|
202
210
|
|
203
|
-
env[
|
211
|
+
env['HTTP_HOST'] ||= [uri.host, (uri.port if uri.port != uri.default_port)].compact.join(':')
|
204
212
|
|
205
|
-
env.update(
|
206
|
-
env[
|
213
|
+
env.update('HTTPS' => 'on') if URI::HTTPS === uri
|
214
|
+
env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' if env[:xhr]
|
207
215
|
|
208
216
|
# TODO: Remove this after Rack 1.1 has been released.
|
209
217
|
# Stringifying and upcasing methods has be commit upstream
|
210
|
-
env[
|
218
|
+
env['REQUEST_METHOD'] ||= env[:method] ? env[:method].to_s.upcase : 'GET'
|
211
219
|
|
212
|
-
if [
|
220
|
+
if %w[GET DELETE].include?(env['REQUEST_METHOD'])
|
213
221
|
# merge :params with the query string
|
214
222
|
if params = env[:params]
|
215
223
|
params = parse_nested_query(params) if params.is_a?(String)
|
216
224
|
|
217
|
-
uri.query = [uri.query, build_nested_query(params)].compact.reject { |v| v == '' }.join(
|
225
|
+
uri.query = [uri.query, build_nested_query(params)].compact.reject { |v| v == '' }.join('&')
|
218
226
|
end
|
219
|
-
elsif !env.
|
220
|
-
env[
|
227
|
+
elsif !env.key?(:input)
|
228
|
+
env['CONTENT_TYPE'] ||= 'application/x-www-form-urlencoded'
|
221
229
|
|
222
230
|
if env[:params].is_a?(Hash)
|
223
231
|
if data = build_multipart(env[:params])
|
224
232
|
env[:input] = data
|
225
|
-
env[
|
226
|
-
env[
|
233
|
+
env['CONTENT_LENGTH'] ||= data.length.to_s
|
234
|
+
env['CONTENT_TYPE'] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
|
227
235
|
else
|
228
236
|
env[:input] = params_to_string(env[:params])
|
229
237
|
end
|
@@ -234,25 +242,17 @@ module Rack
|
|
234
242
|
|
235
243
|
env.delete(:params)
|
236
244
|
|
237
|
-
if env.
|
238
|
-
set_cookie(env.delete(:cookie), uri)
|
239
|
-
end
|
245
|
+
set_cookie(env.delete(:cookie), uri) if env.key?(:cookie)
|
240
246
|
|
241
247
|
Rack::MockRequest.env_for(uri.to_s, env)
|
242
248
|
end
|
243
249
|
|
244
250
|
def process_request(uri, env)
|
245
|
-
uri = URI.parse(uri)
|
246
|
-
uri.host ||= @default_host
|
247
|
-
uri.scheme ||= "https" if env["HTTPS"] == "on"
|
248
|
-
|
249
251
|
@rack_mock_session.request(uri, env)
|
250
252
|
|
251
253
|
if retry_with_digest_auth?(env)
|
252
|
-
auth_env = env.merge(
|
253
|
-
|
254
|
-
"rack-test.digest_auth_retry" => true
|
255
|
-
})
|
254
|
+
auth_env = env.merge('HTTP_AUTHORIZATION' => digest_auth_header,
|
255
|
+
'rack-test.digest_auth_retry' => true)
|
256
256
|
auth_env.delete('rack.request')
|
257
257
|
process_request(uri.path, auth_env)
|
258
258
|
else
|
@@ -263,26 +263,24 @@ module Rack
|
|
263
263
|
end
|
264
264
|
|
265
265
|
def digest_auth_header
|
266
|
-
challenge = last_response[
|
266
|
+
challenge = last_response['WWW-Authenticate'].split(' ', 2).last
|
267
267
|
params = Rack::Auth::Digest::Params.parse(challenge)
|
268
268
|
|
269
|
-
params.merge!(
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
"method" => last_request.env["REQUEST_METHOD"],
|
275
|
-
})
|
269
|
+
params.merge!('username' => @digest_username,
|
270
|
+
'nc' => '00000001',
|
271
|
+
'cnonce' => 'nonsensenonce',
|
272
|
+
'uri' => last_request.fullpath,
|
273
|
+
'method' => last_request.env['REQUEST_METHOD'])
|
276
274
|
|
277
|
-
params[
|
275
|
+
params['response'] = MockDigestRequest.new(params).response(@digest_password)
|
278
276
|
|
279
277
|
"Digest #{params}"
|
280
278
|
end
|
281
279
|
|
282
280
|
def retry_with_digest_auth?(env)
|
283
281
|
last_response.status == 401 &&
|
284
|
-
|
285
|
-
|
282
|
+
digest_auth_configured? &&
|
283
|
+
!env['rack-test.digest_auth_retry']
|
286
284
|
end
|
287
285
|
|
288
286
|
def digest_auth_configured?
|
@@ -290,15 +288,15 @@ module Rack
|
|
290
288
|
end
|
291
289
|
|
292
290
|
def default_env
|
293
|
-
{
|
291
|
+
{ 'rack.test' => true, 'REMOTE_ADDR' => '127.0.0.1' }.merge(@env).merge(headers_for_env)
|
294
292
|
end
|
295
293
|
|
296
294
|
def headers_for_env
|
297
295
|
converted_headers = {}
|
298
296
|
|
299
297
|
@headers.each do |name, value|
|
300
|
-
env_key = name.upcase.
|
301
|
-
env_key =
|
298
|
+
env_key = name.upcase.tr('-', '_')
|
299
|
+
env_key = 'HTTP_' + env_key unless env_key == 'CONTENT_TYPE'
|
302
300
|
converted_headers[env_key] = value
|
303
301
|
end
|
304
302
|
|
@@ -308,16 +306,14 @@ module Rack
|
|
308
306
|
def params_to_string(params)
|
309
307
|
case params
|
310
308
|
when Hash then build_nested_query(params)
|
311
|
-
when nil then
|
309
|
+
when nil then ''
|
312
310
|
else params
|
313
311
|
end
|
314
312
|
end
|
315
|
-
|
316
313
|
end
|
317
314
|
|
318
315
|
def self.encoding_aware_strings?
|
319
|
-
defined?(Encoding) &&
|
316
|
+
defined?(Encoding) && ''.respond_to?(:encode)
|
320
317
|
end
|
321
|
-
|
322
318
|
end
|
323
319
|
end
|
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,25 +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')
|
49
48
|
end
|
50
49
|
|
51
50
|
def http_only?
|
52
|
-
@options.
|
51
|
+
@options.key?('HttpOnly')
|
53
52
|
end
|
54
53
|
|
55
54
|
# :api: private
|
56
55
|
def path
|
57
|
-
|
56
|
+
([*@options['path']].first.split(',').first || '/').strip
|
58
57
|
end
|
59
58
|
|
60
59
|
# :api: private
|
61
60
|
def expires
|
62
|
-
Time.parse(@options[
|
61
|
+
Time.parse(@options['expires']) if @options['expires']
|
63
62
|
end
|
64
63
|
|
65
64
|
# :api: private
|
@@ -71,19 +70,17 @@ module Rack
|
|
71
70
|
def valid?(uri)
|
72
71
|
uri ||= default_uri
|
73
72
|
|
74
|
-
if uri.host.nil?
|
75
|
-
uri.host = @default_host
|
76
|
-
end
|
73
|
+
uri.host = @default_host if uri.host.nil?
|
77
74
|
|
78
75
|
real_domain = domain =~ /^\./ ? domain[1..-1] : domain
|
79
|
-
(!secure? || (secure? && uri.scheme ==
|
80
|
-
|
81
|
-
|
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)}")
|
82
79
|
end
|
83
80
|
|
84
81
|
# :api: private
|
85
82
|
def matches?(uri)
|
86
|
-
!
|
83
|
+
!expired? && valid?(uri)
|
87
84
|
end
|
88
85
|
|
89
86
|
# :api: private
|
@@ -91,25 +88,24 @@ module Rack
|
|
91
88
|
# Orders the cookies from least specific to most
|
92
89
|
[name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
|
93
90
|
end
|
91
|
+
|
94
92
|
def to_h
|
95
93
|
@options.merge(
|
96
94
|
'value' => @value,
|
97
95
|
'HttpOnly' => http_only?,
|
98
|
-
'secure' => secure
|
96
|
+
'secure' => secure?
|
99
97
|
)
|
100
98
|
end
|
101
|
-
|
99
|
+
alias to_hash to_h
|
102
100
|
|
103
|
-
|
101
|
+
protected
|
104
102
|
|
105
103
|
def default_uri
|
106
|
-
URI.parse(
|
104
|
+
URI.parse('//' + @default_host + '/')
|
107
105
|
end
|
108
|
-
|
109
106
|
end
|
110
107
|
|
111
108
|
class CookieJar # :nodoc:
|
112
|
-
|
113
109
|
# :api: private
|
114
110
|
def initialize(cookies = [], default_host = DEFAULT_HOST)
|
115
111
|
@default_host = default_host
|
@@ -128,7 +124,7 @@ module Rack
|
|
128
124
|
end
|
129
125
|
|
130
126
|
def get_cookie(name)
|
131
|
-
hash_for(nil).fetch(name,nil)
|
127
|
+
hash_for(nil).fetch(name, nil)
|
132
128
|
end
|
133
129
|
|
134
130
|
def delete(name)
|
@@ -142,7 +138,7 @@ module Rack
|
|
142
138
|
|
143
139
|
if raw_cookies.is_a? String
|
144
140
|
raw_cookies = raw_cookies.split("\n")
|
145
|
-
raw_cookies.reject!
|
141
|
+
raw_cookies.reject!(&:empty?)
|
146
142
|
end
|
147
143
|
|
148
144
|
raw_cookies.each do |raw_cookie|
|
@@ -162,7 +158,7 @@ module Rack
|
|
162
158
|
|
163
159
|
# :api: private
|
164
160
|
def for(uri)
|
165
|
-
hash_for(uri).values.map
|
161
|
+
hash_for(uri).values.map(&:raw).join(';')
|
166
162
|
end
|
167
163
|
|
168
164
|
def to_hash
|
@@ -172,10 +168,10 @@ module Rack
|
|
172
168
|
cookies[name] = cookie.value
|
173
169
|
end
|
174
170
|
|
175
|
-
|
171
|
+
cookies
|
176
172
|
end
|
177
173
|
|
178
|
-
|
174
|
+
protected
|
179
175
|
|
180
176
|
def hash_for(uri = nil)
|
181
177
|
cookies = {}
|
@@ -189,10 +185,8 @@ module Rack
|
|
189
185
|
cookies[cookie.name] = cookie if !uri || cookie.matches?(uri)
|
190
186
|
end
|
191
187
|
|
192
|
-
|
188
|
+
cookies
|
193
189
|
end
|
194
|
-
|
195
190
|
end
|
196
|
-
|
197
191
|
end
|
198
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,33 +18,28 @@ 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, ::File.extname(path)])
|
30
|
-
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
|
31
28
|
@tempfile.binmode if binary
|
32
|
-
|
33
|
-
ObjectSpace.define_finalizer(self, self.class.finalize(@tempfile))
|
34
|
-
|
35
|
-
FileUtils.copy_file(path, @tempfile.path)
|
36
29
|
end
|
37
30
|
|
38
31
|
def path
|
39
|
-
|
32
|
+
tempfile.path
|
40
33
|
end
|
41
34
|
|
42
|
-
|
35
|
+
alias local_path path
|
43
36
|
|
44
37
|
def method_missing(method_name, *args, &block) #:nodoc:
|
45
|
-
|
38
|
+
tempfile.public_send(method_name, *args, &block)
|
46
39
|
end
|
47
40
|
|
48
|
-
def
|
49
|
-
|
41
|
+
def respond_to_missing?(method_name, include_private = false) #:nodoc:
|
42
|
+
tempfile.respond_to?(method_name, include_private) || super
|
50
43
|
end
|
51
44
|
|
52
45
|
def self.finalize(file)
|
@@ -58,7 +51,25 @@ module Rack
|
|
58
51
|
file.unlink
|
59
52
|
end
|
60
53
|
|
61
|
-
|
54
|
+
private
|
62
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
|
63
74
|
end
|
64
75
|
end
|
data/lib/rack/test/utils.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Rack
|
2
2
|
module Test
|
3
|
-
|
4
3
|
module Utils # :nodoc:
|
5
4
|
include Rack::Utils
|
6
5
|
extend Rack::Utils
|
@@ -12,16 +11,14 @@ module Rack
|
|
12
11
|
"#{prefix}[]="
|
13
12
|
else
|
14
13
|
value.map do |v|
|
15
|
-
unless unescape(prefix) =~ /\[\]$/
|
16
|
-
|
17
|
-
|
18
|
-
build_nested_query(v, "#{prefix}")
|
19
|
-
end.join("&")
|
14
|
+
prefix = "#{prefix}[]" unless unescape(prefix) =~ /\[\]$/
|
15
|
+
build_nested_query(v, prefix.to_s)
|
16
|
+
end.join('&')
|
20
17
|
end
|
21
18
|
when Hash
|
22
19
|
value.map do |k, v|
|
23
20
|
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
24
|
-
end.join(
|
21
|
+
end.join('&')
|
25
22
|
when NilClass
|
26
23
|
prefix.to_s
|
27
24
|
else
|
@@ -32,9 +29,7 @@ module Rack
|
|
32
29
|
|
33
30
|
def build_multipart(params, first = true, multipart = false)
|
34
31
|
if first
|
35
|
-
unless params.is_a?(Hash)
|
36
|
-
raise ArgumentError, "value must be a Hash"
|
37
|
-
end
|
32
|
+
raise ArgumentError, 'value must be a Hash' unless params.is_a?(Hash)
|
38
33
|
|
39
34
|
query = lambda { |value|
|
40
35
|
case value
|
@@ -50,7 +45,7 @@ module Rack
|
|
50
45
|
return nil unless multipart
|
51
46
|
end
|
52
47
|
|
53
|
-
flattened_params =
|
48
|
+
flattened_params = {}
|
54
49
|
|
55
50
|
params.each do |key, value|
|
56
51
|
k = first ? key.to_s : "[#{key}]"
|
@@ -58,23 +53,21 @@ module Rack
|
|
58
53
|
case value
|
59
54
|
when Array
|
60
55
|
value.map do |v|
|
61
|
-
|
62
|
-
if (v.is_a?(Hash))
|
56
|
+
if v.is_a?(Hash)
|
63
57
|
nested_params = {}
|
64
|
-
build_multipart(v, false).each
|
58
|
+
build_multipart(v, false).each do |subkey, subvalue|
|
65
59
|
nested_params[subkey] = subvalue
|
66
|
-
|
60
|
+
end
|
67
61
|
flattened_params["#{k}[]"] ||= []
|
68
62
|
flattened_params["#{k}[]"] << nested_params
|
69
63
|
else
|
70
64
|
flattened_params["#{k}[]"] = value
|
71
65
|
end
|
72
|
-
|
73
66
|
end
|
74
67
|
when Hash
|
75
|
-
build_multipart(value, false).each
|
68
|
+
build_multipart(value, false).each do |subkey, subvalue|
|
76
69
|
flattened_params[k + subkey] = subvalue
|
77
|
-
|
70
|
+
end
|
78
71
|
else
|
79
72
|
flattened_params[k] = value
|
80
73
|
end
|
@@ -89,24 +82,25 @@ module Rack
|
|
89
82
|
module_function :build_multipart
|
90
83
|
|
91
84
|
private
|
85
|
+
|
92
86
|
def build_parts(parameters)
|
93
87
|
get_parts(parameters).join + "--#{MULTIPART_BOUNDARY}--\r"
|
94
88
|
end
|
95
89
|
module_function :build_parts
|
96
90
|
|
97
91
|
def get_parts(parameters)
|
98
|
-
parameters.map
|
99
|
-
if name =~ /\[\]\Z/ && value.is_a?(Array) && value.all? {|v| v.is_a?(Hash)}
|
100
|
-
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|
|
101
95
|
new_value = {}
|
102
|
-
hash.each { |k, v| new_value[name+k] = v }
|
96
|
+
hash.each { |k, v| new_value[name + k] = v }
|
103
97
|
get_parts(new_value).join
|
104
|
-
|
98
|
+
end.join
|
105
99
|
else
|
106
100
|
if value.respond_to?(:original_filename)
|
107
101
|
build_file_part(name, value)
|
108
102
|
|
109
|
-
elsif value.is_a?(Array)
|
103
|
+
elsif value.is_a?(Array) && value.all? { |v| v.respond_to?(:original_filename) }
|
110
104
|
value.map do |v|
|
111
105
|
build_file_part(name, v)
|
112
106
|
end.join
|
@@ -116,14 +110,12 @@ module Rack
|
|
116
110
|
Rack::Test.encoding_aware_strings? ? primitive_part.force_encoding('BINARY') : primitive_part
|
117
111
|
end
|
118
112
|
end
|
119
|
-
|
113
|
+
end
|
120
114
|
end
|
121
115
|
module_function :get_parts
|
122
116
|
|
123
117
|
def build_primitive_part(parameter_name, value)
|
124
|
-
unless value.is_a? Array
|
125
|
-
value = [value]
|
126
|
-
end
|
118
|
+
value = [value] unless value.is_a? Array
|
127
119
|
value.map do |v|
|
128
120
|
<<-EOF
|
129
121
|
--#{MULTIPART_BOUNDARY}\r
|
@@ -136,21 +128,17 @@ EOF
|
|
136
128
|
module_function :build_primitive_part
|
137
129
|
|
138
130
|
def build_file_part(parameter_name, uploaded_file)
|
139
|
-
|
140
|
-
|
141
|
-
<<-EOF
|
131
|
+
uploaded_file.set_encoding(Encoding::BINARY) if uploaded_file.respond_to?(:set_encoding)
|
132
|
+
<<-EOF
|
142
133
|
--#{MULTIPART_BOUNDARY}\r
|
143
134
|
Content-Disposition: form-data; name="#{parameter_name}"; filename="#{escape(uploaded_file.original_filename)}"\r
|
144
135
|
Content-Type: #{uploaded_file.content_type}\r
|
145
|
-
Content-Length: #{
|
136
|
+
Content-Length: #{uploaded_file.size}\r
|
146
137
|
\r
|
147
|
-
#{
|
138
|
+
#{uploaded_file.read}\r
|
148
139
|
EOF
|
149
|
-
end
|
150
140
|
end
|
151
141
|
module_function :build_file_part
|
152
|
-
|
153
142
|
end
|
154
|
-
|
155
143
|
end
|
156
144
|
end
|
data/lib/rack/test/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-test
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryan Helmkamp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -173,7 +173,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
173
173
|
requirements:
|
174
174
|
- - ">="
|
175
175
|
- !ruby/object:Gem::Version
|
176
|
-
version:
|
176
|
+
version: 2.2.2
|
177
177
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
178
178
|
requirements:
|
179
179
|
- - ">="
|