webmock 3.0.0 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
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],