rack-test 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/rack-test.svg)](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
|
+
[![Code Climate](https://codeclimate.com/github/codeclimate/codeclimate/badges/gpa.svg)](https://codeclimate.com/github/codeclimate/codeclimate)
|
5
|
+
[![Test Coverage](https://codeclimate.com/github/codeclimate/codeclimate/badges/coverage.svg)](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
|
- - ">="
|