rack-test 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +82 -1
- data/MIT-LICENSE.txt +1 -0
- data/README.md +52 -63
- data/lib/rack/mock_session.rb +2 -63
- data/lib/rack/test/cookie_jar.rb +101 -49
- data/lib/rack/test/methods.rb +57 -45
- data/lib/rack/test/mock_digest_request.rb +11 -1
- data/lib/rack/test/uploaded_file.rb +35 -11
- data/lib/rack/test/utils.rb +75 -61
- data/lib/rack/test/version.rb +1 -1
- data/lib/rack/test.rb +223 -143
- metadata +27 -96
data/lib/rack/test/methods.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'forwardable'
|
2
4
|
|
3
5
|
module Rack
|
@@ -5,7 +7,10 @@ module Rack
|
|
5
7
|
# This module serves as the primary integration point for using Rack::Test
|
6
8
|
# in a testing environment. It depends on an app method being defined in the
|
7
9
|
# same context, and provides the Rack::Test API methods (see Rack::Test::Session
|
8
|
-
# for their documentation).
|
10
|
+
# for their documentation). It defines the following methods that are delegated
|
11
|
+
# to the current session: :request, :get, :post, :put, :patch, :delete, :options,
|
12
|
+
# :head, :custom_request, :follow_redirect!, :header, :env, :set_cookie,
|
13
|
+
# :clear_cookies, :authorize, :basic_authorize, :last_response, and :last_request.
|
9
14
|
#
|
10
15
|
# Example:
|
11
16
|
#
|
@@ -13,23 +18,14 @@ module Rack
|
|
13
18
|
# include Rack::Test::Methods
|
14
19
|
#
|
15
20
|
# def app
|
16
|
-
# MyApp
|
21
|
+
# MyApp
|
17
22
|
# end
|
18
23
|
# end
|
19
24
|
module Methods
|
20
25
|
extend Forwardable
|
21
26
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@_rack_mock_sessions ||= {}
|
26
|
-
@_rack_mock_sessions[name] ||= build_rack_mock_session
|
27
|
-
end
|
28
|
-
|
29
|
-
def build_rack_mock_session # :nodoc:
|
30
|
-
Rack::MockSession.new(app)
|
31
|
-
end
|
32
|
-
|
27
|
+
# Return the existing session with the given name, or a new
|
28
|
+
# rack session. Always use a new session if name is nil.
|
33
29
|
def rack_test_session(name = :default) # :nodoc:
|
34
30
|
return build_rack_test_session(name) unless name
|
35
31
|
|
@@ -37,47 +33,63 @@ module Rack
|
|
37
33
|
@_rack_test_sessions[name] ||= build_rack_test_session(name)
|
38
34
|
end
|
39
35
|
|
40
|
-
|
41
|
-
|
36
|
+
# For backwards compatibility with older rack-test versions.
|
37
|
+
alias rack_mock_session rack_test_session # :nodoc:
|
38
|
+
|
39
|
+
# Create a new Rack::Test::Session for #app.
|
40
|
+
def build_rack_test_session(_name) # :nodoc:
|
41
|
+
if respond_to?(:build_rack_mock_session, true)
|
42
|
+
# Backwards compatibility for capybara
|
43
|
+
build_rack_mock_session
|
44
|
+
else
|
45
|
+
Session.new(app)
|
46
|
+
end
|
42
47
|
end
|
43
48
|
|
44
|
-
|
45
|
-
|
49
|
+
# Return the currently actively session. This is the session to
|
50
|
+
# which the delegated methods are sent.
|
51
|
+
def current_session
|
52
|
+
@_rack_test_current_session ||= rack_test_session
|
46
53
|
end
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
55
|
+
# Create a new session (or reuse an existing session with the given name),
|
56
|
+
# and make it the current session for the given block.
|
57
|
+
def with_session(name)
|
58
|
+
session = _rack_test_current_session
|
59
|
+
yield(@_rack_test_current_session = rack_test_session(name))
|
60
|
+
ensure
|
61
|
+
@_rack_test_current_session = session
|
52
62
|
end
|
53
63
|
|
54
|
-
def
|
55
|
-
|
64
|
+
def digest_authorize(username, password) # :nodoc:
|
65
|
+
warn 'digest authentication support will be removed in rack-test 2.1', uplevel: 1
|
66
|
+
current_session._digest_authorize(username, password)
|
56
67
|
end
|
57
68
|
|
58
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
].freeze
|
69
|
+
def_delegators(:current_session,
|
70
|
+
:request,
|
71
|
+
:get,
|
72
|
+
:post,
|
73
|
+
:put,
|
74
|
+
:patch,
|
75
|
+
:delete,
|
76
|
+
:options,
|
77
|
+
:head,
|
78
|
+
:custom_request,
|
79
|
+
:follow_redirect!,
|
80
|
+
:header,
|
81
|
+
:env,
|
82
|
+
:set_cookie,
|
83
|
+
:clear_cookies,
|
84
|
+
:authorize,
|
85
|
+
:basic_authorize,
|
86
|
+
:last_response,
|
87
|
+
:last_request,
|
88
|
+
)
|
79
89
|
|
80
|
-
|
90
|
+
# Private accessor to avoid uninitialized instance variable warning in Ruby 2.*
|
91
|
+
attr_accessor :_rack_test_current_session
|
92
|
+
private :_rack_test_current_session
|
81
93
|
end
|
82
94
|
end
|
83
95
|
end
|
@@ -1,6 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :nocov:
|
4
|
+
require 'rack/auth/digest' unless defined?(Rack::Auth::Digest)
|
5
|
+
# :nocov:
|
6
|
+
|
1
7
|
module Rack
|
2
8
|
module Test
|
3
|
-
class
|
9
|
+
class MockDigestRequest_ # :nodoc:
|
4
10
|
def initialize(params)
|
5
11
|
@params = params
|
6
12
|
end
|
@@ -21,5 +27,9 @@ module Rack
|
|
21
27
|
Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
|
22
28
|
end
|
23
29
|
end
|
30
|
+
MockDigestRequest = MockDigestRequest_
|
31
|
+
# :nocov:
|
32
|
+
deprecate_constant :MockDigestRequest if respond_to?(:deprecate_constant, true)
|
33
|
+
# :nocov:
|
24
34
|
end
|
25
35
|
end
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fileutils'
|
2
|
-
require 'pathname'
|
3
4
|
require 'tempfile'
|
5
|
+
require 'stringio'
|
4
6
|
|
5
7
|
module Rack
|
6
8
|
module Test
|
@@ -21,40 +23,60 @@ module Rack
|
|
21
23
|
|
22
24
|
# Creates a new UploadedFile instance.
|
23
25
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# object. Not used for other kind of `content` objects.
|
26
|
+
# Arguments:
|
27
|
+
# content :: is a path to a file, or an {IO} or {StringIO} object representing the content.
|
28
|
+
# content_type :: MIME type of the file
|
29
|
+
# binary :: Whether the file should be set to binmode (content treated as binary).
|
30
|
+
# original_filename :: The filename to use for the file if +content+ is a StringIO.
|
30
31
|
def initialize(content, content_type = 'text/plain', binary = false, original_filename: nil)
|
31
|
-
|
32
|
+
case content
|
33
|
+
when StringIO
|
32
34
|
initialize_from_stringio(content, original_filename)
|
33
35
|
else
|
34
36
|
initialize_from_file_path(content)
|
35
37
|
end
|
38
|
+
|
36
39
|
@content_type = content_type
|
37
40
|
@tempfile.binmode if binary
|
38
41
|
end
|
39
42
|
|
43
|
+
# The path to the tempfile. Will not work if the receiver's content is from a StringIO.
|
40
44
|
def path
|
41
45
|
tempfile.path
|
42
46
|
end
|
43
|
-
|
44
47
|
alias local_path path
|
45
48
|
|
46
|
-
|
49
|
+
# Delegate all methods not handled to the tempfile.
|
50
|
+
def method_missing(method_name, *args, &block)
|
47
51
|
tempfile.public_send(method_name, *args, &block)
|
48
52
|
end
|
49
53
|
|
54
|
+
# Append to given buffer in 64K chunks to avoid multiple large
|
55
|
+
# copies of file data in memory. Rewind tempfile before and
|
56
|
+
# after to make sure all data in tempfile is appended to the
|
57
|
+
# buffer.
|
58
|
+
def append_to(buffer)
|
59
|
+
tempfile.rewind
|
60
|
+
|
61
|
+
buf = String.new
|
62
|
+
buffer << tempfile.readpartial(65_536, buf) until tempfile.eof?
|
63
|
+
|
64
|
+
tempfile.rewind
|
65
|
+
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
50
69
|
def respond_to_missing?(method_name, include_private = false) #:nodoc:
|
51
70
|
tempfile.respond_to?(method_name, include_private) || super
|
52
71
|
end
|
53
72
|
|
73
|
+
# A proc that can be used as a finalizer to close and unlink the tempfile.
|
54
74
|
def self.finalize(file)
|
55
75
|
proc { actually_finalize file }
|
56
76
|
end
|
57
77
|
|
78
|
+
# Close and unlink the given file, used as a finalizer for the tempfile,
|
79
|
+
# if the tempfile is backed by a file in the filesystem.
|
58
80
|
def self.actually_finalize(file)
|
59
81
|
file.close
|
60
82
|
file.unlink
|
@@ -62,11 +84,13 @@ module Rack
|
|
62
84
|
|
63
85
|
private
|
64
86
|
|
87
|
+
# Use the StringIO as the tempfile.
|
65
88
|
def initialize_from_stringio(stringio, original_filename)
|
66
89
|
@tempfile = stringio
|
67
90
|
@original_filename = original_filename || raise(ArgumentError, 'Missing `original_filename` for StringIO object')
|
68
91
|
end
|
69
92
|
|
93
|
+
# Create a tempfile and copy the content from the given path into the tempfile.
|
70
94
|
def initialize_from_file_path(path)
|
71
95
|
raise "#{path} file does not exist" unless ::File.exist?(path)
|
72
96
|
|
@@ -74,7 +98,7 @@ module Rack
|
|
74
98
|
extension = ::File.extname(@original_filename)
|
75
99
|
|
76
100
|
@tempfile = Tempfile.new([::File.basename(@original_filename, extension), extension])
|
77
|
-
@tempfile.set_encoding(Encoding::BINARY)
|
101
|
+
@tempfile.set_encoding(Encoding::BINARY)
|
78
102
|
|
79
103
|
ObjectSpace.define_finalizer(self, self.class.finalize(@tempfile))
|
80
104
|
|
data/lib/rack/test/utils.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
module Test
|
3
5
|
module Utils # :nodoc:
|
4
6
|
include Rack::Utils
|
5
|
-
extend
|
7
|
+
extend self
|
6
8
|
|
9
|
+
# Build a query string for the given value and prefix. The value
|
10
|
+
# can be an array or hash of parameters.
|
7
11
|
def build_nested_query(value, prefix = nil)
|
8
12
|
case value
|
9
13
|
when Array
|
10
14
|
if value.empty?
|
11
15
|
"#{prefix}[]="
|
12
16
|
else
|
17
|
+
prefix += "[]" unless unescape(prefix).end_with?('[]')
|
13
18
|
value.map do |v|
|
14
|
-
prefix = "#{prefix}[]" unless unescape(prefix) =~ /\[\]$/
|
15
19
|
build_nested_query(v, prefix.to_s)
|
16
20
|
end.join('&')
|
17
21
|
end
|
@@ -25,12 +29,12 @@ module Rack
|
|
25
29
|
"#{prefix}=#{escape(value)}"
|
26
30
|
end
|
27
31
|
end
|
28
|
-
module_function :build_nested_query
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
+
# Build a multipart body for the given params.
|
34
|
+
def build_multipart(params, _first = true, multipart = false)
|
35
|
+
raise ArgumentError, 'value must be a Hash' unless params.is_a?(Hash)
|
33
36
|
|
37
|
+
unless multipart
|
34
38
|
query = lambda { |value|
|
35
39
|
case value
|
36
40
|
when Array
|
@@ -45,6 +49,20 @@ module Rack
|
|
45
49
|
return nil unless multipart
|
46
50
|
end
|
47
51
|
|
52
|
+
params = normalize_multipart_params(params, true)
|
53
|
+
|
54
|
+
buffer = String.new
|
55
|
+
build_parts(buffer, params)
|
56
|
+
# :nocov:
|
57
|
+
buffer.force_encoding(Encoding::BINARY) if Rack::Test.encoding_aware_strings?
|
58
|
+
# :nocov:
|
59
|
+
buffer
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Return a flattened hash of parameter values based on the given params.
|
65
|
+
def normalize_multipart_params(params, first=false)
|
48
66
|
flattened_params = {}
|
49
67
|
|
50
68
|
params.each do |key, value|
|
@@ -55,17 +73,16 @@ module Rack
|
|
55
73
|
value.map do |v|
|
56
74
|
if v.is_a?(Hash)
|
57
75
|
nested_params = {}
|
58
|
-
|
76
|
+
normalize_multipart_params(v).each do |subkey, subvalue|
|
59
77
|
nested_params[subkey] = subvalue
|
60
78
|
end
|
61
|
-
flattened_params["#{k}[]"] ||= []
|
62
|
-
flattened_params["#{k}[]"] << nested_params
|
79
|
+
(flattened_params["#{k}[]"] ||= []) << nested_params
|
63
80
|
else
|
64
81
|
flattened_params["#{k}[]"] = value
|
65
82
|
end
|
66
83
|
end
|
67
84
|
when Hash
|
68
|
-
|
85
|
+
normalize_multipart_params(value).each do |subkey, subvalue|
|
69
86
|
flattened_params[k + subkey] = subvalue
|
70
87
|
end
|
71
88
|
else
|
@@ -73,72 +90,69 @@ module Rack
|
|
73
90
|
end
|
74
91
|
end
|
75
92
|
|
76
|
-
|
77
|
-
build_parts(flattened_params)
|
78
|
-
else
|
79
|
-
flattened_params
|
80
|
-
end
|
93
|
+
flattened_params
|
81
94
|
end
|
82
|
-
module_function :build_multipart
|
83
|
-
|
84
|
-
private
|
85
95
|
|
86
|
-
|
87
|
-
|
96
|
+
# Build the multipart content for uploading.
|
97
|
+
def build_parts(buffer, parameters)
|
98
|
+
_build_parts(buffer, parameters)
|
99
|
+
buffer << END_BOUNDARY
|
88
100
|
end
|
89
|
-
module_function :build_parts
|
90
101
|
|
91
|
-
|
102
|
+
# Append each multipart parameter value to the buffer.
|
103
|
+
def _build_parts(buffer, parameters)
|
92
104
|
parameters.map do |name, value|
|
93
105
|
if name =~ /\[\]\Z/ && value.is_a?(Array) && value.all? { |v| v.is_a?(Hash) }
|
94
|
-
value.
|
106
|
+
value.each do |hash|
|
95
107
|
new_value = {}
|
96
108
|
hash.each { |k, v| new_value[name + k] = v }
|
97
|
-
|
98
|
-
end
|
109
|
+
_build_parts(buffer, new_value)
|
110
|
+
end
|
99
111
|
else
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end.join
|
107
|
-
|
108
|
-
else
|
109
|
-
primitive_part = build_primitive_part(name, value)
|
110
|
-
Rack::Test.encoding_aware_strings? ? primitive_part.force_encoding('BINARY') : primitive_part
|
112
|
+
[value].flatten.map do |v|
|
113
|
+
if v.respond_to?(:original_filename)
|
114
|
+
build_file_part(buffer, name, v)
|
115
|
+
else
|
116
|
+
build_primitive_part(buffer, name, v)
|
117
|
+
end
|
111
118
|
end
|
112
119
|
end
|
113
120
|
end
|
114
121
|
end
|
115
|
-
|
116
|
-
|
117
|
-
def build_primitive_part(parameter_name, value)
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
EOF
|
126
|
-
end.join
|
122
|
+
|
123
|
+
# Append the multipart fragment for a parameter that isn't a file upload to the buffer.
|
124
|
+
def build_primitive_part(buffer, parameter_name, value)
|
125
|
+
buffer <<
|
126
|
+
START_BOUNDARY <<
|
127
|
+
"content-disposition: form-data; name=\"" <<
|
128
|
+
parameter_name.to_s <<
|
129
|
+
"\"\r\n\r\n" <<
|
130
|
+
value.to_s <<
|
131
|
+
"\r\n"
|
127
132
|
end
|
128
|
-
|
129
|
-
|
130
|
-
def build_file_part(parameter_name, uploaded_file)
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
\r
|
138
|
-
|
139
|
-
|
133
|
+
|
134
|
+
# Append the multipart fragment for a parameter that is a file upload to the buffer.
|
135
|
+
def build_file_part(buffer, parameter_name, uploaded_file)
|
136
|
+
buffer <<
|
137
|
+
START_BOUNDARY <<
|
138
|
+
"content-disposition: form-data; name=\"" <<
|
139
|
+
parameter_name.to_s <<
|
140
|
+
"\"; filename=\"" <<
|
141
|
+
escape_path(uploaded_file.original_filename) <<
|
142
|
+
"\"\r\ncontent-type: " <<
|
143
|
+
uploaded_file.content_type.to_s <<
|
144
|
+
"\r\ncontent-length: " <<
|
145
|
+
uploaded_file.size.to_s <<
|
146
|
+
"\r\n\r\n"
|
147
|
+
|
148
|
+
# Handle old versions of Capybara::RackTest::Form::NilUploadedFile
|
149
|
+
if uploaded_file.respond_to?(:set_encoding)
|
150
|
+
uploaded_file.set_encoding(Encoding::BINARY)
|
151
|
+
uploaded_file.append_to(buffer)
|
152
|
+
end
|
153
|
+
|
154
|
+
buffer << "\r\n"
|
140
155
|
end
|
141
|
-
module_function :build_file_part
|
142
156
|
end
|
143
157
|
end
|
144
158
|
end
|
data/lib/rack/test/version.rb
CHANGED