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.
Files changed (84) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/CI.yml +37 -0
  3. data/CHANGELOG.md +416 -0
  4. data/Gemfile +1 -1
  5. data/README.md +157 -31
  6. data/Rakefile +12 -4
  7. data/lib/webmock/api.rb +12 -0
  8. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
  9. data/lib/webmock/http_lib_adapters/curb_adapter.rb +17 -3
  10. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
  11. data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -2
  12. data/lib/webmock/http_lib_adapters/http_rb/client.rb +4 -1
  13. data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
  14. data/lib/webmock/http_lib_adapters/http_rb/response.rb +24 -3
  15. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +6 -2
  16. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +2 -2
  17. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +28 -9
  18. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
  19. data/lib/webmock/http_lib_adapters/net_http.rb +54 -14
  20. data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
  21. data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -4
  22. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  23. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  24. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  25. data/lib/webmock/matchers/hash_including_matcher.rb +4 -23
  26. data/lib/webmock/rack_response.rb +1 -1
  27. data/lib/webmock/request_body_diff.rb +1 -1
  28. data/lib/webmock/request_execution_verifier.rb +2 -3
  29. data/lib/webmock/request_pattern.rb +108 -46
  30. data/lib/webmock/request_registry.rb +1 -1
  31. data/lib/webmock/request_signature.rb +1 -1
  32. data/lib/webmock/request_signature_snippet.rb +4 -4
  33. data/lib/webmock/response.rb +11 -5
  34. data/lib/webmock/rspec.rb +10 -3
  35. data/lib/webmock/stub_registry.rb +26 -11
  36. data/lib/webmock/stub_request_snippet.rb +10 -6
  37. data/lib/webmock/test_unit.rb +1 -3
  38. data/lib/webmock/util/hash_counter.rb +4 -4
  39. data/lib/webmock/util/headers.rb +17 -2
  40. data/lib/webmock/util/json.rb +1 -2
  41. data/lib/webmock/util/query_mapper.rb +9 -7
  42. data/lib/webmock/util/uri.rb +10 -10
  43. data/lib/webmock/util/values_stringifier.rb +20 -0
  44. data/lib/webmock/version.rb +1 -1
  45. data/lib/webmock/webmock.rb +10 -3
  46. data/lib/webmock.rb +53 -48
  47. data/minitest/webmock_spec.rb +2 -2
  48. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  49. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  50. data/spec/acceptance/curb/curb_spec.rb +33 -0
  51. data/spec/acceptance/em_http_request/em_http_request_spec.rb +56 -0
  52. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
  53. data/spec/acceptance/excon/excon_spec.rb +4 -2
  54. data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
  55. data/spec/acceptance/http_rb/http_rb_spec.rb +20 -0
  56. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +5 -2
  57. data/spec/acceptance/httpclient/httpclient_spec.rb +8 -1
  58. data/spec/acceptance/manticore/manticore_spec.rb +51 -0
  59. data/spec/acceptance/net_http/net_http_shared.rb +1 -1
  60. data/spec/acceptance/net_http/net_http_spec.rb +53 -1
  61. data/spec/acceptance/patron/patron_spec.rb +7 -0
  62. data/spec/acceptance/patron/patron_spec_helper.rb +3 -3
  63. data/spec/acceptance/shared/callbacks.rb +3 -2
  64. data/spec/acceptance/shared/request_expectations.rb +14 -0
  65. data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
  66. data/spec/acceptance/shared/stubbing_requests.rb +95 -0
  67. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +1 -1
  68. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
  69. data/spec/support/webmock_server.rb +1 -0
  70. data/spec/unit/api_spec.rb +103 -3
  71. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  72. data/spec/unit/request_execution_verifier_spec.rb +12 -12
  73. data/spec/unit/request_pattern_spec.rb +195 -49
  74. data/spec/unit/request_signature_snippet_spec.rb +2 -2
  75. data/spec/unit/response_spec.rb +22 -18
  76. data/spec/unit/stub_request_snippet_spec.rb +30 -10
  77. data/spec/unit/util/query_mapper_spec.rb +13 -0
  78. data/spec/unit/util/uri_spec.rb +74 -2
  79. data/spec/unit/webmock_spec.rb +54 -5
  80. data/test/shared_test.rb +15 -2
  81. data/test/test_webmock.rb +6 -0
  82. data/webmock.gemspec +11 -3
  83. metadata +66 -17
  84. data/.travis.yml +0 -20
@@ -411,6 +411,17 @@ unless RUBY_PLATFORM =~ /java/
411
411
  it_should_behave_like "Curb"
412
412
  include CurbSpecHelper::NamedHttp
413
413
 
414
+ it "should reset @webmock_method after each call" do
415
+ stub_request(:post, "www.example.com").with(body: "01234")
416
+ c = Curl::Easy.new
417
+ c.url = "http://www.example.com"
418
+ c.post_body = "01234"
419
+ c.http_post
420
+ expect {
421
+ c.perform
422
+ }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://www.example.com))
423
+ end
424
+
414
425
  it "should work with blank arguments for post" do
415
426
  stub_request(:post, "www.example.com").with(body: "01234")
416
427
  c = Curl::Easy.new
@@ -462,5 +473,27 @@ unless RUBY_PLATFORM =~ /java/
462
473
  it_should_behave_like "Curb"
463
474
  include CurbSpecHelper::ClassPerform
464
475
  end
476
+
477
+ describe "using #reset" do
478
+ before do
479
+ @curl = Curl::Easy.new
480
+ @curl.url = "http://example.com"
481
+ stub_request(:any, "example.com").
482
+ to_return(body: "abc",
483
+ headers: { "Content-Type" => "application/json" })
484
+ @curl.http_get
485
+ end
486
+
487
+ it "should clear all memoized response fields" do
488
+ @curl.reset
489
+ expect(@curl).to have_attributes(
490
+ body_str: nil,
491
+ content_type: nil,
492
+ header_str: nil,
493
+ last_effective_url: nil,
494
+ response_code: 0,
495
+ )
496
+ end
497
+ end
465
498
  end
466
499
  end
@@ -71,6 +71,35 @@ unless RUBY_PLATFORM =~ /java/
71
71
  end
72
72
  end
73
73
 
74
+ it "only calls request middleware once" do
75
+ stub_request(:get, "www.example.com")
76
+
77
+ middleware = Class.new do
78
+ def self.called!
79
+ @called = called + 1
80
+ end
81
+
82
+ def self.called
83
+ @called || 0
84
+ end
85
+
86
+ def request(client, head, body)
87
+ self.class.called!
88
+ [head, body]
89
+ end
90
+ end
91
+
92
+ EM.run do
93
+ conn = EventMachine::HttpRequest.new('http://www.example.com/')
94
+ conn.use middleware
95
+ http = conn.get
96
+ http.callback do
97
+ expect(middleware.called).to eq(1)
98
+ EM.stop
99
+ end
100
+ end
101
+ end
102
+
74
103
  let(:response_middleware) do
75
104
  Class.new do
76
105
  def response(resp)
@@ -119,6 +148,33 @@ unless RUBY_PLATFORM =~ /java/
119
148
  context 'making a real request', net_connect: true do
120
149
  before { WebMock.allow_net_connect! }
121
150
  include_examples "em-http-request middleware/after_request hook integration"
151
+
152
+ it "only calls request middleware once" do
153
+ middleware = Class.new do
154
+ def self.called!
155
+ @called = called + 1
156
+ end
157
+
158
+ def self.called
159
+ @called || 0
160
+ end
161
+
162
+ def request(client, head, body)
163
+ self.class.called!
164
+ [head, body]
165
+ end
166
+ end
167
+
168
+ EM.run do
169
+ conn = EventMachine::HttpRequest.new(webmock_server_url)
170
+ conn.use middleware
171
+ http = conn.get
172
+ http.callback do
173
+ expect(middleware.called).to eq(1)
174
+ EM.stop
175
+ end
176
+ end
177
+ end
122
178
  end
123
179
 
124
180
  context 'when the request is stubbed' do
@@ -50,7 +50,7 @@ module EMHttpRequestSpecHelper
50
50
  end
51
51
 
52
52
  def client_timeout_exception_class
53
- "WebMock timeout error"
53
+ 'Errno::ETIMEDOUT'
54
54
  end
55
55
 
56
56
  def connection_refused_exception_class
@@ -20,7 +20,8 @@ describe "Excon" do
20
20
  it "should support excon response_block for real requests", net_connect: true do
21
21
  a = []
22
22
  WebMock.allow_net_connect!
23
- r = Excon.new('http://httpstat.us/200').get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
23
+ r = Excon.new('http://httpstat.us/200', headers: { "Accept" => "*" }).
24
+ get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
24
25
  expect(a).to eq(["2", "0", "0", " ", "O", "K"])
25
26
  expect(r.body).to eq("")
26
27
  end
@@ -40,7 +41,8 @@ describe "Excon" do
40
41
  WebMock.after_request { |_, res|
41
42
  response = res
42
43
  }
43
- r = Excon.new('http://httpstat.us/200').get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
44
+ r = Excon.new('http://httpstat.us/200', headers: { "Accept" => "*" }).
45
+ get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
44
46
  expect(response.body).to eq("200 OK")
45
47
  expect(a).to eq(["2", "0", "0", " ", "O", "K"])
46
48
  expect(r.body).to eq("")
@@ -28,6 +28,8 @@ module ExconSpecHelper
28
28
  res
29
29
  end
30
30
 
31
+ Excon.set_raise_on_warnings!(true)
32
+
31
33
  OpenStruct.new \
32
34
  body: response.body,
33
35
  headers: headers,
@@ -70,4 +70,24 @@ describe "HTTP.rb" do
70
70
  expect(response.uri.to_s).to eq "http://example.com/foo"
71
71
  end
72
72
  end
73
+
74
+ context "streamer" do
75
+ it "can be read to a provided buffer" do
76
+ stub_request(:get, "example.com/foo")
77
+ .to_return(status: 200, body: "Hello world! ")
78
+ response = HTTP.get "http://example.com/foo"
79
+
80
+ buffer = ""
81
+ response.body.readpartial(1024, buffer)
82
+
83
+ expect(buffer).to eq "Hello world! "
84
+ end
85
+
86
+ it "can be closed" do
87
+ stub_request :get, "example.com/foo"
88
+ response = HTTP.get "http://example.com/foo"
89
+
90
+ response.connection.close
91
+ end
92
+ end
73
93
  end
@@ -8,7 +8,10 @@ module HttpRbSpecHelper
8
8
  chain = chain.basic_auth(user: basic_auth[0], pass: basic_auth[1])
9
9
  end
10
10
 
11
- response = chain.request(method, normalize_uri(uri), options)
11
+ ssl_ctx = OpenSSL::SSL::SSLContext.new
12
+ ssl_ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
13
+
14
+ response = chain.request(method, normalize_uri(uri), options.merge(ssl_context: ssl_ctx))
12
15
 
13
16
  OpenStruct.new({
14
17
  body: response.body.to_s,
@@ -20,7 +23,7 @@ module HttpRbSpecHelper
20
23
 
21
24
  def client_timeout_exception_class
22
25
  return Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0"
23
- HTTP::ConnectionError
26
+ HTTP::TimeoutError
24
27
  end
25
28
 
26
29
  def connection_refused_exception_class
@@ -31,6 +31,13 @@ describe "HTTPClient" do
31
31
  expect(response_body).to eq("abc")
32
32
  end
33
33
 
34
+ it "should not yield block on empty response if block provided" do
35
+ stub_request(:get, "www.example.com").to_return(body: "")
36
+ response_body = ""
37
+ http_request(:get, "http://www.example.com/"){ raise }
38
+ expect(response_body).to eq("")
39
+ end
40
+
34
41
  it "should match requests if headers are the same but in different order" do
35
42
  stub_request(:get, "www.example.com").with(headers: {"a" => ["b", "c"]} )
36
43
  expect(http_request(
@@ -48,7 +55,7 @@ describe "HTTPClient" do
48
55
 
49
56
  it "should work with get_content" do
50
57
  stub_request(:get, 'www.example.com').to_return(status: 200, body: 'test', headers: {})
51
- str = ''
58
+ str = ''.dup
52
59
  HTTPClient.get_content('www.example.com') do |content|
53
60
  str << content
54
61
  end
@@ -51,6 +51,57 @@ if RUBY_PLATFORM =~ /java/
51
51
  response = Manticore.head("http://example-foo.com")
52
52
  expect(response.code).to eq(204)
53
53
  end
54
+
55
+ context "when a custom failure handler is defined" do
56
+ let(:failure_handler) { proc {} }
57
+
58
+ before do
59
+ allow(failure_handler).to receive(:call).with(kind_of(Manticore::Timeout)) do |ex|
60
+ raise ex
61
+ end
62
+ end
63
+
64
+ it "handles timeouts by invoking the failure handler" do
65
+ stub_request(:get, "http://example-foo.com").to_timeout
66
+ request = Manticore.get("http://example-foo.com").tap do |req|
67
+ req.on_failure(&failure_handler)
68
+ end
69
+ expect { request.call }.to raise_error(Manticore::Timeout)
70
+ expect(failure_handler).to have_received(:call)
71
+ end
72
+ end
73
+
74
+ context 'when used in a streaming mode' do
75
+ let(:webmock_server_url) {"http://#{WebMockServer.instance.host_with_port}/"}
76
+ let(:result_chunks) { [] }
77
+
78
+ def manticore_streaming_get
79
+ Manticore.get(webmock_server_url).tap do |req|
80
+ req.on_success do |response|
81
+ response.body do |chunk|
82
+ result_chunks << chunk
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ context 'when connections are allowed' do
89
+ it 'works' do
90
+ WebMock.allow_net_connect!
91
+ expect { manticore_streaming_get.call }.to_not raise_error
92
+ expect(result_chunks).to_not be_empty
93
+ end
94
+ end
95
+
96
+ context 'when stubbed' do
97
+ it 'works' do
98
+ stub_body = 'hello!'
99
+ stub_request(:get, webmock_server_url).to_return(body: stub_body)
100
+ expect { manticore_streaming_get.call }.to_not raise_error
101
+ expect(result_chunks).to eq [stub_body]
102
+ end
103
+ end
104
+ end
54
105
  end
55
106
  end
56
107
  end
@@ -12,7 +12,7 @@ shared_examples_for "Net::HTTP" do
12
12
  end
13
13
 
14
14
  it "should handle requests with block passed to read_body", net_connect: true do
15
- body = ""
15
+ body = "".dup
16
16
  req = Net::HTTP::Get.new("/")
17
17
  Net::HTTP.start("localhost", port) do |http|
18
18
  http.request(req) do |res|
@@ -201,6 +201,18 @@ describe "Net:HTTP" do
201
201
  expect(Net::HTTP.get_response(Addressable::URI.parse('http://www.example.com/hello?a=1')).body).to eq("abc")
202
202
  end
203
203
 
204
+ it "should support method calls on stubbed socket" do
205
+ WebMock.allow_net_connect!
206
+ stub_request(:get, 'www.google.com')#.with(headers: {"My-Header" => 99})
207
+ req = Net::HTTP::Get.new('/')
208
+ Net::HTTP.start('www.google.com') do |http|
209
+ http.request(req, '')
210
+ socket = http.instance_variable_get(:@socket)
211
+ expect(socket).to be_a(StubSocket)
212
+ expect { socket.io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) }.to_not raise_error
213
+ end
214
+ end
215
+
204
216
  describe "connecting on Net::HTTP.start" do
205
217
  before(:each) do
206
218
  @http = Net::HTTP.new('www.google.com', 443)
@@ -293,7 +305,7 @@ describe "Net:HTTP" do
293
305
  end
294
306
 
295
307
  it "should support the after_request callback on an request with block and read_body" do
296
- response_body = ''
308
+ response_body = ''.dup
297
309
  http_request(:get, "http://localhost:#{port}/") do |response|
298
310
  response.read_body { |fragment| response_body << fragment }
299
311
  end
@@ -314,4 +326,44 @@ describe "Net:HTTP" do
314
326
  expect(@callback_invocation_count).to eq(1)
315
327
  end
316
328
  end
329
+
330
+ it "should match http headers, even if their values have been set in a request as numbers" do
331
+ WebMock.disable_net_connect!
332
+
333
+ stub_request(:post, "www.example.com").with(headers: {"My-Header" => 99})
334
+
335
+ uri = URI.parse('http://www.example.com/')
336
+ req = Net::HTTP::Post.new(uri.path)
337
+ req['My-Header'] = 99
338
+
339
+ res = Net::HTTP.start(uri.host, uri.port) do |http|
340
+ http.request(req, '')
341
+ end
342
+ end
343
+
344
+ describe "hostname handling" do
345
+ it "should set brackets around the hostname if it is an IPv6 address" do
346
+ net_http = Net::HTTP.new('b2dc:5bdf:4f0d::3014:e0ca', 80)
347
+ path = '/example.jpg'
348
+ expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://[b2dc:5bdf:4f0d::3014:e0ca]:80/example.jpg')
349
+ end
350
+
351
+ it "should not set brackets around the hostname if it is already wrapped by brackets" do
352
+ net_http = Net::HTTP.new('[b2dc:5bdf:4f0d::3014:e0ca]', 80)
353
+ path = '/example.jpg'
354
+ expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://[b2dc:5bdf:4f0d::3014:e0ca]:80/example.jpg')
355
+ end
356
+
357
+ it "should not set brackets around the hostname if it is an IPv4 address" do
358
+ net_http = Net::HTTP.new('181.152.137.168', 80)
359
+ path = '/example.jpg'
360
+ expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://181.152.137.168:80/example.jpg')
361
+ end
362
+
363
+ it "should not set brackets around the hostname if it is a domain" do
364
+ net_http = Net::HTTP.new('www.example.com', 80)
365
+ path = '/example.jpg'
366
+ expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://www.example.com:80/example.jpg')
367
+ end
368
+ end
317
369
  end
@@ -19,6 +19,13 @@ unless RUBY_PLATFORM =~ /java/
19
19
  @sess.base_url = "http://www.example.com"
20
20
  end
21
21
 
22
+ it "should allow stubbing PATCH request with body" do
23
+ stub_request(:patch, "http://www.example.com/")
24
+ .with(body: "abc")
25
+
26
+ @sess.patch('/', "abc")
27
+ end
28
+
22
29
  describe "file requests" do
23
30
 
24
31
  before(:each) do
@@ -16,7 +16,7 @@ module PatronSpecHelper
16
16
  sess.timeout = 30
17
17
  sess.max_redirects = 0
18
18
  uri = "#{uri.path}#{uri.query ? '?' : ''}#{uri.query}"
19
- uri.gsub!(' ','%20')
19
+ uri = uri.gsub(' ','%20')
20
20
  response = sess.request(method, uri, options[:headers] || {}, {
21
21
  data: options[:body]
22
22
  })
@@ -28,8 +28,8 @@ module PatronSpecHelper
28
28
  end
29
29
  end
30
30
 
31
- status_line_pattern = %r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?)
32
- message = response.status_line.match(status_line_pattern)[3] || ""
31
+ status_line_pattern = %r(\AHTTP/(\d+(\.\d+)?)\s+(\d\d\d)\s*([^\r\n]+)?)
32
+ message = response.status_line.match(status_line_pattern)[4] || ""
33
33
 
34
34
  OpenStruct.new({
35
35
  body: response.body,
@@ -102,7 +102,7 @@ shared_context "callbacks" do |*adapter_info|
102
102
  WebMock.after_request(except: [:other_lib]) do |_, response|
103
103
  @response = response
104
104
  end
105
- http_request(:get, "http://httpstat.us/201")
105
+ http_request(:get, "http://httpstat.us/201", headers: { "Accept" => "*" })
106
106
  end
107
107
 
108
108
  it "should pass real response to callback with status and message" do
@@ -111,7 +111,8 @@ shared_context "callbacks" do |*adapter_info|
111
111
  end
112
112
 
113
113
  it "should pass real response to callback with headers" do
114
- expect(@response.headers["Content-Length"]).to eq("11")
114
+ expect(@response.headers["X-Powered-By"]).to eq( "ASP.NET")
115
+ expect(@response.headers["Content-Length"]).to eq("11") unless adapter_info.include?(:no_content_length_header)
115
116
  end
116
117
 
117
118
  it "should pass response to callback with body" do
@@ -165,6 +165,20 @@ shared_context "request expectations" do |*adapter_info|
165
165
  expect(a_request(:get, "www.example.com").with(query: hash_including({"a" => ["b", "c"]}))).to have_been_made
166
166
  }.not_to raise_error
167
167
  end
168
+
169
+ it 'should satisfy expectation if the request was executed with excluding part of query params declared as a hash in a query option' do
170
+ expect {
171
+ http_request(:get, "http://www.example.com/?a[]=d&b[]=e&b=1")
172
+ expect(a_request(:get, "www.example.com").with(query: hash_excluding(a: ['b', 'c']))).to have_been_made
173
+ }.not_to raise_error
174
+ end
175
+
176
+ it 'should satisfy expectation if the request was executed with an empty array in the query params' do
177
+ expect {
178
+ http_request(:get, "http://www.example.com/?a[]")
179
+ expect(a_request(:get, "www.example.com").with(query: hash_including(a: []))).to have_been_made
180
+ }.not_to raise_error
181
+ end
168
182
  end
169
183
 
170
184
  context "when using flat array notation" do
@@ -64,7 +64,10 @@ shared_context "declared responses" do |*adapter_info|
64
64
  it "should return response with declared headers" do
65
65
  stub_request(:get, "www.example.com").to_return(headers: SAMPLE_HEADERS)
66
66
  response = http_request(:get, "http://www.example.com/")
67
- expect(response.headers["Content-Length"]).to eq("8888")
67
+ expect(response.headers["Accept"]).to eq("application/json")
68
+ unless adapter_info.include?(:no_content_length_header)
69
+ expect(response.headers["Content-Length"]).to eq("8888")
70
+ end
68
71
  end
69
72
 
70
73
  it "should return response with declared headers even if there are multiple headers with the same key" do
@@ -171,13 +174,22 @@ shared_context "declared responses" do |*adapter_info|
171
174
  end
172
175
 
173
176
  it "should return recorded headers" do
174
- expect(@response.headers).to eq({
175
- "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
176
- "Content-Type"=>"text/html; charset=UTF-8",
177
- "Content-Length"=>"419",
178
- "Connection"=>"Keep-Alive",
179
- "Accept"=>"image/jpeg, image/png"
180
- })
177
+ if adapter_info.include?(:no_content_length_header)
178
+ expect(@response.headers).to eq({
179
+ "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
180
+ "Content-Type"=>"text/html; charset=UTF-8",
181
+ "Connection"=>"Keep-Alive",
182
+ "Accept"=>"image/jpeg, image/png"
183
+ })
184
+ else
185
+ expect(@response.headers).to eq({
186
+ "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
187
+ "Content-Type"=>"text/html; charset=UTF-8",
188
+ "Content-Length"=>"419",
189
+ "Connection"=>"Keep-Alive",
190
+ "Accept"=>"image/jpeg, image/png"
191
+ })
192
+ end
181
193
  end
182
194
 
183
195
  it "should return recorded body" do
@@ -205,13 +217,22 @@ shared_context "declared responses" do |*adapter_info|
205
217
  end
206
218
 
207
219
  it "should return recorded headers" do
208
- expect(@response.headers).to eq({
209
- "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
210
- "Content-Type"=>"text/html; charset=UTF-8",
211
- "Content-Length"=>"419",
212
- "Connection"=>"Keep-Alive",
213
- "Accept"=>"image/jpeg, image/png"
214
- })
220
+ if adapter_info.include?(:no_content_length_header)
221
+ expect(@response.headers).to eq({
222
+ "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
223
+ "Content-Type"=>"text/html; charset=UTF-8",
224
+ "Connection"=>"Keep-Alive",
225
+ "Accept"=>"image/jpeg, image/png"
226
+ })
227
+ else
228
+ expect(@response.headers).to eq({
229
+ "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
230
+ "Content-Type"=>"text/html; charset=UTF-8",
231
+ "Content-Length"=>"419",
232
+ "Connection"=>"Keep-Alive",
233
+ "Accept"=>"image/jpeg, image/png"
234
+ })
235
+ end
215
236
  end
216
237
 
217
238
  it "should return recorded body" do
@@ -63,6 +63,16 @@ shared_examples_for "stubbing requests" do |*adapter_info|
63
63
  stub_request(:get, "www.example.com").with(query: hash_including({"a" => ["b", "c"]})).to_return(body: "abc")
64
64
  expect(http_request(:get, "http://www.example.com/?a[]=b&a[]=c&b=1").body).to eq("abc")
65
65
  end
66
+
67
+ it 'should return stubbed response when stub expects exclude part of query params' do
68
+ stub_request(:get, 'www.example.com').with(query: hash_excluding(a: ['b', 'c'])).to_return(body: 'abc')
69
+ expect(http_request(:get, 'http://www.example.com/?a[]=c&a[]=d&b=1').body).to eq('abc')
70
+ end
71
+
72
+ it "should return stubbed response when stub expects an empty array" do
73
+ stub_request(:get, 'www.example.com').with(query: { a: [] }).to_return(body: 'abc')
74
+ expect(http_request(:get, 'http://www.example.com/?a[]').body).to eq('abc')
75
+ end
66
76
  end
67
77
 
68
78
  describe "based on method" do
@@ -150,6 +160,32 @@ shared_examples_for "stubbing requests" do |*adapter_info|
150
160
  body: 'c[d][]=f&a=1&c[d][]=e')
151
161
  }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: POST http://www.example.com/ with body 'c\[d\]\[\]=f&a=1&c\[d\]\[\]=e'))
152
162
  end
163
+
164
+ describe "for request with form url encoded body and content type" do
165
+ it "should match if stubbed request body hash has string values matching string values in request body" do
166
+ WebMock.reset!
167
+ stub_request(:post, "www.example.com").with(body: {"foo" => '1'})
168
+ expect(http_request(
169
+ :post, "http://www.example.com/", headers: {'Content-Type' => 'application/x-www-form-urlencoded'},
170
+ body: "foo=1").status).to eq("200")
171
+ end
172
+
173
+ it "should match if stubbed request body hash has NON string values matching string values in request body" do
174
+ WebMock.reset!
175
+ stub_request(:post, "www.example.com").with(body: {"foo" => 1})
176
+ expect(http_request(
177
+ :post, "http://www.example.com/", headers: {'Content-Type' => 'application/x-www-form-urlencoded'},
178
+ body: "foo=1").status).to eq("200")
179
+ end
180
+
181
+ it "should match if stubbed request body is hash_included" do
182
+ WebMock.reset!
183
+ stub_request(:post, "www.example.com").with(body: {"foo" => hash_including("bar" => '1')})
184
+ expect(http_request(
185
+ :post, "http://www.example.com/", headers: {'Content-Type' => 'application/x-www-form-urlencoded'},
186
+ body: "foo[bar]=1").status).to eq("200")
187
+ end
188
+ end
153
189
  end
154
190
 
155
191
 
@@ -182,6 +218,30 @@ shared_examples_for "stubbing requests" do |*adapter_info|
182
218
  :post, "http://www.example.com/", headers: {'Content-Type' => 'application/json'},
183
219
  body: "{\"foo\":\"a b c\"}").status).to eq("200")
184
220
  end
221
+
222
+ it "should match if stubbed request body hash has NON string values matching NON string values in request body" do
223
+ WebMock.reset!
224
+ stub_request(:post, "www.example.com").with(body: {"foo" => 1})
225
+ expect(http_request(
226
+ :post, "http://www.example.com/", headers: {'Content-Type' => 'application/json'},
227
+ body: "{\"foo\":1}").status).to eq("200")
228
+ end
229
+
230
+ it "should not match if stubbed request body hash has string values matching NON string values in request body" do
231
+ WebMock.reset!
232
+ stub_request(:post, "www.example.com").with(body: {"foo" => '1'})
233
+ expect{http_request(
234
+ :post, "http://www.example.com/", headers: {'Content-Type' => 'application/json'},
235
+ body: "{\"foo\":1}") }.to raise_error(WebMock::NetConnectNotAllowedError)
236
+ end
237
+
238
+ it "should not match if stubbed request body hash has NON string values matching string values in request body" do
239
+ WebMock.reset!
240
+ stub_request(:post, "www.example.com").with(body: {"foo" => 1})
241
+ expect{http_request(
242
+ :post, "http://www.example.com/", headers: {'Content-Type' => 'application/json'},
243
+ body: "{\"foo\":\"1\"}") }.to raise_error(WebMock::NetConnectNotAllowedError)
244
+ end
185
245
  end
186
246
 
187
247
  describe "for request with xml body and content type is set to xml" do
@@ -533,6 +593,23 @@ shared_examples_for "stubbing requests" do |*adapter_info|
533
593
  end
534
594
  end
535
595
  end
596
+
597
+ context "when global stub should be invoked last" do
598
+ before do
599
+ WebMock.globally_stub_request(:after_local_stubs) do
600
+ { body: "global stub body" }
601
+ end
602
+ end
603
+
604
+ it "uses global stub when non-global stub is not defined" do
605
+ expect(http_request(:get, "http://www.example.com/").body).to eq("global stub body")
606
+ end
607
+
608
+ it "uses non-global stub first" do
609
+ stub_request(:get, "www.example.com").to_return(body: 'non-global stub body')
610
+ expect(http_request(:get, "http://www.example.com/").body).to eq("non-global stub body")
611
+ end
612
+ end
536
613
  end
537
614
 
538
615
  describe "when stubbing request with a block evaluated on request" do
@@ -580,4 +657,22 @@ shared_examples_for "stubbing requests" do |*adapter_info|
580
657
  }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://www.example.com/))
581
658
  end
582
659
  end
660
+
661
+ describe "in Rspec around(:each) hook" do
662
+ # order goes
663
+ # around(:each)
664
+ # before(:each)
665
+ # after(:each)
666
+ # anything after example.run in around(:each)
667
+ around(:each) do |example|
668
+ example.run
669
+ expect {
670
+ http_request(:get, "http://www.example.com/")
671
+ }.to_not raise_error # WebMock::NetConnectNotAllowedError
672
+ end
673
+
674
+ it "should still allow me to make a mocked request" do
675
+ stub_request(:get, "www.example.com")
676
+ end
677
+ end
583
678
  end
@@ -127,7 +127,7 @@ unless RUBY_PLATFORM =~ /java/
127
127
  end
128
128
  hydra.queue @request
129
129
  hydra.run
130
- expect(test_headers).to include('X-Test' => '1')
130
+ expect(test_headers.to_h).to include('X-Test' => '1')
131
131
  end
132
132
  end
133
133
  end
@@ -6,7 +6,7 @@ module TyphoeusHydraSpecHelper
6
6
 
7
7
 
8
8
  def http_request(method, uri, options = {}, &block)
9
- uri.gsub!(" ", "%20") #typhoeus doesn't like spaces in the uri
9
+ uri = uri.gsub(" ", "%20") #typhoeus doesn't like spaces in the uri
10
10
  request_options = {
11
11
  method: method,
12
12
  body: options[:body],