webmock 3.0.1 → 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.
- checksums.yaml +5 -5
- data/.github/workflows/CI.yml +38 -0
- data/CHANGELOG.md +496 -2
- data/Gemfile +1 -1
- data/README.md +169 -34
- data/Rakefile +12 -4
- data/lib/webmock/api.rb +12 -0
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +221 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +19 -5
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -2
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +2 -1
- data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +27 -3
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +9 -3
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +7 -3
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +28 -9
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
- data/lib/webmock/http_lib_adapters/net_http.rb +36 -89
- data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
- data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -4
- data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
- data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
- data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
- data/lib/webmock/matchers/hash_including_matcher.rb +4 -23
- data/lib/webmock/rack_response.rb +1 -1
- data/lib/webmock/request_body_diff.rb +1 -1
- data/lib/webmock/request_execution_verifier.rb +2 -3
- data/lib/webmock/request_pattern.rb +129 -51
- data/lib/webmock/request_registry.rb +1 -1
- data/lib/webmock/request_signature.rb +3 -3
- data/lib/webmock/request_signature_snippet.rb +4 -4
- data/lib/webmock/request_stub.rb +15 -0
- data/lib/webmock/response.rb +19 -13
- data/lib/webmock/rspec.rb +10 -3
- data/lib/webmock/stub_registry.rb +26 -11
- data/lib/webmock/stub_request_snippet.rb +10 -6
- data/lib/webmock/test_unit.rb +1 -3
- data/lib/webmock/util/hash_counter.rb +3 -3
- data/lib/webmock/util/headers.rb +17 -2
- data/lib/webmock/util/json.rb +1 -2
- data/lib/webmock/util/query_mapper.rb +9 -7
- data/lib/webmock/util/uri.rb +10 -10
- data/lib/webmock/util/values_stringifier.rb +20 -0
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +20 -3
- data/lib/webmock.rb +53 -48
- data/minitest/webmock_spec.rb +3 -3
- data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
- data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
- data/spec/acceptance/curb/curb_spec.rb +44 -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 +2 -2
- data/spec/acceptance/excon/excon_spec.rb +4 -2
- data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
- data/spec/acceptance/http_rb/http_rb_spec.rb +20 -0
- data/spec/acceptance/http_rb/http_rb_spec_helper.rb +5 -2
- data/spec/acceptance/httpclient/httpclient_spec.rb +8 -1
- data/spec/acceptance/manticore/manticore_spec.rb +51 -0
- data/spec/acceptance/net_http/net_http_shared.rb +47 -10
- data/spec/acceptance/net_http/net_http_spec.rb +102 -24
- data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
- data/spec/acceptance/patron/patron_spec.rb +26 -21
- data/spec/acceptance/patron/patron_spec_helper.rb +3 -3
- data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +14 -14
- data/spec/acceptance/shared/callbacks.rb +3 -2
- data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +1 -1
- data/spec/acceptance/shared/request_expectations.rb +14 -0
- data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
- data/spec/acceptance/shared/stubbing_requests.rb +95 -0
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +1 -1
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
- data/spec/support/webmock_server.rb +1 -0
- data/spec/unit/api_spec.rb +103 -3
- data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
- data/spec/unit/request_execution_verifier_spec.rb +12 -12
- data/spec/unit/request_pattern_spec.rb +207 -49
- data/spec/unit/request_signature_snippet_spec.rb +2 -2
- data/spec/unit/request_signature_spec.rb +21 -1
- data/spec/unit/request_stub_spec.rb +35 -0
- data/spec/unit/response_spec.rb +51 -19
- data/spec/unit/stub_request_snippet_spec.rb +30 -10
- data/spec/unit/util/query_mapper_spec.rb +13 -0
- data/spec/unit/util/uri_spec.rb +74 -2
- data/spec/unit/webmock_spec.rb +108 -5
- data/test/shared_test.rb +15 -2
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +15 -7
- metadata +86 -37
- data/.travis.yml +0 -20
|
@@ -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
|
|
@@ -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 }
|
|
@@ -12,7 +14,7 @@ shared_examples_for "Net::HTTP" do
|
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
it "should handle requests with block passed to read_body", net_connect: true do
|
|
15
|
-
body = ""
|
|
17
|
+
body = "".dup
|
|
16
18
|
req = Net::HTTP::Get.new("/")
|
|
17
19
|
Net::HTTP.start("localhost", port) do |http|
|
|
18
20
|
http.request(req) do |res|
|
|
@@ -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
|
|
@@ -201,6 +184,34 @@ describe "Net:HTTP" do
|
|
|
201
184
|
expect(Net::HTTP.get_response(Addressable::URI.parse('http://www.example.com/hello?a=1')).body).to eq("abc")
|
|
202
185
|
end
|
|
203
186
|
|
|
187
|
+
it "should support method calls on stubbed socket" do
|
|
188
|
+
WebMock.allow_net_connect!
|
|
189
|
+
stub_request(:get, 'www.google.com')#.with(headers: {"My-Header" => 99})
|
|
190
|
+
req = Net::HTTP::Get.new('/')
|
|
191
|
+
Net::HTTP.start('www.google.com') do |http|
|
|
192
|
+
http.request(req, '')
|
|
193
|
+
socket = http.instance_variable_get(:@socket)
|
|
194
|
+
expect(socket).to be_a(StubSocket)
|
|
195
|
+
expect { socket.io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) }.to_not raise_error
|
|
196
|
+
end
|
|
197
|
+
end
|
|
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
|
+
|
|
204
215
|
describe "connecting on Net::HTTP.start" do
|
|
205
216
|
before(:each) do
|
|
206
217
|
@http = Net::HTTP.new('www.google.com', 443)
|
|
@@ -242,6 +253,21 @@ describe "Net:HTTP" do
|
|
|
242
253
|
}
|
|
243
254
|
end
|
|
244
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
|
+
|
|
245
271
|
it "should connect to the server if the URI matches an regex", net_connect: true do
|
|
246
272
|
WebMock.disable_net_connect!(allow: /google.com/)
|
|
247
273
|
Net::HTTP.get('www.google.com','/')
|
|
@@ -270,6 +296,13 @@ describe "Net:HTTP" do
|
|
|
270
296
|
it_should_behave_like "Net::HTTP"
|
|
271
297
|
end
|
|
272
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
|
+
|
|
273
306
|
describe 'after_request callback support', net_connect: true do
|
|
274
307
|
let(:expected_body_regex) { /hello world/ }
|
|
275
308
|
|
|
@@ -293,7 +326,7 @@ describe "Net:HTTP" do
|
|
|
293
326
|
end
|
|
294
327
|
|
|
295
328
|
it "should support the after_request callback on an request with block and read_body" do
|
|
296
|
-
response_body = ''
|
|
329
|
+
response_body = ''.dup
|
|
297
330
|
http_request(:get, "http://localhost:#{port}/") do |response|
|
|
298
331
|
response.read_body { |fragment| response_body << fragment }
|
|
299
332
|
end
|
|
@@ -314,4 +347,49 @@ describe "Net:HTTP" do
|
|
|
314
347
|
expect(@callback_invocation_count).to eq(1)
|
|
315
348
|
end
|
|
316
349
|
end
|
|
350
|
+
|
|
351
|
+
it "should match http headers, even if their values have been set in a request as numbers" do
|
|
352
|
+
WebMock.disable_net_connect!
|
|
353
|
+
|
|
354
|
+
stub_request(:post, "www.example.com").with(headers: {"My-Header" => 99})
|
|
355
|
+
|
|
356
|
+
uri = URI.parse('http://www.example.com/')
|
|
357
|
+
req = Net::HTTP::Post.new(uri.path)
|
|
358
|
+
req['My-Header'] = 99
|
|
359
|
+
|
|
360
|
+
res = Net::HTTP.start(uri.host, uri.port) do |http|
|
|
361
|
+
http.request(req, '')
|
|
362
|
+
end
|
|
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
|
|
317
395
|
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
|
|
@@ -86,31 +93,29 @@ unless RUBY_PLATFORM =~ /java/
|
|
|
86
93
|
@sess.copy("/abc", "/def")
|
|
87
94
|
end
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
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
|
|
97
103
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
it "should not encode body with default encoding" do
|
|
105
|
+
stub_request(:get, "www.example.com").
|
|
106
|
+
to_return(body: "Øl")
|
|
101
107
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
105
111
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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"))
|
|
110
116
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
end
|
|
117
|
+
expect(@sess.get("").body.encoding).to eq(Encoding::ASCII_8BIT)
|
|
118
|
+
expect(@sess.get("").decoded_body.encoding).to eq(Encoding.default_internal)
|
|
114
119
|
end
|
|
115
120
|
end
|
|
116
121
|
end
|
|
@@ -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
|
|
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
|
|
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,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["
|
|
114
|
+
expect(@response.headers["Server"]).to eq( "Kestrel")
|
|
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
|
|
@@ -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
|
|
@@ -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["
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|