webmock 1.8.6 → 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 +7 -0
- data/.github/workflows/CI.yml +37 -0
- data/.gitignore +6 -0
- data/CHANGELOG.md +1198 -0
- data/Gemfile +3 -15
- data/README.md +761 -305
- data/Rakefile +13 -40
- data/lib/webmock/api.rb +63 -17
- data/lib/webmock/callback_registry.rb +1 -1
- data/lib/webmock/config.rb +8 -0
- data/lib/webmock/cucumber.rb +2 -0
- data/lib/webmock/errors.rb +8 -24
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +148 -84
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +224 -4
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +104 -34
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
- data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +64 -0
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
- data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +152 -86
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +145 -0
- data/lib/webmock/http_lib_adapters/net_http.rb +155 -46
- data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
- data/lib/webmock/http_lib_adapters/patron_adapter.rb +16 -15
- data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +76 -82
- 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 -12
- data/lib/webmock/minitest.rb +29 -3
- data/lib/webmock/rack_response.rb +14 -7
- data/lib/webmock/request_body_diff.rb +64 -0
- data/lib/webmock/request_execution_verifier.rb +38 -17
- data/lib/webmock/request_pattern.rb +158 -38
- data/lib/webmock/request_registry.rb +3 -3
- data/lib/webmock/request_signature.rb +7 -3
- data/lib/webmock/request_signature_snippet.rb +61 -0
- data/lib/webmock/request_stub.rb +9 -6
- data/lib/webmock/response.rb +30 -15
- data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +38 -2
- data/lib/webmock/rspec/matchers/webmock_matcher.rb +23 -2
- data/lib/webmock/rspec/matchers.rb +0 -1
- data/lib/webmock/rspec.rb +11 -2
- data/lib/webmock/stub_registry.rb +31 -10
- data/lib/webmock/stub_request_snippet.rb +14 -6
- data/lib/webmock/test_unit.rb +4 -4
- data/lib/webmock/util/hash_counter.rb +20 -6
- data/lib/webmock/util/hash_keys_stringifier.rb +5 -3
- data/lib/webmock/util/hash_validator.rb +17 -0
- data/lib/webmock/util/headers.rb +23 -2
- data/lib/webmock/util/json.rb +20 -7
- data/lib/webmock/util/query_mapper.rb +281 -0
- data/lib/webmock/util/uri.rb +29 -19
- data/lib/webmock/util/values_stringifier.rb +20 -0
- data/lib/webmock/util/version_checker.rb +40 -2
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +56 -17
- data/lib/webmock.rb +56 -46
- data/minitest/test_helper.rb +8 -3
- data/minitest/test_webmock.rb +4 -1
- data/minitest/webmock_spec.rb +16 -6
- 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 +227 -68
- data/spec/acceptance/curb/curb_spec_helper.rb +11 -8
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +322 -28
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +15 -10
- data/spec/acceptance/excon/excon_spec.rb +66 -4
- data/spec/acceptance/excon/excon_spec_helper.rb +21 -7
- data/spec/acceptance/http_rb/http_rb_spec.rb +93 -0
- data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
- data/spec/acceptance/httpclient/httpclient_spec.rb +152 -11
- data/spec/acceptance/httpclient/httpclient_spec_helper.rb +25 -16
- data/spec/acceptance/manticore/manticore_spec.rb +107 -0
- data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
- data/spec/acceptance/net_http/net_http_shared.rb +52 -24
- data/spec/acceptance/net_http/net_http_spec.rb +164 -50
- data/spec/acceptance/net_http/net_http_spec_helper.rb +19 -10
- data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
- data/spec/acceptance/patron/patron_spec.rb +29 -40
- data/spec/acceptance/patron/patron_spec_helper.rb +15 -11
- data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +229 -58
- data/spec/acceptance/shared/callbacks.rb +32 -30
- data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +20 -5
- data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +14 -14
- data/spec/acceptance/shared/precedence_of_stubs.rb +6 -6
- data/spec/acceptance/shared/request_expectations.rb +560 -296
- data/spec/acceptance/shared/returning_declared_responses.rb +180 -138
- data/spec/acceptance/shared/stubbing_requests.rb +385 -154
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +78 -17
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +19 -15
- data/spec/acceptance/webmock_shared.rb +2 -2
- data/spec/fixtures/test.txt +1 -0
- data/spec/quality_spec.rb +27 -3
- data/spec/spec_helper.rb +11 -20
- data/spec/support/failures.rb +9 -0
- data/spec/support/my_rack_app.rb +8 -3
- data/spec/support/network_connection.rb +7 -13
- data/spec/support/webmock_server.rb +8 -3
- data/spec/unit/api_spec.rb +175 -0
- data/spec/unit/errors_spec.rb +116 -19
- data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +1 -1
- data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +2 -2
- data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
- data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
- data/spec/unit/rack_response_spec.rb +54 -16
- data/spec/unit/request_body_diff_spec.rb +90 -0
- data/spec/unit/request_execution_verifier_spec.rb +147 -39
- data/spec/unit/request_pattern_spec.rb +462 -198
- data/spec/unit/request_registry_spec.rb +29 -9
- data/spec/unit/request_signature_snippet_spec.rb +89 -0
- data/spec/unit/request_signature_spec.rb +91 -49
- data/spec/unit/request_stub_spec.rb +71 -70
- data/spec/unit/response_spec.rb +100 -81
- data/spec/unit/stub_registry_spec.rb +37 -20
- data/spec/unit/stub_request_snippet_spec.rb +51 -31
- data/spec/unit/util/hash_counter_spec.rb +6 -6
- data/spec/unit/util/hash_keys_stringifier_spec.rb +4 -4
- data/spec/unit/util/headers_spec.rb +4 -4
- data/spec/unit/util/json_spec.rb +29 -3
- data/spec/unit/util/query_mapper_spec.rb +157 -0
- data/spec/unit/util/uri_spec.rb +150 -36
- data/spec/unit/util/version_checker_spec.rb +15 -9
- data/spec/unit/webmock_spec.rb +57 -4
- data/test/http_request.rb +3 -3
- data/test/shared_test.rb +45 -13
- data/test/test_helper.rb +1 -1
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +30 -11
- metadata +308 -199
- data/.rvmrc +0 -1
- data/.travis.yml +0 -11
- data/Guardfile +0 -24
- data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_0_x.rb +0 -151
- data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +0 -210
@@ -1,7 +1,9 @@
|
|
1
1
|
begin
|
2
2
|
require 'httpclient'
|
3
|
+
require 'jsonclient' # defined in 'httpclient' gem as well
|
3
4
|
rescue LoadError
|
4
5
|
# httpclient not found
|
6
|
+
# or jsonclient not defined (in old versions of httclient gem)
|
5
7
|
end
|
6
8
|
|
7
9
|
if defined?(::HTTPClient)
|
@@ -11,59 +13,85 @@ if defined?(::HTTPClient)
|
|
11
13
|
class HTTPClientAdapter < HttpLibAdapter
|
12
14
|
adapter_for :httpclient
|
13
15
|
|
14
|
-
|
16
|
+
unless const_defined?(:OriginalHttpClient)
|
17
|
+
OriginalHttpClient = ::HTTPClient
|
18
|
+
end
|
19
|
+
|
20
|
+
unless const_defined?(:OriginalJsonClient)
|
21
|
+
OriginalJsonClient = ::JSONClient if defined?(::JSONClient)
|
22
|
+
end
|
15
23
|
|
16
24
|
def self.enable!
|
17
25
|
Object.send(:remove_const, :HTTPClient)
|
18
26
|
Object.send(:const_set, :HTTPClient, WebMockHTTPClient)
|
27
|
+
if defined? ::JSONClient
|
28
|
+
Object.send(:remove_const, :JSONClient)
|
29
|
+
Object.send(:const_set, :JSONClient, WebMockJSONClient)
|
30
|
+
end
|
19
31
|
end
|
20
32
|
|
21
33
|
def self.disable!
|
22
34
|
Object.send(:remove_const, :HTTPClient)
|
23
35
|
Object.send(:const_set, :HTTPClient, OriginalHttpClient)
|
36
|
+
if defined? ::JSONClient
|
37
|
+
Object.send(:remove_const, :JSONClient)
|
38
|
+
Object.send(:const_set, :JSONClient, OriginalJsonClient)
|
39
|
+
end
|
24
40
|
end
|
25
41
|
end
|
26
42
|
end
|
27
43
|
end
|
28
44
|
|
45
|
+
module WebMockHTTPClients
|
29
46
|
|
30
|
-
|
47
|
+
REQUEST_RESPONSE_LOCK = Mutex.new
|
31
48
|
|
32
|
-
def
|
33
|
-
|
49
|
+
def do_get_block(req, proxy, conn, &block)
|
50
|
+
do_get(req, proxy, conn, false, &block)
|
34
51
|
end
|
35
52
|
|
36
|
-
def
|
37
|
-
|
53
|
+
def do_get_stream(req, proxy, conn, &block)
|
54
|
+
do_get(req, proxy, conn, true, &block)
|
38
55
|
end
|
39
56
|
|
40
|
-
def
|
57
|
+
def do_get(req, proxy, conn, stream = false, &block)
|
41
58
|
request_signature = build_request_signature(req, :reuse_existing)
|
42
59
|
|
43
60
|
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
44
61
|
|
45
62
|
if webmock_responses[request_signature]
|
46
|
-
webmock_response = webmock_responses.delete(request_signature)
|
47
|
-
response = build_httpclient_response(webmock_response, stream, &block)
|
63
|
+
webmock_response = synchronize_request_response { webmock_responses.delete(request_signature) }
|
64
|
+
response = build_httpclient_response(webmock_response, stream, req.header, &block)
|
48
65
|
@request_filter.each do |filter|
|
49
66
|
filter.filter_response(req, response)
|
50
67
|
end
|
51
68
|
res = conn.push(response)
|
52
69
|
WebMock::CallbackRegistry.invoke_callbacks(
|
53
|
-
{:
|
70
|
+
{lib: :httpclient}, request_signature, webmock_response)
|
54
71
|
res
|
55
72
|
elsif WebMock.net_connect_allowed?(request_signature.uri)
|
73
|
+
# in case there is a nil entry in the hash...
|
74
|
+
synchronize_request_response { webmock_responses.delete(request_signature) }
|
75
|
+
|
56
76
|
res = if stream
|
57
77
|
do_get_stream_without_webmock(req, proxy, conn, &block)
|
78
|
+
elsif block
|
79
|
+
body = ''
|
80
|
+
do_get_block_without_webmock(req, proxy, conn) do |http_res, chunk|
|
81
|
+
if chunk && chunk.bytesize > 0
|
82
|
+
body += chunk
|
83
|
+
block.call(http_res, chunk)
|
84
|
+
end
|
85
|
+
end
|
58
86
|
else
|
59
|
-
do_get_block_without_webmock(req, proxy, conn
|
87
|
+
do_get_block_without_webmock(req, proxy, conn)
|
60
88
|
end
|
61
89
|
res = conn.pop
|
62
90
|
conn.push(res)
|
63
91
|
if WebMock::CallbackRegistry.any_callbacks?
|
64
|
-
webmock_response = build_webmock_response(res)
|
92
|
+
webmock_response = build_webmock_response(res, body)
|
65
93
|
WebMock::CallbackRegistry.invoke_callbacks(
|
66
|
-
{:
|
94
|
+
{lib: :httpclient, real_request: true}, request_signature,
|
67
95
|
webmock_response)
|
68
96
|
end
|
69
97
|
res
|
@@ -72,30 +100,21 @@ if defined?(::HTTPClient)
|
|
72
100
|
end
|
73
101
|
end
|
74
102
|
|
75
|
-
def
|
103
|
+
def do_request_async(method, uri, query, body, extheader)
|
76
104
|
req = create_request(method, uri, query, body, extheader)
|
77
105
|
request_signature = build_request_signature(req)
|
78
|
-
webmock_request_signatures << request_signature
|
106
|
+
synchronize_request_response { webmock_request_signatures << request_signature }
|
79
107
|
|
80
108
|
if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
|
81
|
-
|
109
|
+
super
|
82
110
|
else
|
83
111
|
raise WebMock::NetConnectNotAllowedError.new(request_signature)
|
84
112
|
end
|
85
113
|
end
|
86
114
|
|
87
|
-
|
88
|
-
alias_method :do_get_block, :do_get_block_with_webmock
|
89
|
-
|
90
|
-
alias_method :do_get_stream_without_webmock, :do_get_stream
|
91
|
-
alias_method :do_get_stream, :do_get_stream_with_webmock
|
92
|
-
|
93
|
-
alias_method :do_request_async_without_webmock, :do_request_async
|
94
|
-
alias_method :do_request_async, :do_request_async_with_webmock
|
95
|
-
|
96
|
-
def build_httpclient_response(webmock_response, stream = false, &block)
|
115
|
+
def build_httpclient_response(webmock_response, stream = false, req_header = nil, &block)
|
97
116
|
body = stream ? StringIO.new(webmock_response.body) : webmock_response.body
|
98
|
-
response = HTTP::Message.new_response(body)
|
117
|
+
response = HTTP::Message.new_response(body, req_header)
|
99
118
|
response.header.init_response(webmock_response.status[0])
|
100
119
|
response.reason=webmock_response.status[1]
|
101
120
|
webmock_response.headers.to_a.each { |name, value| response.header.set(name, value) }
|
@@ -103,91 +122,138 @@ if defined?(::HTTPClient)
|
|
103
122
|
raise HTTPClient::TimeoutError if webmock_response.should_timeout
|
104
123
|
webmock_response.raise_error_if_any
|
105
124
|
|
106
|
-
block.call(response, body) if block
|
125
|
+
block.call(response, body) if block && body && body.bytesize > 0
|
107
126
|
|
108
127
|
response
|
109
128
|
end
|
110
|
-
end
|
111
129
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
130
|
+
def build_webmock_response(httpclient_response, body = nil)
|
131
|
+
webmock_response = WebMock::Response.new
|
132
|
+
webmock_response.status = [httpclient_response.status, httpclient_response.reason]
|
133
|
+
|
134
|
+
webmock_response.headers = {}.tap do |hash|
|
135
|
+
httpclient_response.header.all.each do |(key, value)|
|
136
|
+
if hash.has_key?(key)
|
137
|
+
hash[key] = Array(hash[key]) + [value]
|
138
|
+
else
|
139
|
+
hash[key] = value
|
140
|
+
end
|
122
141
|
end
|
123
142
|
end
|
124
|
-
end
|
125
143
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
144
|
+
if body
|
145
|
+
webmock_response.body = body
|
146
|
+
elsif httpclient_response.content.respond_to?(:read)
|
147
|
+
webmock_response.body = httpclient_response.content.read
|
148
|
+
body = HTTP::Message::Body.new
|
149
|
+
body.init_response(StringIO.new(webmock_response.body))
|
150
|
+
httpclient_response.body = body
|
151
|
+
else
|
152
|
+
webmock_response.body = httpclient_response.content
|
153
|
+
end
|
154
|
+
webmock_response
|
133
155
|
end
|
134
|
-
webmock_response
|
135
|
-
end
|
136
156
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
157
|
+
def build_request_signature(req, reuse_existing = false)
|
158
|
+
uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
|
159
|
+
uri.query = WebMock::Util::QueryMapper.values_to_query(req.header.request_query, notation: WebMock::Config.instance.query_values_notation) if req.header.request_query
|
160
|
+
uri.port = req.header.request_uri.port
|
161
|
+
|
162
|
+
@request_filter.each do |filter|
|
163
|
+
filter.filter_request(req)
|
164
|
+
end
|
165
|
+
|
166
|
+
headers = req.header.all.inject({}) do |hdrs, header|
|
167
|
+
hdrs[header[0]] ||= []
|
168
|
+
hdrs[header[0]] << header[1]
|
169
|
+
hdrs
|
170
|
+
end
|
171
|
+
headers = headers_from_session(uri).merge(headers)
|
172
|
+
|
173
|
+
signature = WebMock::RequestSignature.new(
|
174
|
+
req.header.request_method.downcase.to_sym,
|
175
|
+
uri.to_s,
|
176
|
+
body: req.http_body.dump,
|
177
|
+
headers: headers
|
178
|
+
)
|
179
|
+
|
180
|
+
# reuse a previous identical signature object if we stored one for later use
|
181
|
+
if reuse_existing && previous_signature = previous_signature_for(signature)
|
182
|
+
return previous_signature
|
183
|
+
end
|
142
184
|
|
143
|
-
|
144
|
-
|
185
|
+
signature
|
186
|
+
end
|
145
187
|
|
146
|
-
|
147
|
-
|
188
|
+
def webmock_responses
|
189
|
+
@webmock_responses ||= Hash.new do |hash, request_signature|
|
190
|
+
synchronize_request_response do
|
191
|
+
hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
|
192
|
+
end
|
193
|
+
end
|
148
194
|
end
|
149
195
|
|
150
|
-
|
151
|
-
|
152
|
-
hdrs[header[0]] << header[1]
|
153
|
-
hdrs
|
196
|
+
def webmock_request_signatures
|
197
|
+
@webmock_request_signatures ||= []
|
154
198
|
end
|
155
199
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
200
|
+
def previous_signature_for(signature)
|
201
|
+
synchronize_request_response do
|
202
|
+
return nil unless index = webmock_request_signatures.index(signature)
|
203
|
+
webmock_request_signatures.delete_at(index)
|
204
|
+
end
|
161
205
|
end
|
162
206
|
|
163
|
-
|
164
|
-
req.header.request_method.downcase.to_sym,
|
165
|
-
uri.to_s,
|
166
|
-
:body => req.content,
|
167
|
-
:headers => headers
|
168
|
-
)
|
207
|
+
private
|
169
208
|
|
170
|
-
#
|
171
|
-
|
172
|
-
|
209
|
+
# some of the headers sent by HTTPClient are derived from
|
210
|
+
# the client session
|
211
|
+
def headers_from_session(uri)
|
212
|
+
session_headers = HTTP::Message::Headers.new
|
213
|
+
@session_manager.send(:open, uri).send(:set_header, MessageMock.new(session_headers))
|
214
|
+
session_headers.all.inject({}) do |hdrs, header|
|
215
|
+
hdrs[header[0]] = header[1]
|
216
|
+
hdrs
|
217
|
+
end
|
173
218
|
end
|
174
219
|
|
175
|
-
|
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
|
176
229
|
end
|
177
230
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
231
|
+
class WebMockHTTPClient < HTTPClient
|
232
|
+
alias_method :do_get_block_without_webmock, :do_get_block
|
233
|
+
alias_method :do_get_stream_without_webmock, :do_get_stream
|
234
|
+
|
235
|
+
include WebMockHTTPClients
|
182
236
|
end
|
183
237
|
|
184
|
-
|
185
|
-
|
238
|
+
if defined? ::JSONClient
|
239
|
+
class WebMockJSONClient < JSONClient
|
240
|
+
alias_method :do_get_block_without_webmock, :do_get_block
|
241
|
+
alias_method :do_get_stream_without_webmock, :do_get_stream
|
242
|
+
|
243
|
+
include WebMockHTTPClients
|
244
|
+
end
|
186
245
|
end
|
187
246
|
|
188
|
-
|
189
|
-
|
190
|
-
|
247
|
+
|
248
|
+
# Mocks a HTTPClient HTTP::Message
|
249
|
+
class MessageMock
|
250
|
+
attr_reader :header
|
251
|
+
|
252
|
+
def initialize(headers)
|
253
|
+
@header = headers
|
254
|
+
end
|
255
|
+
|
256
|
+
def http_version=(value);end
|
191
257
|
end
|
192
258
|
|
193
259
|
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
begin
|
2
|
+
require 'manticore'
|
3
|
+
rescue LoadError
|
4
|
+
# manticore not found
|
5
|
+
end
|
6
|
+
|
7
|
+
if defined?(Manticore)
|
8
|
+
module WebMock
|
9
|
+
module HttpLibAdapters
|
10
|
+
class ManticoreAdapter < HttpLibAdapter
|
11
|
+
adapter_for :manticore
|
12
|
+
|
13
|
+
OriginalManticoreClient = Manticore::Client
|
14
|
+
|
15
|
+
def self.enable!
|
16
|
+
Manticore.send(:remove_const, :Client)
|
17
|
+
Manticore.send(:const_set, :Client, WebMockManticoreClient)
|
18
|
+
Manticore.instance_variable_set(:@manticore_facade, WebMockManticoreClient.new)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.disable!
|
22
|
+
Manticore.send(:remove_const, :Client)
|
23
|
+
Manticore.send(:const_set, :Client, OriginalManticoreClient)
|
24
|
+
Manticore.instance_variable_set(:@manticore_facade, OriginalManticoreClient.new)
|
25
|
+
end
|
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
|
+
|
33
|
+
class WebMockManticoreClient < Manticore::Client
|
34
|
+
def request(klass, url, options={}, &block)
|
35
|
+
super(klass, WebMock::Util::URI.normalize_uri(url).to_s, format_options(options))
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def format_options(options)
|
41
|
+
return options unless headers = options[:headers]
|
42
|
+
|
43
|
+
options.merge(headers: join_array_values(headers))
|
44
|
+
end
|
45
|
+
|
46
|
+
def join_array_values(headers)
|
47
|
+
headers.reduce({}) do |h, (k,v)|
|
48
|
+
v = v.join(', ') if v.is_a?(Array)
|
49
|
+
h.merge(k => v)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def response_object_for(request, context, &block)
|
54
|
+
request_signature = generate_webmock_request_signature(request, context)
|
55
|
+
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
56
|
+
|
57
|
+
if webmock_response = registered_response_for(request_signature)
|
58
|
+
webmock_response.raise_error_if_any
|
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
|
63
|
+
|
64
|
+
elsif real_request_allowed?(request_signature.uri)
|
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
|
70
|
+
|
71
|
+
else
|
72
|
+
raise WebMock::NetConnectNotAllowedError.new(request_signature)
|
73
|
+
end
|
74
|
+
|
75
|
+
manticore_response
|
76
|
+
end
|
77
|
+
|
78
|
+
def registered_response_for(request_signature)
|
79
|
+
WebMock::StubRegistry.instance.response_for_request(request_signature)
|
80
|
+
end
|
81
|
+
|
82
|
+
def real_request_allowed?(uri)
|
83
|
+
WebMock.net_connect_allowed?(uri)
|
84
|
+
end
|
85
|
+
|
86
|
+
def generate_webmock_request_signature(request, context)
|
87
|
+
method = request.method.downcase
|
88
|
+
uri = request.uri.to_s
|
89
|
+
body = read_body(request)
|
90
|
+
headers = split_array_values(request.headers)
|
91
|
+
|
92
|
+
if context.get_credentials_provider && credentials = context.get_credentials_provider.get_credentials(AuthScope::ANY)
|
93
|
+
headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(credentials.get_user_name,credentials.get_password)
|
94
|
+
end
|
95
|
+
|
96
|
+
WebMock::RequestSignature.new(method, uri, {body: body, headers: headers})
|
97
|
+
end
|
98
|
+
|
99
|
+
def read_body(request)
|
100
|
+
if request.respond_to?(:entity) && !request.entity.nil?
|
101
|
+
Manticore::EntityConverter.new.read_entity(request.entity)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def split_array_values(headers = [])
|
106
|
+
headers.each_with_object({}) do |(k, v), h|
|
107
|
+
h[k] = case v
|
108
|
+
when /,/ then v.split(',').map(&:strip)
|
109
|
+
else v
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def generate_manticore_response(webmock_response)
|
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
|
125
|
+
end
|
126
|
+
|
127
|
+
def generate_webmock_response(manticore_response)
|
128
|
+
webmock_response = WebMock::Response.new
|
129
|
+
webmock_response.status = [manticore_response.code, manticore_response.message]
|
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
|
+
|
139
|
+
webmock_response
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|