webmock 3.11.1 → 3.18.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/CI.yml +38 -0
- data/CHANGELOG.md +126 -2
- data/Gemfile +1 -1
- data/README.md +33 -16
- data/Rakefile +12 -2
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +9 -2
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +2 -2
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +6 -3
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +1 -3
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +17 -3
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +4 -2
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +6 -2
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +8 -1
- data/lib/webmock/http_lib_adapters/net_http.rb +29 -115
- data/lib/webmock/request_pattern.rb +30 -8
- data/lib/webmock/request_signature.rb +2 -2
- data/lib/webmock/request_stub.rb +15 -0
- data/lib/webmock/response.rb +8 -8
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +10 -0
- data/minitest/webmock_spec.rb +1 -1
- data/spec/acceptance/async_http_client/async_http_client_spec.rb +27 -5
- data/spec/acceptance/curb/curb_spec.rb +11 -0
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +57 -1
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
- data/spec/acceptance/excon/excon_spec.rb +2 -2
- data/spec/acceptance/manticore/manticore_spec.rb +32 -0
- data/spec/acceptance/net_http/net_http_shared.rb +46 -9
- data/spec/acceptance/net_http/net_http_spec.rb +75 -23
- data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
- data/spec/acceptance/patron/patron_spec.rb +19 -21
- data/spec/acceptance/patron/patron_spec_helper.rb +2 -2
- data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +14 -14
- data/spec/acceptance/shared/callbacks.rb +2 -2
- data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +1 -1
- data/spec/unit/request_pattern_spec.rb +82 -46
- data/spec/unit/request_signature_spec.rb +21 -1
- data/spec/unit/request_stub_spec.rb +35 -0
- data/spec/unit/response_spec.rb +29 -1
- data/spec/unit/webmock_spec.rb +54 -0
- data/webmock.gemspec +6 -5
- metadata +46 -32
- data/.travis.yml +0 -24
@@ -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
|
-
|
31
|
+
socket_before_request = socket_after_request = nil
|
30
32
|
@http.start {|conn|
|
31
|
-
|
33
|
+
socket_before_request = conn.instance_variable_get(:@socket)
|
32
34
|
conn.request(Net::HTTP::Get.new("/"))
|
33
|
-
|
35
|
+
socket_after_request = conn.instance_variable_get(:@socket)
|
34
36
|
}
|
35
37
|
|
36
|
-
if !defined?(WebMock::
|
37
|
-
expect(
|
38
|
-
expect(
|
39
|
-
expect(
|
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(
|
42
|
-
expect(
|
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
|
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
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
|
@@ -340,4 +361,35 @@ describe "Net:HTTP" do
|
|
340
361
|
http.request(req, '')
|
341
362
|
end
|
342
363
|
end
|
364
|
+
|
365
|
+
describe "hostname handling" do
|
366
|
+
it "should set brackets around the hostname if it is an IPv6 address" do
|
367
|
+
net_http = Net::HTTP.new('b2dc:5bdf:4f0d::3014:e0ca', 80)
|
368
|
+
path = '/example.jpg'
|
369
|
+
expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://[b2dc:5bdf:4f0d::3014:e0ca]:80/example.jpg')
|
370
|
+
end
|
371
|
+
|
372
|
+
it "should not set brackets around the hostname if it is already wrapped by brackets" do
|
373
|
+
net_http = Net::HTTP.new('[b2dc:5bdf:4f0d::3014:e0ca]', 80)
|
374
|
+
path = '/example.jpg'
|
375
|
+
expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://[b2dc:5bdf:4f0d::3014:e0ca]:80/example.jpg')
|
376
|
+
end
|
377
|
+
|
378
|
+
it "should not set brackets around the hostname if it is an IPv4 address" do
|
379
|
+
net_http = Net::HTTP.new('181.152.137.168', 80)
|
380
|
+
path = '/example.jpg'
|
381
|
+
expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://181.152.137.168:80/example.jpg')
|
382
|
+
end
|
383
|
+
|
384
|
+
it "should not set brackets around the hostname if it is a domain" do
|
385
|
+
net_http = Net::HTTP.new('www.example.com', 80)
|
386
|
+
path = '/example.jpg'
|
387
|
+
expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://www.example.com:80/example.jpg')
|
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
|
394
|
+
end
|
343
395
|
end
|
@@ -93,31 +93,29 @@ unless RUBY_PLATFORM =~ /java/
|
|
93
93
|
@sess.copy("/abc", "/def")
|
94
94
|
end
|
95
95
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
119
|
-
|
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
|
@@ -28,8 +28,8 @@ module PatronSpecHelper
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
status_line_pattern = %r(\AHTTP/(\d
|
32
|
-
message = response.status_line.match(status_line_pattern)[
|
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,
|
@@ -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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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["
|
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) { '
|
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
|
@@ -534,69 +534,105 @@ describe WebMock::RequestPattern do
|
|
534
534
|
end
|
535
535
|
|
536
536
|
describe "for request with json body and content type is set to json" do
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
537
|
+
shared_examples "a json body" do
|
538
|
+
it "should match when hash matches body" do
|
539
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
|
540
|
+
to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: content_type},
|
541
|
+
body: "{\"a\":\"1\",\"c\":{\"d\":[\"e\",\"f\"]},\"b\":\"five\"}"))
|
542
|
+
end
|
542
543
|
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
544
|
+
it "should match if hash matches body in different form" do
|
545
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
|
546
|
+
to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: content_type},
|
547
|
+
body: "{\"a\":\"1\",\"b\":\"five\",\"c\":{\"d\":[\"e\",\"f\"]}}"))
|
548
|
+
end
|
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
|
548
555
|
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
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
|
+
|
562
|
+
it "should not match when body is not json" do
|
563
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
|
564
|
+
not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
|
565
|
+
headers: {content_type: content_type}, body: "foo bar"))
|
566
|
+
end
|
567
|
+
|
568
|
+
it "should not match if request body is different" do
|
569
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: {a: 1, b: 2})).
|
570
|
+
not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
|
571
|
+
headers: {content_type: content_type}, body: "{\"a\":1,\"c\":null}"))
|
572
|
+
end
|
573
|
+
|
574
|
+
it "should not match if request body is has less params than pattern" do
|
575
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: {a: 1, b: 2})).
|
576
|
+
not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
|
577
|
+
headers: {content_type: content_type}, body: "{\"a\":1}"))
|
578
|
+
end
|
554
579
|
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
580
|
+
it "should not match if request body is has more params than pattern" do
|
581
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: {a: 1})).
|
582
|
+
not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
|
583
|
+
headers: {content_type: content_type}, body: "{\"a\":1,\"c\":1}"))
|
584
|
+
end
|
559
585
|
end
|
560
586
|
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
headers: {content_type: 'application/json'}, body: "{\"a\":1}"))
|
587
|
+
context "standard application/json" do
|
588
|
+
let(:content_type) { 'application/json' }
|
589
|
+
it_behaves_like "a json body"
|
565
590
|
end
|
566
591
|
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
headers: {content_type: 'application/json'}, body: "{\"a\":1,\"c\":1}"))
|
592
|
+
context "custom json content type" do
|
593
|
+
let(:content_type) { 'application/vnd.api+json' }
|
594
|
+
it_behaves_like "a json body"
|
571
595
|
end
|
572
596
|
end
|
573
597
|
|
574
598
|
describe "for request with xml body and content type is set to xml" do
|
575
599
|
let(:body_hash) { {"opt" => {:a => '1', :b => 'five', 'c' => {'d' => ['e', 'f']}}} }
|
576
600
|
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
601
|
+
shared_examples "a xml body" do
|
602
|
+
it "should match when hash matches body" do
|
603
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
|
604
|
+
to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: content_type},
|
605
|
+
body: "<opt a=\"1\" b=\"five\">\n <c>\n <d>e</d>\n <d>f</d>\n </c>\n</opt>\n"))
|
606
|
+
end
|
582
607
|
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
608
|
+
it "should match if hash matches body in different form" do
|
609
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
|
610
|
+
to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: content_type},
|
611
|
+
body: "<opt b=\"five\" a=\"1\">\n <c>\n <d>e</d>\n <d>f</d>\n </c>\n</opt>\n"))
|
612
|
+
end
|
588
613
|
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
614
|
+
it "should not match when body is not xml" do
|
615
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
|
616
|
+
not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
|
617
|
+
headers: {content_type: content_type}, body: "foo bar"))
|
618
|
+
end
|
594
619
|
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
620
|
+
it "matches when the content type include a charset" do
|
621
|
+
expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
|
622
|
+
to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: "#{content_type};charset=UTF-8"},
|
623
|
+
body: "<opt a=\"1\" b=\"five\">\n <c>\n <d>e</d>\n <d>f</d>\n </c>\n</opt>\n"))
|
624
|
+
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
context "standard application/xml" do
|
629
|
+
let(:content_type) { 'application/xml' }
|
630
|
+
it_behaves_like "a xml body"
|
631
|
+
end
|
599
632
|
|
633
|
+
context "custom xml content type" do
|
634
|
+
let(:content_type) { 'application/atom+xml' }
|
635
|
+
it_behaves_like "a xml body"
|
600
636
|
end
|
601
637
|
end
|
602
638
|
end
|
@@ -18,7 +18,7 @@ describe WebMock::RequestSignature do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
it "assigns normalized headers" do
|
21
|
-
|
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)
|