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.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/CI.yml +37 -0
  3. data/.gitignore +6 -0
  4. data/CHANGELOG.md +1198 -0
  5. data/Gemfile +3 -15
  6. data/README.md +761 -305
  7. data/Rakefile +13 -40
  8. data/lib/webmock/api.rb +63 -17
  9. data/lib/webmock/callback_registry.rb +1 -1
  10. data/lib/webmock/config.rb +8 -0
  11. data/lib/webmock/cucumber.rb +2 -0
  12. data/lib/webmock/errors.rb +8 -24
  13. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
  14. data/lib/webmock/http_lib_adapters/curb_adapter.rb +148 -84
  15. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +224 -4
  16. data/lib/webmock/http_lib_adapters/excon_adapter.rb +104 -34
  17. data/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
  18. data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +64 -0
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +152 -86
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +145 -0
  25. data/lib/webmock/http_lib_adapters/net_http.rb +155 -46
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +16 -15
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +76 -82
  29. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +4 -12
  33. data/lib/webmock/minitest.rb +29 -3
  34. data/lib/webmock/rack_response.rb +14 -7
  35. data/lib/webmock/request_body_diff.rb +64 -0
  36. data/lib/webmock/request_execution_verifier.rb +38 -17
  37. data/lib/webmock/request_pattern.rb +158 -38
  38. data/lib/webmock/request_registry.rb +3 -3
  39. data/lib/webmock/request_signature.rb +7 -3
  40. data/lib/webmock/request_signature_snippet.rb +61 -0
  41. data/lib/webmock/request_stub.rb +9 -6
  42. data/lib/webmock/response.rb +30 -15
  43. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +38 -2
  44. data/lib/webmock/rspec/matchers/webmock_matcher.rb +23 -2
  45. data/lib/webmock/rspec/matchers.rb +0 -1
  46. data/lib/webmock/rspec.rb +11 -2
  47. data/lib/webmock/stub_registry.rb +31 -10
  48. data/lib/webmock/stub_request_snippet.rb +14 -6
  49. data/lib/webmock/test_unit.rb +4 -4
  50. data/lib/webmock/util/hash_counter.rb +20 -6
  51. data/lib/webmock/util/hash_keys_stringifier.rb +5 -3
  52. data/lib/webmock/util/hash_validator.rb +17 -0
  53. data/lib/webmock/util/headers.rb +23 -2
  54. data/lib/webmock/util/json.rb +20 -7
  55. data/lib/webmock/util/query_mapper.rb +281 -0
  56. data/lib/webmock/util/uri.rb +29 -19
  57. data/lib/webmock/util/values_stringifier.rb +20 -0
  58. data/lib/webmock/util/version_checker.rb +40 -2
  59. data/lib/webmock/version.rb +1 -1
  60. data/lib/webmock/webmock.rb +56 -17
  61. data/lib/webmock.rb +56 -46
  62. data/minitest/test_helper.rb +8 -3
  63. data/minitest/test_webmock.rb +4 -1
  64. data/minitest/webmock_spec.rb +16 -6
  65. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  66. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  67. data/spec/acceptance/curb/curb_spec.rb +227 -68
  68. data/spec/acceptance/curb/curb_spec_helper.rb +11 -8
  69. data/spec/acceptance/em_http_request/em_http_request_spec.rb +322 -28
  70. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +15 -10
  71. data/spec/acceptance/excon/excon_spec.rb +66 -4
  72. data/spec/acceptance/excon/excon_spec_helper.rb +21 -7
  73. data/spec/acceptance/http_rb/http_rb_spec.rb +93 -0
  74. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
  75. data/spec/acceptance/httpclient/httpclient_spec.rb +152 -11
  76. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +25 -16
  77. data/spec/acceptance/manticore/manticore_spec.rb +107 -0
  78. data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
  79. data/spec/acceptance/net_http/net_http_shared.rb +52 -24
  80. data/spec/acceptance/net_http/net_http_spec.rb +164 -50
  81. data/spec/acceptance/net_http/net_http_spec_helper.rb +19 -10
  82. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  83. data/spec/acceptance/patron/patron_spec.rb +29 -40
  84. data/spec/acceptance/patron/patron_spec_helper.rb +15 -11
  85. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +229 -58
  86. data/spec/acceptance/shared/callbacks.rb +32 -30
  87. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +20 -5
  88. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +14 -14
  89. data/spec/acceptance/shared/precedence_of_stubs.rb +6 -6
  90. data/spec/acceptance/shared/request_expectations.rb +560 -296
  91. data/spec/acceptance/shared/returning_declared_responses.rb +180 -138
  92. data/spec/acceptance/shared/stubbing_requests.rb +385 -154
  93. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +78 -17
  94. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +19 -15
  95. data/spec/acceptance/webmock_shared.rb +2 -2
  96. data/spec/fixtures/test.txt +1 -0
  97. data/spec/quality_spec.rb +27 -3
  98. data/spec/spec_helper.rb +11 -20
  99. data/spec/support/failures.rb +9 -0
  100. data/spec/support/my_rack_app.rb +8 -3
  101. data/spec/support/network_connection.rb +7 -13
  102. data/spec/support/webmock_server.rb +8 -3
  103. data/spec/unit/api_spec.rb +175 -0
  104. data/spec/unit/errors_spec.rb +116 -19
  105. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +1 -1
  106. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +2 -2
  107. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  108. data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
  109. data/spec/unit/rack_response_spec.rb +54 -16
  110. data/spec/unit/request_body_diff_spec.rb +90 -0
  111. data/spec/unit/request_execution_verifier_spec.rb +147 -39
  112. data/spec/unit/request_pattern_spec.rb +462 -198
  113. data/spec/unit/request_registry_spec.rb +29 -9
  114. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  115. data/spec/unit/request_signature_spec.rb +91 -49
  116. data/spec/unit/request_stub_spec.rb +71 -70
  117. data/spec/unit/response_spec.rb +100 -81
  118. data/spec/unit/stub_registry_spec.rb +37 -20
  119. data/spec/unit/stub_request_snippet_spec.rb +51 -31
  120. data/spec/unit/util/hash_counter_spec.rb +6 -6
  121. data/spec/unit/util/hash_keys_stringifier_spec.rb +4 -4
  122. data/spec/unit/util/headers_spec.rb +4 -4
  123. data/spec/unit/util/json_spec.rb +29 -3
  124. data/spec/unit/util/query_mapper_spec.rb +157 -0
  125. data/spec/unit/util/uri_spec.rb +150 -36
  126. data/spec/unit/util/version_checker_spec.rb +15 -9
  127. data/spec/unit/webmock_spec.rb +57 -4
  128. data/test/http_request.rb +3 -3
  129. data/test/shared_test.rb +45 -13
  130. data/test/test_helper.rb +1 -1
  131. data/test/test_webmock.rb +6 -0
  132. data/webmock.gemspec +30 -11
  133. metadata +308 -199
  134. data/.rvmrc +0 -1
  135. data/.travis.yml +0 -11
  136. data/Guardfile +0 -24
  137. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_0_x.rb +0 -151
  138. 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
- OriginalHttpClient = ::HTTPClient unless const_defined?(:OriginalHttpClient)
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
- class WebMockHTTPClient < HTTPClient
47
+ REQUEST_RESPONSE_LOCK = Mutex.new
31
48
 
32
- def do_get_block_with_webmock(req, proxy, conn, &block)
33
- do_get_with_webmock(req, proxy, conn, false, &block)
49
+ def do_get_block(req, proxy, conn, &block)
50
+ do_get(req, proxy, conn, false, &block)
34
51
  end
35
52
 
36
- def do_get_stream_with_webmock(req, proxy, conn, &block)
37
- do_get_with_webmock(req, proxy, conn, true, &block)
53
+ def do_get_stream(req, proxy, conn, &block)
54
+ do_get(req, proxy, conn, true, &block)
38
55
  end
39
56
 
40
- def do_get_with_webmock(req, proxy, conn, stream = false, &block)
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
- {:lib => :httpclient}, request_signature, webmock_response)
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, &block)
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
- {:lib => :httpclient, :real_request => true}, request_signature,
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 do_request_async_with_webmock(method, uri, query, body, extheader)
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
- do_request_async_without_webmock(method, uri, query, body, extheader)
109
+ super
82
110
  else
83
111
  raise WebMock::NetConnectNotAllowedError.new(request_signature)
84
112
  end
85
113
  end
86
114
 
87
- alias_method :do_get_block_without_webmock, :do_get_block
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
- def build_webmock_response(httpclient_response)
113
- webmock_response = WebMock::Response.new
114
- webmock_response.status = [httpclient_response.status, httpclient_response.reason]
115
-
116
- webmock_response.headers = {}.tap do |hash|
117
- httpclient_response.header.all.each do |(key, value)|
118
- if hash.has_key?(key)
119
- hash[key] = Array(hash[key]) + [value]
120
- else
121
- hash[key] = value
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
- if httpclient_response.content.respond_to?(:read)
127
- webmock_response.body = httpclient_response.content.read
128
- body = HTTP::Message::Body.new
129
- body.init_response(StringIO.new(webmock_response.body))
130
- httpclient_response.body = body
131
- else
132
- webmock_response.body = httpclient_response.content
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
- def build_request_signature(req, reuse_existing = false)
138
- uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
139
- uri.query_values = req.header.request_query if req.header.request_query
140
- uri.port = req.header.request_uri.port
141
- uri = uri.omit(:userinfo)
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
- auth = www_auth.basic_auth
144
- auth.challenge(req.header.request_uri, nil)
185
+ signature
186
+ end
145
187
 
146
- @request_filter.each do |filter|
147
- filter.filter_request(req)
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
- headers = req.header.all.inject({}) do |hdrs, header|
151
- hdrs[header[0]] ||= []
152
- hdrs[header[0]] << header[1]
153
- hdrs
196
+ def webmock_request_signatures
197
+ @webmock_request_signatures ||= []
154
198
  end
155
199
 
156
- if (auth_cred = auth.get(req)) && auth.scheme == 'Basic'
157
- userinfo = WebMock::Util::Headers.decode_userinfo_from_header(auth_cred)
158
- userinfo = WebMock::Util::URI.encode_unsafe_chars_in_userinfo(userinfo)
159
- headers.reject! {|k,v| k =~ /[Aa]uthorization/ && v =~ /^Basic / } #we added it to url userinfo
160
- uri.userinfo = userinfo
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
- signature = WebMock::RequestSignature.new(
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
- # reuse a previous identical signature object if we stored one for later use
171
- if reuse_existing && previous_signature = previous_signature_for(signature)
172
- return previous_signature
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
- signature
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
- def webmock_responses
179
- @webmock_responses ||= Hash.new do |hash, request_signature|
180
- hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
181
- end
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
- def webmock_request_signatures
185
- @webmock_request_signatures ||= []
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
- def previous_signature_for(signature)
189
- return nil unless index = webmock_request_signatures.index(signature)
190
- webmock_request_signatures.delete_at(index)
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