webmock 3.0.0 → 3.14.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 +5 -5
- data/.github/workflows/CI.yml +37 -0
- data/CHANGELOG.md +416 -0
- data/Gemfile +1 -1
- data/README.md +157 -31
- data/Rakefile +12 -4
- data/lib/webmock/api.rb +12 -0
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +17 -3
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -2
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +4 -1
- data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +24 -3
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +6 -2
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +2 -2
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +28 -9
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
- data/lib/webmock/http_lib_adapters/net_http.rb +54 -14
- data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
- data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -4
- data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
- data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
- data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
- data/lib/webmock/matchers/hash_including_matcher.rb +4 -23
- data/lib/webmock/rack_response.rb +1 -1
- data/lib/webmock/request_body_diff.rb +1 -1
- data/lib/webmock/request_execution_verifier.rb +2 -3
- data/lib/webmock/request_pattern.rb +108 -46
- data/lib/webmock/request_registry.rb +1 -1
- data/lib/webmock/request_signature.rb +1 -1
- data/lib/webmock/request_signature_snippet.rb +4 -4
- data/lib/webmock/response.rb +11 -5
- data/lib/webmock/rspec.rb +10 -3
- data/lib/webmock/stub_registry.rb +26 -11
- data/lib/webmock/stub_request_snippet.rb +10 -6
- data/lib/webmock/test_unit.rb +1 -3
- data/lib/webmock/util/hash_counter.rb +4 -4
- data/lib/webmock/util/headers.rb +17 -2
- data/lib/webmock/util/json.rb +1 -2
- data/lib/webmock/util/query_mapper.rb +9 -7
- data/lib/webmock/util/uri.rb +10 -10
- data/lib/webmock/util/values_stringifier.rb +20 -0
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +10 -3
- data/lib/webmock.rb +53 -48
- data/minitest/webmock_spec.rb +2 -2
- data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
- data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
- data/spec/acceptance/curb/curb_spec.rb +33 -0
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +56 -0
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
- data/spec/acceptance/excon/excon_spec.rb +4 -2
- data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
- data/spec/acceptance/http_rb/http_rb_spec.rb +20 -0
- data/spec/acceptance/http_rb/http_rb_spec_helper.rb +5 -2
- data/spec/acceptance/httpclient/httpclient_spec.rb +8 -1
- data/spec/acceptance/manticore/manticore_spec.rb +51 -0
- data/spec/acceptance/net_http/net_http_shared.rb +1 -1
- data/spec/acceptance/net_http/net_http_spec.rb +53 -1
- data/spec/acceptance/patron/patron_spec.rb +7 -0
- data/spec/acceptance/patron/patron_spec_helper.rb +3 -3
- data/spec/acceptance/shared/callbacks.rb +3 -2
- data/spec/acceptance/shared/request_expectations.rb +14 -0
- data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
- data/spec/acceptance/shared/stubbing_requests.rb +95 -0
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +1 -1
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
- data/spec/support/webmock_server.rb +1 -0
- data/spec/unit/api_spec.rb +103 -3
- data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
- data/spec/unit/request_execution_verifier_spec.rb +12 -12
- data/spec/unit/request_pattern_spec.rb +195 -49
- data/spec/unit/request_signature_snippet_spec.rb +2 -2
- data/spec/unit/response_spec.rb +22 -18
- data/spec/unit/stub_request_snippet_spec.rb +30 -10
- data/spec/unit/util/query_mapper_spec.rb +13 -0
- data/spec/unit/util/uri_spec.rb +74 -2
- data/spec/unit/webmock_spec.rb +54 -5
- data/test/shared_test.rb +15 -2
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +11 -3
- metadata +66 -17
- data/.travis.yml +0 -20
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
module HTTP
|
|
2
2
|
class Request
|
|
3
3
|
def webmock_signature
|
|
4
|
+
request_body = if defined?(HTTP::Request::Body)
|
|
5
|
+
''.tap { |string| body.each { |part| string << part } }
|
|
6
|
+
else
|
|
7
|
+
body
|
|
8
|
+
end
|
|
9
|
+
|
|
4
10
|
::WebMock::RequestSignature.new(verb, uri.to_s, {
|
|
5
11
|
headers: headers.to_h,
|
|
6
|
-
body:
|
|
12
|
+
body: request_body
|
|
7
13
|
})
|
|
8
14
|
end
|
|
9
15
|
end
|
|
@@ -11,20 +11,41 @@ module HTTP
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
class << self
|
|
14
|
-
def from_webmock(webmock_response, request_signature = nil)
|
|
14
|
+
def from_webmock(request, webmock_response, request_signature = nil)
|
|
15
15
|
status = Status.new(webmock_response.status.first)
|
|
16
16
|
headers = webmock_response.headers || {}
|
|
17
|
-
body = Body.new Streamer.new webmock_response.body
|
|
18
17
|
uri = normalize_uri(request_signature && request_signature.uri)
|
|
19
18
|
|
|
19
|
+
# HTTP.rb 3.0+ uses a keyword argument to pass the encoding, but 1.x
|
|
20
|
+
# and 2.x use a positional argument, and 0.x don't support supplying
|
|
21
|
+
# the encoding.
|
|
22
|
+
body = if HTTP::VERSION < "1.0.0"
|
|
23
|
+
Body.new(Streamer.new(webmock_response.body))
|
|
24
|
+
elsif HTTP::VERSION < "3.0.0"
|
|
25
|
+
Body.new(Streamer.new(webmock_response.body), webmock_response.body.encoding)
|
|
26
|
+
else
|
|
27
|
+
Body.new(Streamer.new(webmock_response.body), encoding: webmock_response.body.encoding)
|
|
28
|
+
end
|
|
29
|
+
|
|
20
30
|
return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0"
|
|
21
31
|
|
|
32
|
+
# 5.0.0 had a breaking change to require request instead of uri.
|
|
33
|
+
if HTTP::VERSION < '5.0.0'
|
|
34
|
+
return new({
|
|
35
|
+
status: status,
|
|
36
|
+
version: "1.1",
|
|
37
|
+
headers: headers,
|
|
38
|
+
body: body,
|
|
39
|
+
uri: uri
|
|
40
|
+
})
|
|
41
|
+
end
|
|
42
|
+
|
|
22
43
|
new({
|
|
23
44
|
status: status,
|
|
24
45
|
version: "1.1",
|
|
25
46
|
headers: headers,
|
|
26
47
|
body: body,
|
|
27
|
-
|
|
48
|
+
request: request,
|
|
28
49
|
})
|
|
29
50
|
end
|
|
30
51
|
|
|
@@ -5,7 +5,7 @@ module HTTP
|
|
|
5
5
|
@io = StringIO.new str
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def readpartial(size = nil)
|
|
8
|
+
def readpartial(size = nil, outbuf = nil)
|
|
9
9
|
unless size
|
|
10
10
|
if defined?(HTTP::Client::BUFFER_SIZE)
|
|
11
11
|
size = HTTP::Client::BUFFER_SIZE
|
|
@@ -14,7 +14,11 @@ module HTTP
|
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
@io.read size
|
|
17
|
+
@io.read size, outbuf
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def close
|
|
21
|
+
@io.close
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
def sequence_id
|
|
@@ -38,12 +38,12 @@ module HTTP
|
|
|
38
38
|
webmock_response.raise_error_if_any
|
|
39
39
|
|
|
40
40
|
invoke_callbacks(webmock_response, real_request: false)
|
|
41
|
-
::HTTP::Response.from_webmock webmock_response, request_signature
|
|
41
|
+
::HTTP::Response.from_webmock @request, webmock_response, request_signature
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def raise_timeout_error
|
|
45
45
|
raise Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0"
|
|
46
|
-
raise HTTP::
|
|
46
|
+
raise HTTP::TimeoutError, "connection error: #{Errno::ETIMEDOUT.new}"
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def perform
|
|
@@ -43,6 +43,9 @@ if defined?(::HTTPClient)
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
module WebMockHTTPClients
|
|
46
|
+
|
|
47
|
+
REQUEST_RESPONSE_LOCK = Mutex.new
|
|
48
|
+
|
|
46
49
|
def do_get_block(req, proxy, conn, &block)
|
|
47
50
|
do_get(req, proxy, conn, false, &block)
|
|
48
51
|
end
|
|
@@ -57,7 +60,7 @@ if defined?(::HTTPClient)
|
|
|
57
60
|
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
|
58
61
|
|
|
59
62
|
if webmock_responses[request_signature]
|
|
60
|
-
webmock_response = webmock_responses.delete(request_signature)
|
|
63
|
+
webmock_response = synchronize_request_response { webmock_responses.delete(request_signature) }
|
|
61
64
|
response = build_httpclient_response(webmock_response, stream, req.header, &block)
|
|
62
65
|
@request_filter.each do |filter|
|
|
63
66
|
filter.filter_response(req, response)
|
|
@@ -68,15 +71,17 @@ if defined?(::HTTPClient)
|
|
|
68
71
|
res
|
|
69
72
|
elsif WebMock.net_connect_allowed?(request_signature.uri)
|
|
70
73
|
# in case there is a nil entry in the hash...
|
|
71
|
-
webmock_responses.delete(request_signature)
|
|
74
|
+
synchronize_request_response { webmock_responses.delete(request_signature) }
|
|
72
75
|
|
|
73
76
|
res = if stream
|
|
74
77
|
do_get_stream_without_webmock(req, proxy, conn, &block)
|
|
75
78
|
elsif block
|
|
76
79
|
body = ''
|
|
77
80
|
do_get_block_without_webmock(req, proxy, conn) do |http_res, chunk|
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
if chunk && chunk.bytesize > 0
|
|
82
|
+
body += chunk
|
|
83
|
+
block.call(http_res, chunk)
|
|
84
|
+
end
|
|
80
85
|
end
|
|
81
86
|
else
|
|
82
87
|
do_get_block_without_webmock(req, proxy, conn)
|
|
@@ -98,7 +103,7 @@ if defined?(::HTTPClient)
|
|
|
98
103
|
def do_request_async(method, uri, query, body, extheader)
|
|
99
104
|
req = create_request(method, uri, query, body, extheader)
|
|
100
105
|
request_signature = build_request_signature(req)
|
|
101
|
-
webmock_request_signatures << request_signature
|
|
106
|
+
synchronize_request_response { webmock_request_signatures << request_signature }
|
|
102
107
|
|
|
103
108
|
if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
|
|
104
109
|
super
|
|
@@ -117,7 +122,7 @@ if defined?(::HTTPClient)
|
|
|
117
122
|
raise HTTPClient::TimeoutError if webmock_response.should_timeout
|
|
118
123
|
webmock_response.raise_error_if_any
|
|
119
124
|
|
|
120
|
-
block.call(response, body) if block
|
|
125
|
+
block.call(response, body) if block && body && body.bytesize > 0
|
|
121
126
|
|
|
122
127
|
response
|
|
123
128
|
end
|
|
@@ -182,7 +187,9 @@ if defined?(::HTTPClient)
|
|
|
182
187
|
|
|
183
188
|
def webmock_responses
|
|
184
189
|
@webmock_responses ||= Hash.new do |hash, request_signature|
|
|
185
|
-
|
|
190
|
+
synchronize_request_response do
|
|
191
|
+
hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
|
|
192
|
+
end
|
|
186
193
|
end
|
|
187
194
|
end
|
|
188
195
|
|
|
@@ -191,8 +198,10 @@ if defined?(::HTTPClient)
|
|
|
191
198
|
end
|
|
192
199
|
|
|
193
200
|
def previous_signature_for(signature)
|
|
194
|
-
|
|
195
|
-
|
|
201
|
+
synchronize_request_response do
|
|
202
|
+
return nil unless index = webmock_request_signatures.index(signature)
|
|
203
|
+
webmock_request_signatures.delete_at(index)
|
|
204
|
+
end
|
|
196
205
|
end
|
|
197
206
|
|
|
198
207
|
private
|
|
@@ -207,6 +216,16 @@ if defined?(::HTTPClient)
|
|
|
207
216
|
hdrs
|
|
208
217
|
end
|
|
209
218
|
end
|
|
219
|
+
|
|
220
|
+
def synchronize_request_response
|
|
221
|
+
if REQUEST_RESPONSE_LOCK.owned?
|
|
222
|
+
yield
|
|
223
|
+
else
|
|
224
|
+
REQUEST_RESPONSE_LOCK.synchronize do
|
|
225
|
+
yield
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
210
229
|
end
|
|
211
230
|
|
|
212
231
|
class WebMockHTTPClient < HTTPClient
|
|
@@ -24,6 +24,12 @@ if defined?(Manticore)
|
|
|
24
24
|
Manticore.instance_variable_set(:@manticore_facade, OriginalManticoreClient.new)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
class StubbedTimeoutResponse < Manticore::StubbedResponse
|
|
28
|
+
def call
|
|
29
|
+
@handlers[:failure].call(Manticore::ConnectTimeout.new("Too slow (mocked timeout)"))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
27
33
|
class WebMockManticoreClient < Manticore::Client
|
|
28
34
|
def request(klass, url, options={}, &block)
|
|
29
35
|
super(klass, WebMock::Util::URI.normalize_uri(url).to_s, format_options(options))
|
|
@@ -50,19 +56,22 @@ if defined?(Manticore)
|
|
|
50
56
|
|
|
51
57
|
if webmock_response = registered_response_for(request_signature)
|
|
52
58
|
webmock_response.raise_error_if_any
|
|
53
|
-
manticore_response = generate_manticore_response(webmock_response)
|
|
54
|
-
|
|
59
|
+
manticore_response = generate_manticore_response(webmock_response)
|
|
60
|
+
manticore_response.on_success do
|
|
61
|
+
WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: false}, request_signature, webmock_response)
|
|
62
|
+
end
|
|
55
63
|
|
|
56
64
|
elsif real_request_allowed?(request_signature.uri)
|
|
57
|
-
manticore_response = Manticore::Response.new(self, request, context, &block)
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
manticore_response = Manticore::Response.new(self, request, context, &block)
|
|
66
|
+
manticore_response.on_complete do |completed_response|
|
|
67
|
+
webmock_response = generate_webmock_response(completed_response)
|
|
68
|
+
WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: true}, request_signature, webmock_response)
|
|
69
|
+
end
|
|
60
70
|
|
|
61
71
|
else
|
|
62
72
|
raise WebMock::NetConnectNotAllowedError.new(request_signature)
|
|
63
73
|
end
|
|
64
74
|
|
|
65
|
-
WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: real_request}, request_signature, webmock_response)
|
|
66
75
|
manticore_response
|
|
67
76
|
end
|
|
68
77
|
|
|
@@ -103,21 +112,30 @@ if defined?(Manticore)
|
|
|
103
112
|
end
|
|
104
113
|
|
|
105
114
|
def generate_manticore_response(webmock_response)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
if webmock_response.should_timeout
|
|
116
|
+
StubbedTimeoutResponse.new
|
|
117
|
+
else
|
|
118
|
+
Manticore::StubbedResponse.stub(
|
|
119
|
+
code: webmock_response.status[0],
|
|
120
|
+
body: webmock_response.body,
|
|
121
|
+
headers: webmock_response.headers,
|
|
122
|
+
cookies: {}
|
|
123
|
+
)
|
|
124
|
+
end
|
|
114
125
|
end
|
|
115
126
|
|
|
116
127
|
def generate_webmock_response(manticore_response)
|
|
117
128
|
webmock_response = WebMock::Response.new
|
|
118
129
|
webmock_response.status = [manticore_response.code, manticore_response.message]
|
|
119
|
-
webmock_response.body = manticore_response.body
|
|
120
130
|
webmock_response.headers = manticore_response.headers
|
|
131
|
+
|
|
132
|
+
# The attempt to read the body could fail if manticore is used in a streaming mode
|
|
133
|
+
webmock_response.body = begin
|
|
134
|
+
manticore_response.body
|
|
135
|
+
rescue ::Manticore::StreamClosedException
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
|
|
121
139
|
webmock_response
|
|
122
140
|
end
|
|
123
141
|
end
|
|
@@ -154,7 +154,7 @@ module WebMock
|
|
|
154
154
|
def build_net_http_response(webmock_response, &block)
|
|
155
155
|
response = Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", webmock_response.status[0].to_s, webmock_response.status[1])
|
|
156
156
|
body = webmock_response.body
|
|
157
|
-
body = nil if
|
|
157
|
+
body = nil if webmock_response.status[0].to_s == '204'
|
|
158
158
|
|
|
159
159
|
response.instance_variable_set(:@body, body)
|
|
160
160
|
webmock_response.headers.to_a.each do |name, values|
|
|
@@ -228,15 +228,16 @@ class PatchedStringIO < StringIO #:nodoc:
|
|
|
228
228
|
|
|
229
229
|
alias_method :orig_read_nonblock, :read_nonblock
|
|
230
230
|
|
|
231
|
-
def read_nonblock(size, *args)
|
|
232
|
-
|
|
231
|
+
def read_nonblock(size, *args, **kwargs)
|
|
232
|
+
args.reject! {|arg| !arg.is_a?(Hash)}
|
|
233
|
+
orig_read_nonblock(size, *args, **kwargs)
|
|
233
234
|
end
|
|
234
235
|
|
|
235
236
|
end
|
|
236
237
|
|
|
237
238
|
class StubSocket #:nodoc:
|
|
238
239
|
|
|
239
|
-
attr_accessor :read_timeout, :continue_timeout
|
|
240
|
+
attr_accessor :read_timeout, :continue_timeout, :write_timeout
|
|
240
241
|
|
|
241
242
|
def initialize(*args)
|
|
242
243
|
end
|
|
@@ -251,17 +252,20 @@ class StubSocket #:nodoc:
|
|
|
251
252
|
def readuntil(*args)
|
|
252
253
|
end
|
|
253
254
|
|
|
255
|
+
def io
|
|
256
|
+
@io ||= StubIO.new
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
class StubIO
|
|
260
|
+
def setsockopt(*args); end
|
|
261
|
+
end
|
|
254
262
|
end
|
|
255
263
|
|
|
256
264
|
module Net #:nodoc: all
|
|
257
265
|
|
|
258
266
|
class WebMockNetBufferedIO < BufferedIO
|
|
259
|
-
def initialize(io,
|
|
260
|
-
|
|
261
|
-
@rbuf = ''
|
|
262
|
-
@debug_output = debug_output
|
|
263
|
-
|
|
264
|
-
@io = case io
|
|
267
|
+
def initialize(io, *args, **kwargs)
|
|
268
|
+
io = case io
|
|
265
269
|
when Socket, OpenSSL::SSL::SSLSocket, IO
|
|
266
270
|
io
|
|
267
271
|
when StringIO
|
|
@@ -269,7 +273,36 @@ module Net #:nodoc: all
|
|
|
269
273
|
when String
|
|
270
274
|
PatchedStringIO.new(io)
|
|
271
275
|
end
|
|
272
|
-
raise "Unable to create local socket" unless
|
|
276
|
+
raise "Unable to create local socket" unless io
|
|
277
|
+
|
|
278
|
+
# Prior to 2.4.0 `BufferedIO` only takes a single argument (`io`) with no
|
|
279
|
+
# options. Here we pass through our full set of arguments only if we're
|
|
280
|
+
# on 2.4.0 or later, and use a simplified invocation otherwise.
|
|
281
|
+
if RUBY_VERSION >= '2.4.0'
|
|
282
|
+
super
|
|
283
|
+
else
|
|
284
|
+
super(io)
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
if RUBY_VERSION >= '2.6.0'
|
|
289
|
+
# https://github.com/ruby/ruby/blob/7d02441f0d6e5c9d0a73a024519eba4f69e36dce/lib/net/protocol.rb#L208
|
|
290
|
+
# Modified version of method from ruby, so that nil is always passed into orig_read_nonblock to avoid timeout
|
|
291
|
+
def rbuf_fill
|
|
292
|
+
case rv = @io.read_nonblock(BUFSIZE, nil, exception: false)
|
|
293
|
+
when String
|
|
294
|
+
return if rv.nil?
|
|
295
|
+
@rbuf << rv
|
|
296
|
+
rv.clear
|
|
297
|
+
return
|
|
298
|
+
when :wait_readable
|
|
299
|
+
@io.to_io.wait_readable(@read_timeout) or raise Net::ReadTimeout
|
|
300
|
+
when :wait_writable
|
|
301
|
+
@io.to_io.wait_writable(@read_timeout) or raise Net::ReadTimeout
|
|
302
|
+
when nil
|
|
303
|
+
raise EOFError, 'end of file reached'
|
|
304
|
+
end while true
|
|
305
|
+
end
|
|
273
306
|
end
|
|
274
307
|
end
|
|
275
308
|
|
|
@@ -280,8 +313,6 @@ module WebMock
|
|
|
280
313
|
module NetHTTPUtility
|
|
281
314
|
|
|
282
315
|
def self.request_signature_from_request(net_http, request, body = nil)
|
|
283
|
-
protocol = net_http.use_ssl? ? "https" : "http"
|
|
284
|
-
|
|
285
316
|
path = request.path
|
|
286
317
|
|
|
287
318
|
if path.respond_to?(:request_uri) #https://github.com/bblimke/webmock/issues/288
|
|
@@ -290,7 +321,7 @@ module WebMock
|
|
|
290
321
|
|
|
291
322
|
path = WebMock::Util::URI.heuristic_parse(path).request_uri if path =~ /^http/
|
|
292
323
|
|
|
293
|
-
uri =
|
|
324
|
+
uri = get_uri(net_http, path)
|
|
294
325
|
method = request.method.downcase.to_sym
|
|
295
326
|
|
|
296
327
|
headers = Hash[*request.to_hash.map {|k,v| [k, v]}.inject([]) {|r,x| r + x}]
|
|
@@ -310,6 +341,15 @@ module WebMock
|
|
|
310
341
|
WebMock::RequestSignature.new(method, uri, body: request.body, headers: headers)
|
|
311
342
|
end
|
|
312
343
|
|
|
344
|
+
def self.get_uri(net_http, path)
|
|
345
|
+
protocol = net_http.use_ssl? ? "https" : "http"
|
|
346
|
+
|
|
347
|
+
hostname = net_http.address
|
|
348
|
+
hostname = "[#{hostname}]" if /\A\[.*\]\z/ !~ hostname && /:/ =~ hostname
|
|
349
|
+
|
|
350
|
+
"#{protocol}://#{hostname}:#{net_http.port}#{path}"
|
|
351
|
+
end
|
|
352
|
+
|
|
313
353
|
def self.validate_headers(headers)
|
|
314
354
|
# For Ruby versions < 2.3.0, if you make a request with headers that are symbols
|
|
315
355
|
# Net::HTTP raises a NoMethodError
|
|
@@ -69,7 +69,7 @@ if defined?(::Patron)
|
|
|
69
69
|
uri = WebMock::Util::URI.heuristic_parse(req.url)
|
|
70
70
|
uri.path = uri.normalized_path.gsub("[^:]//","/")
|
|
71
71
|
|
|
72
|
-
if [:put, :post].include?(req.action)
|
|
72
|
+
if [:put, :post, :patch].include?(req.action)
|
|
73
73
|
if req.file_name
|
|
74
74
|
if !File.exist?(req.file_name) || !File.readable?(req.file_name)
|
|
75
75
|
raise ArgumentError.new("Unable to open specified file.")
|
|
@@ -106,11 +106,11 @@ if defined?(::Patron)
|
|
|
106
106
|
header_data = ([status_line] + header_fields).join("\r\n")
|
|
107
107
|
|
|
108
108
|
::Patron::Response.new(
|
|
109
|
-
"",
|
|
109
|
+
"".dup,
|
|
110
110
|
webmock_response.status[0],
|
|
111
111
|
0,
|
|
112
112
|
header_data,
|
|
113
|
-
webmock_response.body,
|
|
113
|
+
webmock_response.body.dup,
|
|
114
114
|
default_response_charset
|
|
115
115
|
)
|
|
116
116
|
end
|
|
@@ -118,7 +118,7 @@ if defined?(::Patron)
|
|
|
118
118
|
def self.build_webmock_response(patron_response)
|
|
119
119
|
webmock_response = WebMock::Response.new
|
|
120
120
|
reason = patron_response.status_line.
|
|
121
|
-
scan(%r(\AHTTP/(\d
|
|
121
|
+
scan(%r(\AHTTP/(\d+(?:\.\d+)?)\s+(\d\d\d)\s*([^\r\n]+)?))[0][2]
|
|
122
122
|
webmock_response.status = [patron_response.status, reason]
|
|
123
123
|
webmock_response.body = patron_response.body
|
|
124
124
|
webmock_response.headers = patron_response.headers
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module WebMock
|
|
2
|
+
module Matchers
|
|
3
|
+
# Base class for Hash matchers
|
|
4
|
+
# https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
|
|
5
|
+
class HashArgumentMatcher
|
|
6
|
+
def initialize(expected)
|
|
7
|
+
@expected = Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(expected, deep: true).sort]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def ==(_actual, &block)
|
|
11
|
+
@expected.all?(&block)
|
|
12
|
+
rescue NoMethodError
|
|
13
|
+
false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.from_rspec_matcher(matcher)
|
|
17
|
+
new(matcher.instance_variable_get(:@expected))
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module WebMock
|
|
2
|
+
module Matchers
|
|
3
|
+
# this is a based on RSpec::Mocks::ArgumentMatchers::HashExcludingMatcher
|
|
4
|
+
# https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
|
|
5
|
+
class HashExcludingMatcher < HashArgumentMatcher
|
|
6
|
+
def ==(actual)
|
|
7
|
+
super { |key, value| !actual.key?(key) || value != actual[key] }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def inspect
|
|
11
|
+
"hash_excluding(#{@expected.inspect})"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
module WebMock
|
|
2
2
|
module Matchers
|
|
3
|
-
#this is a based on RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher
|
|
4
|
-
#https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
|
|
5
|
-
class HashIncludingMatcher
|
|
6
|
-
def initialize(expected)
|
|
7
|
-
@expected = Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(expected, deep: true).sort]
|
|
8
|
-
end
|
|
9
|
-
|
|
3
|
+
# this is a based on RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher
|
|
4
|
+
# https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
|
|
5
|
+
class HashIncludingMatcher < HashArgumentMatcher
|
|
10
6
|
def ==(actual)
|
|
11
|
-
|
|
7
|
+
super { |key, value| actual.key?(key) && value === actual[key] }
|
|
12
8
|
rescue NoMethodError
|
|
13
9
|
false
|
|
14
10
|
end
|
|
@@ -16,21 +12,6 @@ module WebMock
|
|
|
16
12
|
def inspect
|
|
17
13
|
"hash_including(#{@expected.inspect})"
|
|
18
14
|
end
|
|
19
|
-
|
|
20
|
-
def self.from_rspec_matcher(matcher)
|
|
21
|
-
new(matcher.instance_variable_get(:@expected))
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
#this is a based on RSpec::Mocks::ArgumentMatchers::AnyArgMatcher
|
|
26
|
-
class AnyArgMatcher
|
|
27
|
-
def initialize(ignore)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def ==(other)
|
|
31
|
-
true
|
|
32
|
-
end
|
|
33
15
|
end
|
|
34
|
-
|
|
35
16
|
end
|
|
36
17
|
end
|
|
@@ -12,7 +12,7 @@ module WebMock
|
|
|
12
12
|
def body_diff
|
|
13
13
|
return {} unless request_signature_diffable? && request_stub_diffable?
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Hashdiff.diff(request_signature_body_hash, request_stub_body_hash)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
attr_reader :request_signature, :request_stub
|
|
@@ -53,9 +53,8 @@ module WebMock
|
|
|
53
53
|
|
|
54
54
|
def failure_message_phrase(is_negated=false)
|
|
55
55
|
negation = is_negated ? "was not" : "was"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
text
|
|
56
|
+
"The request #{request_pattern} #{negation} expected to execute #{quantity_phrase(is_negated)}but it executed #{times(times_executed)}" +
|
|
57
|
+
self.class.executed_requests_message
|
|
59
58
|
end
|
|
60
59
|
|
|
61
60
|
def quantity_phrase(is_negated=false)
|