rack-test 2.1.0 → 2.3.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 +34 -0
- data/MIT-LICENSE.txt +1 -1
- data/README.md +16 -0
- data/lib/rack/test/cookie_jar.rb +10 -11
- data/lib/rack/test/uploaded_file.rb +8 -20
- data/lib/rack/test/utils.rb +20 -15
- data/lib/rack/test/version.rb +1 -1
- data/lib/rack/test.rb +39 -14
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 434b9a347a037eb6639e5e154affb301394d2067ba701dfaa5229189e7c2c895
|
|
4
|
+
data.tar.gz: 066fcdc1df73ec7bc4ecab58ba971762f6dd40cbf8df1f7e91bb6d72a31304e0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60c104aea1a7d8fef5be9a1e0103f5bafb81dcbdc47e3a3a91a3ea2667413ccdcf0cd6a6d9327cbd0a36f27bbb2e4a72ee6b442484b833a88ad24d90d3d1a001
|
|
7
|
+
data.tar.gz: 066d410b73e733a74b0dea06827f7a64b80b2d87d4d3081caf8456f0b63e3c9ba455ed1fc635440d4ba89c7844fe27e62cc745775ebd95869e66d78250384896
|
data/History.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
## 2.3.0 / 2026-10-21
|
|
2
|
+
|
|
3
|
+
* Bug fixes:
|
|
4
|
+
* Do not mutate arguments given to `Rack::Utils.build_multipart`
|
|
5
|
+
(Oskar Eichler #361)
|
|
6
|
+
* Rewind `tempfile` if there are errors when appending in
|
|
7
|
+
`UploadedFile#append_to` (Oskar Eichler #363)
|
|
8
|
+
* Preserve request method and params for 308 redirects (requires
|
|
9
|
+
Rack 2+) (Oskar Eichler #364)
|
|
10
|
+
* Reuse `rack.input` in 307/308 redirect request if it is rewindable,
|
|
11
|
+
fixing cases that use application/json and other content types
|
|
12
|
+
(Oskar Eichler #367)
|
|
13
|
+
* Don't use query string parameters from original request as request
|
|
14
|
+
body parameters in 307/308 redirects (Jeremy Evans)
|
|
15
|
+
|
|
16
|
+
* Minor enhancements:
|
|
17
|
+
* Add `Rack::Test::Session#default_input_content_type` and
|
|
18
|
+
`#convert_input_hash_to_string` private methods to allow for
|
|
19
|
+
easier overriding of default behavior. (Haruki Kobayashi #359)
|
|
20
|
+
|
|
21
|
+
## 2.2.0 / 2024-12-23
|
|
22
|
+
|
|
23
|
+
* Bug fixes:
|
|
24
|
+
* `Rack::Test::Cookie` now parses cookie parameters using a
|
|
25
|
+
case-insensitive approach (Guillaume Malette #349)
|
|
26
|
+
|
|
27
|
+
* Minor enhancements:
|
|
28
|
+
* Arrays of cookies containing a blank cookie are now handled
|
|
29
|
+
correctly when processing responses. (Martin Emde #343)
|
|
30
|
+
* `Rack::Test::UploadedFile` no longer uses a finalizer for named
|
|
31
|
+
paths to close and unlink the created Tempfile. Tempfile itself
|
|
32
|
+
uses a finalizer to close and unlink itself, so there is no
|
|
33
|
+
reason for `Rack::Test::UploadedFile` to do so (Jeremy Evans #338)
|
|
34
|
+
|
|
1
35
|
## 2.1.0 / 2023-03-14
|
|
2
36
|
|
|
3
37
|
* Breaking changes:
|
data/MIT-LICENSE.txt
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Copyright (c) 2008-2009 Bryan Helmkamp, Engine Yard Inc.
|
|
2
|
-
Copyright (c) 2022 Jeremy Evans
|
|
2
|
+
Copyright (c) 2022-2026 Jeremy Evans and contributors
|
|
3
3
|
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
|
@@ -89,6 +89,22 @@ class TestApp < Test::Unit::TestCase
|
|
|
89
89
|
end
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
If your application does not automatically use the `Rack::Lint` middleware in test mode,
|
|
93
|
+
and you want to test that requests to and responses from your application are compliant
|
|
94
|
+
with the Rack specification, you should manually use the `Rack::Lint` middleware:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
class TestApp < Test::Unit::TestCase
|
|
98
|
+
include Rack::Test::Methods
|
|
99
|
+
|
|
100
|
+
APP = Rack::Lint.new(YOUR_APP)
|
|
101
|
+
|
|
102
|
+
def app
|
|
103
|
+
APP
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
92
108
|
## Install
|
|
93
109
|
|
|
94
110
|
To install the latest release as a gem:
|
data/lib/rack/test/cookie_jar.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Rack
|
|
|
12
12
|
|
|
13
13
|
# The name of the cookie, will be a string
|
|
14
14
|
attr_reader :name
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
# The value of the cookie, will be a string or nil if there is no value.
|
|
17
17
|
attr_reader :value
|
|
18
18
|
|
|
@@ -28,7 +28,7 @@ module Rack
|
|
|
28
28
|
@raw, options = raw.split(/[;,] */n, 2)
|
|
29
29
|
|
|
30
30
|
@name, @value = parse_query(@raw, ';').to_a.first
|
|
31
|
-
@options = parse_query(options, ';')
|
|
31
|
+
@options = Hash[parse_query(options, ';').map { |k, v| [k.downcase, v] }]
|
|
32
32
|
|
|
33
33
|
if domain = @options['domain']
|
|
34
34
|
@exact_domain_match = false
|
|
@@ -45,7 +45,7 @@ module Rack
|
|
|
45
45
|
@options['path'] ||= uri.path.sub(/\/[^\/]*\Z/, '')
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# Whether the given cookie can replace the current cookie in the cookie jar.
|
|
49
49
|
def replaces?(other)
|
|
50
50
|
[name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
|
|
51
51
|
end
|
|
@@ -69,7 +69,7 @@ module Rack
|
|
|
69
69
|
# Whether the cookie has the httponly flag, indicating it is not available via
|
|
70
70
|
# a javascript API.
|
|
71
71
|
def http_only?
|
|
72
|
-
@options.key?('
|
|
72
|
+
@options.key?('httponly')
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
# The explicit or implicit path for the cookie.
|
|
@@ -93,9 +93,8 @@ module Rack
|
|
|
93
93
|
|
|
94
94
|
uri.host = @default_host if uri.host.nil?
|
|
95
95
|
|
|
96
|
-
real_domain = domain =~ /^\./ ? domain[1..-1] : domain
|
|
97
96
|
!!((!secure? || (secure? && uri.scheme == 'https')) &&
|
|
98
|
-
uri.host =~ Regexp.new("#{'^' if @exact_domain_match}#{Regexp.escape(
|
|
97
|
+
uri.host =~ Regexp.new("#{'^' if @exact_domain_match}#{Regexp.escape(domain)}$", Regexp::IGNORECASE))
|
|
99
98
|
end
|
|
100
99
|
|
|
101
100
|
# Cookies that do not match the URI will not be sent in requests to the URI.
|
|
@@ -110,11 +109,13 @@ module Rack
|
|
|
110
109
|
|
|
111
110
|
# A hash of cookie options, including the cookie value, but excluding the cookie name.
|
|
112
111
|
def to_h
|
|
113
|
-
@options.merge(
|
|
112
|
+
hash = @options.merge(
|
|
114
113
|
'value' => @value,
|
|
115
114
|
'HttpOnly' => http_only?,
|
|
116
115
|
'secure' => secure?
|
|
117
116
|
)
|
|
117
|
+
hash.delete('httponly')
|
|
118
|
+
hash
|
|
118
119
|
end
|
|
119
120
|
alias to_hash to_h
|
|
120
121
|
|
|
@@ -183,12 +184,10 @@ module Rack
|
|
|
183
184
|
def merge(raw_cookies, uri = nil)
|
|
184
185
|
return unless raw_cookies
|
|
185
186
|
|
|
186
|
-
if raw_cookies.is_a? String
|
|
187
|
-
raw_cookies = raw_cookies.split("\n")
|
|
188
|
-
raw_cookies.reject!(&:empty?)
|
|
189
|
-
end
|
|
187
|
+
raw_cookies = raw_cookies.split("\n") if raw_cookies.is_a? String
|
|
190
188
|
|
|
191
189
|
raw_cookies.each do |raw_cookie|
|
|
190
|
+
next if raw_cookie.empty?
|
|
192
191
|
cookie = Cookie.new(raw_cookie, uri, @default_host)
|
|
193
192
|
self << cookie if cookie.valid?(uri)
|
|
194
193
|
end
|
|
@@ -50,6 +50,7 @@ module Rack
|
|
|
50
50
|
|
|
51
51
|
# Delegate all methods not handled to the tempfile.
|
|
52
52
|
def method_missing(method_name, *args, &block)
|
|
53
|
+
# TODO: After Ruby <2.7.3 support is dropped, switch to ...
|
|
53
54
|
tempfile.public_send(method_name, *args, &block)
|
|
54
55
|
end
|
|
55
56
|
|
|
@@ -60,30 +61,19 @@ module Rack
|
|
|
60
61
|
def append_to(buffer)
|
|
61
62
|
tempfile.rewind
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
begin
|
|
65
|
+
buf = String.new
|
|
66
|
+
buffer << tempfile.readpartial(65_536, buf) until tempfile.eof?
|
|
67
|
+
nil
|
|
68
|
+
ensure
|
|
69
|
+
tempfile.rewind
|
|
70
|
+
end
|
|
69
71
|
end
|
|
70
72
|
|
|
71
73
|
def respond_to_missing?(method_name, include_private = false) #:nodoc:
|
|
72
74
|
tempfile.respond_to?(method_name, include_private) || super
|
|
73
75
|
end
|
|
74
76
|
|
|
75
|
-
# A proc that can be used as a finalizer to close and unlink the tempfile.
|
|
76
|
-
def self.finalize(file)
|
|
77
|
-
proc { actually_finalize file }
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Close and unlink the given file, used as a finalizer for the tempfile,
|
|
81
|
-
# if the tempfile is backed by a file in the filesystem.
|
|
82
|
-
def self.actually_finalize(file)
|
|
83
|
-
file.close
|
|
84
|
-
file.unlink
|
|
85
|
-
end
|
|
86
|
-
|
|
87
77
|
private
|
|
88
78
|
|
|
89
79
|
# Use the StringIO as the tempfile.
|
|
@@ -104,8 +94,6 @@ module Rack
|
|
|
104
94
|
@tempfile = Tempfile.new([::File.basename(@original_filename, extension), extension])
|
|
105
95
|
@tempfile.set_encoding(Encoding::BINARY)
|
|
106
96
|
|
|
107
|
-
ObjectSpace.define_finalizer(self, self.class.finalize(@tempfile))
|
|
108
|
-
|
|
109
97
|
FileUtils.copy_file(path, @tempfile.path)
|
|
110
98
|
end
|
|
111
99
|
end
|
data/lib/rack/test/utils.rb
CHANGED
|
@@ -6,9 +6,16 @@ module Rack
|
|
|
6
6
|
include Rack::Utils
|
|
7
7
|
extend self
|
|
8
8
|
|
|
9
|
+
class << self
|
|
10
|
+
attr_accessor :override_build_nested_query
|
|
11
|
+
end
|
|
12
|
+
self.override_build_nested_query = false
|
|
13
|
+
|
|
9
14
|
# Build a query string for the given value and prefix. The value
|
|
10
15
|
# can be an array or hash of parameters.
|
|
11
16
|
def build_nested_query(value, prefix = nil)
|
|
17
|
+
return super if Rack::Test::Utils.override_build_nested_query
|
|
18
|
+
|
|
12
19
|
case value
|
|
13
20
|
when Array
|
|
14
21
|
if value.empty?
|
|
@@ -67,16 +74,10 @@ module Rack
|
|
|
67
74
|
|
|
68
75
|
case value
|
|
69
76
|
when Array
|
|
70
|
-
value.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
nested_params[subkey] = subvalue
|
|
75
|
-
end
|
|
76
|
-
(flattened_params["#{k}[]"] ||= []) << nested_params
|
|
77
|
-
else
|
|
78
|
-
flattened_params["#{k}[]"] = value
|
|
79
|
-
end
|
|
77
|
+
next if value.empty?
|
|
78
|
+
|
|
79
|
+
flattened_params["#{k}[]"] = value.map do |v|
|
|
80
|
+
v.is_a?(Hash) ? normalize_multipart_params(v) : v
|
|
80
81
|
end
|
|
81
82
|
when Hash
|
|
82
83
|
normalize_multipart_params(value).each do |subkey, subvalue|
|
|
@@ -98,15 +99,19 @@ module Rack
|
|
|
98
99
|
|
|
99
100
|
# Append each multipart parameter value to the buffer.
|
|
100
101
|
def _build_parts(buffer, parameters)
|
|
101
|
-
parameters.
|
|
102
|
-
if name =~ /\[\]\
|
|
103
|
-
value.each do |
|
|
102
|
+
parameters.each do |name, value|
|
|
103
|
+
if name =~ /\[\]\z/ && value.is_a?(Array) && value.any? { |v| v.is_a?(Hash) }
|
|
104
|
+
value.each do |item|
|
|
104
105
|
new_value = {}
|
|
105
|
-
|
|
106
|
+
if item.is_a?(Hash)
|
|
107
|
+
item.each { |k, v| new_value[name + k] = v }
|
|
108
|
+
else
|
|
109
|
+
new_value[name] = item
|
|
110
|
+
end
|
|
106
111
|
_build_parts(buffer, new_value)
|
|
107
112
|
end
|
|
108
113
|
else
|
|
109
|
-
[value].flatten.
|
|
114
|
+
[value].flatten.each do |v|
|
|
110
115
|
if v.respond_to?(:original_filename)
|
|
111
116
|
build_file_part(buffer, name, v)
|
|
112
117
|
else
|
data/lib/rack/test/version.rb
CHANGED
data/lib/rack/test.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'uri'
|
|
4
4
|
|
|
5
|
-
# :
|
|
5
|
+
# simplecov:disable
|
|
6
6
|
begin
|
|
7
7
|
require "rack/version"
|
|
8
8
|
rescue LoadError
|
|
@@ -16,7 +16,7 @@ else
|
|
|
16
16
|
require "rack"
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
|
-
# :
|
|
19
|
+
# simplecov:enable
|
|
20
20
|
|
|
21
21
|
require 'forwardable'
|
|
22
22
|
|
|
@@ -211,12 +211,29 @@ module Rack
|
|
|
211
211
|
raise Error, 'Last response was not a redirect. Cannot follow_redirect!'
|
|
212
212
|
end
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
env = {
|
|
215
|
+
'HTTP_REFERER' => last_request.url,
|
|
216
|
+
'rack.session' => last_request.session,
|
|
217
|
+
'rack.session.options' => last_request.session_options
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
params = {}
|
|
221
|
+
case last_response.status
|
|
222
|
+
when 307, 308
|
|
215
223
|
request_method = last_request.request_method
|
|
216
|
-
|
|
224
|
+
if (input = last_request.body)
|
|
225
|
+
if input.respond_to?(:rewind)
|
|
226
|
+
input.rewind
|
|
227
|
+
env[:input] = input
|
|
228
|
+
%w[CONTENT_TYPE CONTENT_LENGTH].each do |key|
|
|
229
|
+
env[key] = last_request.env[key] if last_request.env.key?(key)
|
|
230
|
+
end
|
|
231
|
+
else
|
|
232
|
+
params = last_request.POST
|
|
233
|
+
end
|
|
234
|
+
end
|
|
217
235
|
else
|
|
218
236
|
request_method = 'GET'
|
|
219
|
-
params = {}
|
|
220
237
|
end
|
|
221
238
|
|
|
222
239
|
# Compute the next location by appending the location header with the
|
|
@@ -228,9 +245,7 @@ module Rack
|
|
|
228
245
|
request_method,
|
|
229
246
|
next_location.to_s,
|
|
230
247
|
params,
|
|
231
|
-
|
|
232
|
-
'rack.session' => last_request.session,
|
|
233
|
-
'rack.session.options' => last_request.session_options
|
|
248
|
+
env
|
|
234
249
|
)
|
|
235
250
|
end
|
|
236
251
|
|
|
@@ -255,12 +270,12 @@ module Rack
|
|
|
255
270
|
|
|
256
271
|
private
|
|
257
272
|
|
|
258
|
-
# :
|
|
273
|
+
# simplecov:disable
|
|
259
274
|
if !defined?(Rack::RELEASE) || Gem::Version.new(Rack::RELEASE) < Gem::Version.new('2.2.2')
|
|
260
275
|
def close_body(body)
|
|
261
276
|
body.close if body.respond_to?(:close)
|
|
262
277
|
end
|
|
263
|
-
# :
|
|
278
|
+
# simplecov:enable
|
|
264
279
|
else
|
|
265
280
|
# close() gets called automatically in newer Rack versions.
|
|
266
281
|
def close_body(body)
|
|
@@ -281,11 +296,11 @@ module Rack
|
|
|
281
296
|
'REMOTE_ADDR' => '127.0.0.1',
|
|
282
297
|
'SERVER_PROTOCOL' => 'HTTP/1.0',
|
|
283
298
|
}
|
|
284
|
-
# :
|
|
299
|
+
# simplecov:disable
|
|
285
300
|
unless Rack.release >= '2.3'
|
|
286
301
|
DEFAULT_ENV['HTTP_VERSION'] = DEFAULT_ENV['SERVER_PROTOCOL']
|
|
287
302
|
end
|
|
288
|
-
# :
|
|
303
|
+
# simplecov:enable
|
|
289
304
|
DEFAULT_ENV.freeze
|
|
290
305
|
private_constant :DEFAULT_ENV
|
|
291
306
|
|
|
@@ -307,7 +322,7 @@ module Rack
|
|
|
307
322
|
append_query_params(query_array, params)
|
|
308
323
|
end
|
|
309
324
|
elsif !env.key?(:input)
|
|
310
|
-
env['CONTENT_TYPE'] ||=
|
|
325
|
+
env['CONTENT_TYPE'] ||= default_input_content_type
|
|
311
326
|
params ||= {}
|
|
312
327
|
multipart = env.has_key?(:multipart) ? env.delete(:multipart) : env['CONTENT_TYPE'].start_with?('multipart/')
|
|
313
328
|
|
|
@@ -317,7 +332,7 @@ module Rack
|
|
|
317
332
|
env['CONTENT_LENGTH'] ||= data.length.to_s
|
|
318
333
|
env['CONTENT_TYPE'] = "#{multipart_content_type(env)}; boundary=#{MULTIPART_BOUNDARY}"
|
|
319
334
|
else
|
|
320
|
-
env[:input] =
|
|
335
|
+
env[:input] = convert_input_hash_to_string(params)
|
|
321
336
|
end
|
|
322
337
|
else
|
|
323
338
|
env[:input] = params
|
|
@@ -369,6 +384,16 @@ module Rack
|
|
|
369
384
|
|
|
370
385
|
@last_response
|
|
371
386
|
end
|
|
387
|
+
|
|
388
|
+
# Return the default content type to use when none is given.
|
|
389
|
+
def default_input_content_type
|
|
390
|
+
'application/x-www-form-urlencoded'
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Convert the given params hash into a request body string.
|
|
394
|
+
def convert_input_hash_to_string(value, prefix = nil)
|
|
395
|
+
build_nested_query(value, prefix)
|
|
396
|
+
end
|
|
372
397
|
end
|
|
373
398
|
|
|
374
399
|
# Whether the version of rack in use handles encodings.
|
metadata
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rack-test
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Evans
|
|
8
8
|
- Bryan Helmkamp
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
|
15
14
|
name: rack
|
|
@@ -95,7 +94,6 @@ metadata:
|
|
|
95
94
|
bug_tracker_uri: https://github.com/rack/rack-test/issues
|
|
96
95
|
mailing_list_uri: https://github.com/rack/rack-test/discussions
|
|
97
96
|
changelog_uri: https://github.com/rack/rack-test/blob/main/History.md
|
|
98
|
-
post_install_message:
|
|
99
97
|
rdoc_options: []
|
|
100
98
|
require_paths:
|
|
101
99
|
- lib
|
|
@@ -110,8 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
110
108
|
- !ruby/object:Gem::Version
|
|
111
109
|
version: '0'
|
|
112
110
|
requirements: []
|
|
113
|
-
rubygems_version:
|
|
114
|
-
signing_key:
|
|
111
|
+
rubygems_version: 4.0.20
|
|
115
112
|
specification_version: 4
|
|
116
113
|
summary: Simple testing API built on Rack
|
|
117
114
|
test_files: []
|