rack-test 1.1.0 → 2.0.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 +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