webmock 3.14.0 → 3.18.1

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/CI.yml +1 -0
  3. data/CHANGELOG.md +86 -2
  4. data/README.md +12 -3
  5. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +6 -1
  6. data/lib/webmock/http_lib_adapters/curb_adapter.rb +2 -2
  7. data/lib/webmock/http_lib_adapters/http_rb/client.rb +1 -3
  8. data/lib/webmock/http_lib_adapters/http_rb/response.rb +4 -1
  9. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +4 -2
  10. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +6 -2
  11. data/lib/webmock/http_lib_adapters/net_http.rb +22 -115
  12. data/lib/webmock/request_pattern.rb +23 -7
  13. data/lib/webmock/request_signature.rb +2 -2
  14. data/lib/webmock/request_stub.rb +15 -0
  15. data/lib/webmock/response.rb +8 -8
  16. data/lib/webmock/version.rb +1 -1
  17. data/lib/webmock/webmock.rb +10 -0
  18. data/minitest/webmock_spec.rb +1 -1
  19. data/spec/acceptance/async_http_client/async_http_client_spec.rb +5 -5
  20. data/spec/acceptance/curb/curb_spec.rb +11 -0
  21. data/spec/acceptance/em_http_request/em_http_request_spec.rb +1 -1
  22. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
  23. data/spec/acceptance/excon/excon_spec.rb +2 -2
  24. data/spec/acceptance/net_http/net_http_shared.rb +46 -9
  25. data/spec/acceptance/net_http/net_http_spec.rb +49 -23
  26. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  27. data/spec/acceptance/patron/patron_spec.rb +19 -21
  28. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +14 -14
  29. data/spec/acceptance/shared/callbacks.rb +2 -2
  30. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +1 -1
  31. data/spec/unit/request_pattern_spec.rb +12 -0
  32. data/spec/unit/request_signature_spec.rb +21 -1
  33. data/spec/unit/request_stub_spec.rb +35 -0
  34. data/spec/unit/response_spec.rb +29 -1
  35. data/spec/unit/webmock_spec.rb +54 -0
  36. data/webmock.gemspec +4 -4
  37. metadata +29 -29
@@ -248,7 +248,7 @@ unless RUBY_PLATFORM =~ /java/
248
248
  end
249
249
 
250
250
  context 'multiple requests' do
251
- let(:endpoint) { Async::HTTP::Endpoint.parse('http://www.example.com') }
251
+ let!(:endpoint) { Async::HTTP::Endpoint.parse('http://www.example.com') }
252
252
  let(:requests_count) { 3 }
253
253
 
254
254
  shared_examples :common do
@@ -300,13 +300,13 @@ unless RUBY_PLATFORM =~ /java/
300
300
  end
301
301
 
302
302
  context 'HTTP1 protocol' do
303
- let(:protocol) { Async::HTTP::Protocol::HTTP1 }
303
+ let!(:protocol) { Async::HTTP::Protocol::HTTP1 }
304
304
 
305
305
  include_examples :common
306
306
  end
307
307
 
308
308
  context 'HTTP2 protocol' do
309
- let(:protocol) { Async::HTTP::Protocol::HTTP2 }
309
+ let!(:protocol) { Async::HTTP::Protocol::HTTP2 }
310
310
 
311
311
  include_examples :common
312
312
  end
@@ -331,13 +331,13 @@ unless RUBY_PLATFORM =~ /java/
331
331
  end
332
332
 
333
333
  context 'HTTP1 protocol' do
334
- let(:protocol) { Async::HTTP::Protocol::HTTP1 }
334
+ let!(:protocol) { Async::HTTP::Protocol::HTTP1 }
335
335
 
336
336
  include_examples :common
337
337
  end
338
338
 
339
339
  context 'HTTP2 protocol' do
340
- let(:protocol) { Async::HTTP::Protocol::HTTP2 }
340
+ let!(:protocol) { Async::HTTP::Protocol::HTTP2 }
341
341
 
342
342
  include_examples :common
343
343
  end
@@ -383,6 +383,17 @@ unless RUBY_PLATFORM =~ /java/
383
383
  expect(c.body).to eq("abc")
384
384
  end
385
385
 
386
+ it "supports headers containing the ':' character" do
387
+ stub_request(:get, "www.example.com").with(headers: {'Referer'=>'http://www.example.com'}).to_return(body: "abc")
388
+
389
+ c = Curl::Easy.new
390
+ c.url = "http://www.example.com"
391
+ c.headers = ["Referer: http://www.example.com"]
392
+ c.http(:GET)
393
+ expect(c.body).to eq("abc")
394
+ expect(c.headers).to eq(["Referer: http://www.example.com"])
395
+ end
396
+
386
397
  describe 'match request body' do
387
398
  it 'for post' do
388
399
  stub_request(:post, "www.example.com").with(body: 'foo=nhe').to_return(body: "abc")
@@ -22,7 +22,7 @@ unless RUBY_PLATFORM =~ /java/
22
22
 
23
23
  def make_request
24
24
  EM.run do
25
- request = EM::HttpRequest.new(http_url).get(redirects: 1)
25
+ request = EM::HttpRequest.new(http_url, ssl: {verify_peer: true}).get(redirects: 1)
26
26
  request.callback { EM.stop }
27
27
  end
28
28
  end
@@ -16,7 +16,7 @@ module EMHttpRequestSpecHelper
16
16
  error_set = false
17
17
  uri = Addressable::URI.heuristic_parse(uri)
18
18
  EventMachine.run {
19
- request = EventMachine::HttpRequest.new("#{uri.normalize.to_s}")
19
+ request = EventMachine::HttpRequest.new("#{uri.normalize.to_s}", ssl: {verify_peer: true})
20
20
  http = request.send(method, {
21
21
  timeout: 30,
22
22
  body: options[:body],
@@ -20,7 +20,7 @@ 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', headers: { "Accept" => "*" }).
23
+ r = Excon.new('https://httpstat.us/200', headers: { "Accept" => "*" }).
24
24
  get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
25
25
  expect(a).to eq(["2", "0", "0", " ", "O", "K"])
26
26
  expect(r.body).to eq("")
@@ -41,7 +41,7 @@ describe "Excon" do
41
41
  WebMock.after_request { |_, res|
42
42
  response = res
43
43
  }
44
- r = Excon.new('http://httpstat.us/200', headers: { "Accept" => "*" }).
44
+ r = Excon.new('https://httpstat.us/200', headers: { "Accept" => "*" }).
45
45
  get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
46
46
  expect(response.body).to eq("200 OK")
47
47
  expect(a).to eq(["2", "0", "0", " ", "O", "K"])
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  shared_examples_for "Net::HTTP" do
2
4
  describe "when making real requests", net_connect: true do
3
5
  let(:port){ WebMockServer.instance.port }
@@ -26,21 +28,56 @@ shared_examples_for "Net::HTTP" do
26
28
 
27
29
  it "should connect only once when connected on start", net_connect: true do
28
30
  @http = Net::HTTP.new('localhost', port)
29
- socket_id_before_request = socket_id_after_request = nil
31
+ socket_before_request = socket_after_request = nil
30
32
  @http.start {|conn|
31
- socket_id_before_request = conn.instance_variable_get(:@socket).object_id
33
+ socket_before_request = conn.instance_variable_get(:@socket)
32
34
  conn.request(Net::HTTP::Get.new("/"))
33
- socket_id_after_request = conn.instance_variable_get(:@socket).object_id
35
+ socket_after_request = conn.instance_variable_get(:@socket)
34
36
  }
35
37
 
36
- if !defined?(WebMock::Config) || WebMock::Config.instance.net_http_connect_on_start
37
- expect(socket_id_before_request).not_to eq(nil.object_id)
38
- expect(socket_id_after_request).not_to eq(nil.object_id)
39
- expect(socket_id_after_request).to eq(socket_id_before_request)
38
+ if !defined?(WebMock::NetHTTPUtility) || WebMock::Config.instance.net_http_connect_on_start
39
+ expect(socket_before_request).to be_a(Net::BufferedIO)
40
+ expect(socket_after_request).to be_a(Net::BufferedIO)
41
+ expect(socket_after_request).to be(socket_before_request)
42
+ else
43
+ expect(socket_before_request).to be_a(StubSocket)
44
+ expect(socket_after_request).to be_a(Net::BufferedIO)
45
+ end
46
+ end
47
+
48
+ it "should allow sending multiple requests when persisted", net_connect: true do
49
+ @http = Net::HTTP.new('example.org')
50
+ @http.start
51
+ expect(@http.get("/")).to be_a(Net::HTTPSuccess)
52
+ expect(@http.get("/")).to be_a(Net::HTTPSuccess)
53
+ expect(@http.get("/")).to be_a(Net::HTTPSuccess)
54
+ @http.finish
55
+ end
56
+
57
+ it "should not leak file descriptors", net_connect: true do
58
+ sockets = Set.new
59
+
60
+ @http = Net::HTTP.new('example.org')
61
+ @http.start
62
+ sockets << @http.instance_variable_get(:@socket)
63
+ @http.get("/")
64
+ sockets << @http.instance_variable_get(:@socket)
65
+ @http.get("/")
66
+ sockets << @http.instance_variable_get(:@socket)
67
+ @http.get("/")
68
+ sockets << @http.instance_variable_get(:@socket)
69
+ @http.finish
70
+
71
+ if !defined?(WebMock::NetHTTPUtility) || WebMock.net_http_connect_on_start?(Addressable::URI.parse("http://example.com/"))
72
+ expect(sockets.length).to eq(1)
73
+ expect(sockets.to_a[0]).to be_a(Net::BufferedIO)
40
74
  else
41
- expect(socket_id_before_request).to eq(nil.object_id)
42
- expect(socket_id_after_request).not_to eq(nil.object_id)
75
+ expect(sockets.length).to eq(2)
76
+ expect(sockets.to_a[0]).to be_a(StubSocket)
77
+ expect(sockets.to_a[1]).to be_a(Net::BufferedIO)
43
78
  end
79
+
80
+ expect(sockets.all?(&:closed?)).to be(true)
44
81
  end
45
82
 
46
83
  it "should pass the read_timeout value on", net_connect: true do
@@ -115,33 +115,16 @@ describe "Net:HTTP" do
115
115
  expect(Net::HTTP.start("www.example.com") { |http| http.request(req)}.body).to eq("abc")
116
116
  end
117
117
 
118
- it "raises an ArgumentError if passed headers as symbols if RUBY_VERSION < 2.3.0" do
118
+ it "raises an ArgumentError if passed headers as symbols" do
119
119
  uri = URI.parse("http://google.com/")
120
120
  http = Net::HTTP.new(uri.host, uri.port)
121
121
  request = Net::HTTP::Get.new(uri.request_uri)
122
+ request[:InvalidHeaderSinceItsASymbol] = "this will not be valid"
122
123
 
123
- # Net::HTTP calls downcase on header keys assigned with []=
124
- # In Ruby 1.8.7 symbols do not respond to downcase
125
- #
126
- # Meaning you can not assign header keys as symbols in ruby 1.8.7 using []=
127
- if :symbol.respond_to?(:downcase)
128
- request[:InvalidHeaderSinceItsASymbol] = "this will not be valid"
129
- else
130
- request.instance_eval do
131
- @header = request.to_hash.merge({InvalidHeaderSinceItsASymbol: "this will not be valid"})
132
- end
133
- end
134
-
135
- if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.3.0')
136
- expect do
137
- http.request(request)
138
- end.to raise_error ArgumentError, "Net:HTTP does not accept headers as symbols"
139
- else
140
- stub_http_request(:get, "google.com").with(headers: { InvalidHeaderSinceItsASymbol: "this will not be valid" })
141
- expect do
142
- http.request(request)
143
- end.not_to raise_error
144
- end
124
+ stub_http_request(:get, "google.com").with(headers: { InvalidHeaderSinceItsASymbol: "this will not be valid" })
125
+ expect do
126
+ http.request(request)
127
+ end.not_to raise_error
145
128
  end
146
129
 
147
130
  it "should handle multiple values for the same response header" do
@@ -213,6 +196,22 @@ describe "Net:HTTP" do
213
196
  end
214
197
  end
215
198
 
199
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7.0')
200
+ it "uses the StubSocket to provide IP address" do
201
+ Net::HTTP.start("http://example.com") do |http|
202
+ expect(http.ipaddr).to eq("127.0.0.1")
203
+ end
204
+ end
205
+ end
206
+
207
+ it "defines common socket methods" do
208
+ Net::HTTP.start("http://example.com") do |http|
209
+ socket = http.instance_variable_get(:@socket)
210
+ expect(socket.io.ssl_version).to eq("TLSv1.3")
211
+ expect(socket.io.cipher).to eq(["TLS_AES_128_GCM_SHA256", "TLSv1.3", 128, 128])
212
+ end
213
+ end
214
+
216
215
  describe "connecting on Net::HTTP.start" do
217
216
  before(:each) do
218
217
  @http = Net::HTTP.new('www.google.com', 443)
@@ -254,6 +253,21 @@ describe "Net:HTTP" do
254
253
  }
255
254
  end
256
255
 
256
+ it "should connect to the server on start when allowlisted", net_connect: true do
257
+ WebMock.disable_net_connect!(allow: "www.google.com", net_http_connect_on_start: "www.google.com")
258
+ @http.start {|conn|
259
+ cert = OpenSSL::X509::Certificate.new conn.peer_cert
260
+ expect(cert).to be_a(OpenSSL::X509::Certificate)
261
+ }
262
+ end
263
+
264
+ it "should not connect to the server on start when not allowlisted", net_connect: true do
265
+ WebMock.disable_net_connect!(allow: "www.google.com", net_http_connect_on_start: "www.yahoo.com")
266
+ @http.start {|conn|
267
+ expect(conn.peer_cert).to be_nil
268
+ }
269
+ end
270
+
257
271
  it "should connect to the server if the URI matches an regex", net_connect: true do
258
272
  WebMock.disable_net_connect!(allow: /google.com/)
259
273
  Net::HTTP.get('www.google.com','/')
@@ -282,6 +296,13 @@ describe "Net:HTTP" do
282
296
  it_should_behave_like "Net::HTTP"
283
297
  end
284
298
 
299
+ describe "when net_http_connect_on_start is a specific host" do
300
+ before(:each) do
301
+ WebMock.allow_net_connect!(net_http_connect_on_start: "localhost")
302
+ end
303
+ it_should_behave_like "Net::HTTP"
304
+ end
305
+
285
306
  describe 'after_request callback support', net_connect: true do
286
307
  let(:expected_body_regex) { /hello world/ }
287
308
 
@@ -365,5 +386,10 @@ describe "Net:HTTP" do
365
386
  path = '/example.jpg'
366
387
  expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://www.example.com:80/example.jpg')
367
388
  end
389
+
390
+ it "does not require a path" do
391
+ net_http = Net::HTTP.new('www.example.com', 80)
392
+ expect(WebMock::NetHTTPUtility.get_uri(net_http)).to eq('http://www.example.com:80')
393
+ end
368
394
  end
369
395
  end
@@ -17,4 +17,4 @@ describe "Real Net:HTTP without webmock", without_webmock: true do
17
17
  end
18
18
 
19
19
  it_should_behave_like "Net::HTTP"
20
- end
20
+ end
@@ -93,31 +93,29 @@ unless RUBY_PLATFORM =~ /java/
93
93
  @sess.copy("/abc", "/def")
94
94
  end
95
95
 
96
- if /^1\.9/ === RUBY_VERSION
97
- describe "handling encoding same way as patron" do
98
- around(:each) do |example|
99
- @encoding = Encoding.default_internal
100
- Encoding.default_internal = "UTF-8"
101
- example.run
102
- Encoding.default_internal = @encoding
103
- end
96
+ describe "handling encoding same way as patron" do
97
+ around(:each) do |example|
98
+ @encoding = Encoding.default_internal
99
+ Encoding.default_internal = "UTF-8"
100
+ example.run
101
+ Encoding.default_internal = @encoding
102
+ end
104
103
 
105
- it "should not encode body with default encoding" do
106
- stub_request(:get, "www.example.com").
107
- to_return(body: "Øl")
104
+ it "should not encode body with default encoding" do
105
+ stub_request(:get, "www.example.com").
106
+ to_return(body: "Øl")
108
107
 
109
- expect(@sess.get("").body.encoding).to eq(Encoding::ASCII_8BIT)
110
- expect(@sess.get("").inspectable_body.encoding).to eq(Encoding::UTF_8)
111
- end
108
+ expect(@sess.get("").body.encoding).to eq(Encoding::ASCII_8BIT)
109
+ expect(@sess.get("").inspectable_body.encoding).to eq(Encoding::UTF_8)
110
+ end
112
111
 
113
- it "should not encode body to default internal" do
114
- stub_request(:get, "www.example.com").
115
- to_return(headers: {'Content-Type' => 'text/html; charset=iso-8859-1'},
116
- body: "Øl".encode("iso-8859-1"))
112
+ it "should not encode body to default internal" do
113
+ stub_request(:get, "www.example.com").
114
+ to_return(headers: {'Content-Type' => 'text/html; charset=iso-8859-1'},
115
+ body: "Øl".encode("iso-8859-1"))
117
116
 
118
- expect(@sess.get("").body.encoding).to eq(Encoding::ASCII_8BIT)
119
- expect(@sess.get("").decoded_body.encoding).to eq(Encoding.default_internal)
120
- end
117
+ expect(@sess.get("").body.encoding).to eq(Encoding::ASCII_8BIT)
118
+ expect(@sess.get("").decoded_body.encoding).to eq(Encoding.default_internal)
121
119
  end
122
120
  end
123
121
  end
@@ -91,7 +91,7 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
91
91
  describe "is not allowed, with exceptions" do
92
92
  describe "allowing by host string" do
93
93
  before :each do
94
- WebMock.disable_net_connect!(allow: 'httpstat.us')
94
+ WebMock.disable_net_connect!(allow: 'https://httpstat.us')
95
95
  end
96
96
 
97
97
  context "when the host is not allowed" do
@@ -109,13 +109,13 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
109
109
 
110
110
  context "when the host is allowed" do
111
111
  it "should return stubbed response if request was stubbed" do
112
- stub_request(:get, 'httpstat.us/200').to_return(body: "abc")
113
- expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc")
112
+ stub_request(:get, 'https://httpstat.us/200').to_return(body: "abc")
113
+ expect(http_request(:get, "https://httpstat.us/200").body).to eq("abc")
114
114
  end
115
115
 
116
116
  # WARNING: this makes a real HTTP request!
117
117
  it "should make a real request to allowed host", net_connect: true do
118
- expect(http_request(:get, "http://httpstat.us/200").status).to eq('200')
118
+ expect(http_request(:get, "https://httpstat.us/200").status).to eq('200')
119
119
  end
120
120
  end
121
121
  end
@@ -229,13 +229,13 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
229
229
 
230
230
  context "when the host is allowed" do
231
231
  it "should return stubbed response if request was stubbed" do
232
- stub_request(:get, 'httpstat.us/200').to_return(body: "abc")
233
- expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc")
232
+ stub_request(:get, 'https://httpstat.us/200').to_return(body: "abc")
233
+ expect(http_request(:get, "https://httpstat.us/200").body).to eq("abc")
234
234
  end
235
235
 
236
236
  # WARNING: this makes a real HTTP request!
237
237
  it "should make a real request to allowed host", net_connect: true do
238
- expect(http_request(:get, "http://httpstat.us/200").status).to eq('200')
238
+ expect(http_request(:get, "https://httpstat.us/200").status).to eq('200')
239
239
  end
240
240
 
241
241
  it "should make a real request if request is allowed by path regexp and url contains default port", net_connect: true do
@@ -266,20 +266,20 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
266
266
 
267
267
  context "when the host is allowed" do
268
268
  it "should return stubbed response if request was stubbed" do
269
- stub_request(:get, 'httpstat.us/200').to_return(body: "abc")
270
- expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc")
269
+ stub_request(:get, 'https://httpstat.us/200').to_return(body: "abc")
270
+ expect(http_request(:get, "https://httpstat.us/200").body).to eq("abc")
271
271
  end
272
272
 
273
273
  # WARNING: this makes a real HTTP request!
274
274
  it "should make a real request to allowed host", net_connect: true do
275
- expect(http_request(:get, "http://httpstat.us/200").status).to eq('200')
275
+ expect(http_request(:get, "https://httpstat.us/200").status).to eq('200')
276
276
  end
277
277
  end
278
278
  end
279
279
 
280
280
  describe "allowing by a list of the above" do
281
281
  before :each do
282
- WebMock.disable_net_connect!(allow: [lambda{|_| false }, %r{foobar}, 'httpstat.us'])
282
+ WebMock.disable_net_connect!(allow: [lambda{|_| false }, %r{foobar}, 'https://httpstat.us'])
283
283
  end
284
284
 
285
285
  context "when the host is not allowed" do
@@ -297,13 +297,13 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
297
297
 
298
298
  context "when the host is allowed" do
299
299
  it "should return stubbed response if request was stubbed" do
300
- stub_request(:get, 'httpstat.us/200').to_return(body: "abc")
301
- expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc")
300
+ stub_request(:get, 'https://httpstat.us/200').to_return(body: "abc")
301
+ expect(http_request(:get, "https://httpstat.us/200").body).to eq("abc")
302
302
  end
303
303
 
304
304
  # WARNING: this makes a real HTTP request!
305
305
  it "should make a real request to allowed host", net_connect: true do
306
- expect(http_request(:get, "http://httpstat.us/200").status).to eq('200')
306
+ expect(http_request(:get, "https://httpstat.us/200").status).to eq('200')
307
307
  end
308
308
  end
309
309
  end
@@ -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", headers: { "Accept" => "*" })
105
+ http_request(:get, "https://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,7 @@ 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["X-Powered-By"]).to eq( "ASP.NET")
114
+ expect(@response.headers["Server"]).to eq( "Kestrel")
115
115
  expect(@response.headers["Content-Length"]).to eq("11") unless adapter_info.include?(:no_content_length_header)
116
116
  end
117
117
 
@@ -18,7 +18,7 @@ shared_context "complex cross-concern behaviors" do |*adapter_info|
18
18
  expect(played_back_response).to eq(real_response)
19
19
  end
20
20
 
21
- let(:no_content_url) { 'http://httpstat.us/204' }
21
+ let(:no_content_url) { 'https://httpstat.us/204' }
22
22
  [nil, ''].each do |stub_val|
23
23
  it "returns the same value (nil or "") for a request stubbed as #{stub_val.inspect} that a real empty response has", net_connect: true do
24
24
  unless http_library == :curb
@@ -547,6 +547,18 @@ describe WebMock::RequestPattern do
547
547
  body: "{\"a\":\"1\",\"b\":\"five\",\"c\":{\"d\":[\"e\",\"f\"]}}"))
548
548
  end
549
549
 
550
+ it "should match if the request body has a top level array" do
551
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: [{a: 1}])).
552
+ to match(WebMock::RequestSignature.new(:post, "www.example.com",
553
+ headers: {content_type: content_type}, body: "[{\"a\":1}]"))
554
+ end
555
+
556
+ it "should not match if the request body has a different top level array" do
557
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: ["a", "b"])).
558
+ not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
559
+ headers: {content_type: content_type}, body: "[\"a\", \"c\"]"))
560
+ end
561
+
550
562
  it "should not match when body is not json" do
551
563
  expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
552
564
  not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
@@ -18,7 +18,7 @@ describe WebMock::RequestSignature do
18
18
  end
19
19
 
20
20
  it "assigns normalized headers" do
21
- expect(WebMock::Util::Headers).to receive(:normalize_headers).with('A' => 'a').and_return('B' => 'b')
21
+ allow(WebMock::Util::Headers).to receive(:normalize_headers).with({'A' => 'a'}.freeze).and_return('B' => 'b')
22
22
  expect(
23
23
  WebMock::RequestSignature.new(:get, "www.example.com", headers: {'A' => 'a'}).headers
24
24
  ).to eq({'B' => 'b'})
@@ -125,11 +125,21 @@ describe WebMock::RequestSignature do
125
125
  expect(subject.url_encoded?).to be true
126
126
  end
127
127
 
128
+ it "returns true if the headers are urlencoded with a specified charset" do
129
+ subject.headers = { "Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8" }
130
+ expect(subject.url_encoded?).to be true
131
+ end
132
+
128
133
  it "returns false if the headers are NOT urlencoded" do
129
134
  subject.headers = { "Content-Type" => "application/made-up-format" }
130
135
  expect(subject.url_encoded?).to be false
131
136
  end
132
137
 
138
+ it "returns false when no content type header is present" do
139
+ subject.headers = { "Some-Header" => "some-value" }
140
+ expect(subject.url_encoded?).to be false
141
+ end
142
+
133
143
  it "returns false when no headers are set" do
134
144
  subject.headers = nil
135
145
  expect(subject.url_encoded?).to be false
@@ -142,11 +152,21 @@ describe WebMock::RequestSignature do
142
152
  expect(subject.json_headers?).to be true
143
153
  end
144
154
 
155
+ it "returns true if the headers are json with a specified charset" do
156
+ subject.headers = { "Content-Type" => "application/json; charset=UTF-8" }
157
+ expect(subject.json_headers?).to be true
158
+ end
159
+
145
160
  it "returns false if the headers are NOT json" do
146
161
  subject.headers = { "Content-Type" => "application/made-up-format" }
147
162
  expect(subject.json_headers?).to be false
148
163
  end
149
164
 
165
+ it "returns false when no content type header is present" do
166
+ subject.headers = { "Some-Header" => "some-value" }
167
+ expect(subject.json_headers?).to be false
168
+ end
169
+
150
170
  it "returns false when no headers are set" do
151
171
  subject.headers = nil
152
172
  expect(subject.json_headers?).to be false
@@ -50,6 +50,41 @@ describe WebMock::RequestStub do
50
50
 
51
51
  end
52
52
 
53
+ describe "to_return_json" do
54
+
55
+ it "should raise if a block is given" do
56
+ expect {
57
+ @request_stub.to_return_json(body: "abc", status: 500) { puts "don't call me" }
58
+ }.to raise_error(ArgumentError, '#to_return_json does not support passing a block')
59
+ end
60
+
61
+ it "should assign responses normally" do
62
+ @request_stub.to_return_json([{body: "abc"}, {body: "def"}])
63
+ expect([@request_stub.response.body, @request_stub.response.body]).to eq(["abc", "def"])
64
+ end
65
+
66
+ it "should json-ify a Hash body" do
67
+ @request_stub.to_return_json(body: {abc: "def"}, status: 500)
68
+ expect(@request_stub.response.body).to eq({abc: "def"}.to_json)
69
+ expect(@request_stub.response.status).to eq([500, ""])
70
+ end
71
+
72
+ it "should apply the content_type header" do
73
+ @request_stub.to_return_json(body: {abc: "def"}, status: 500)
74
+ expect(@request_stub.response.headers).to eq({"Content-Type"=>"application/json"})
75
+ end
76
+
77
+ it "should preserve existing headers" do
78
+ @request_stub.to_return_json(headers: {"A" => "a"}, body: "")
79
+ expect(@request_stub.response.headers).to eq({"A"=>"a", "Content-Type"=>"application/json"})
80
+ end
81
+
82
+ it "should allow callsites to override content_type header" do
83
+ @request_stub.to_return_json(headers: {content_type: 'application/super-special-json'})
84
+ expect(@request_stub.response.headers).to eq({"Content-Type"=>"application/super-special-json"})
85
+ end
86
+ end
87
+
53
88
  describe "then" do
54
89
  it "should return stub without any modifications, acting as syntactic sugar" do
55
90
  expect(@request_stub.then).to eq(@request_stub)
@@ -31,7 +31,7 @@ describe WebMock::Response do
31
31
  end
32
32
 
33
33
  it "should report normalized headers" do
34
- expect(WebMock::Util::Headers).to receive(:normalize_headers).with('A' => 'a').and_return('B' => 'b')
34
+ allow(WebMock::Util::Headers).to receive(:normalize_headers).with({'A' => 'a'}.freeze).and_return('B' => 'b')
35
35
  @response = WebMock::Response.new(headers: {'A' => 'a'})
36
36
  expect(@response.headers).to eq({'B' => 'b'})
37
37
  end
@@ -142,6 +142,34 @@ describe WebMock::Response do
142
142
  end
143
143
 
144
144
  describe "from raw response" do
145
+ describe "when input is a StringIO" do
146
+ before(:each) do
147
+ @io = StringIO.new(File.read(CURL_EXAMPLE_OUTPUT_PATH))
148
+ @response = WebMock::Response.new(@io)
149
+ end
150
+
151
+ it "should read status" do
152
+ expect(@response.status).to eq([202, "OK"])
153
+ end
154
+
155
+ it "should read headers" do
156
+ expect(@response.headers).to eq(
157
+ "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
158
+ "Content-Type"=>"text/html; charset=UTF-8",
159
+ "Content-Length"=>"419",
160
+ "Connection"=>"Keep-Alive",
161
+ "Accept"=>"image/jpeg, image/png"
162
+ )
163
+ end
164
+
165
+ it "should read body" do
166
+ expect(@response.body.size).to eq(419)
167
+ end
168
+
169
+ it "should close IO" do
170
+ expect(@io).to be_closed
171
+ end
172
+ end
145
173
 
146
174
  describe "when input is IO" do
147
175
  before(:each) do