webmock 3.0.0 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/CI.yml +37 -0
- data/CHANGELOG.md +416 -0
- data/Gemfile +1 -1
- data/README.md +157 -31
- data/Rakefile +12 -4
- data/lib/webmock/api.rb +12 -0
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +17 -3
- 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 +4 -1
- data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +24 -3
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +6 -2
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +2 -2
- 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 +54 -14
- 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 +108 -46
- data/lib/webmock/request_registry.rb +1 -1
- data/lib/webmock/request_signature.rb +1 -1
- data/lib/webmock/request_signature_snippet.rb +4 -4
- data/lib/webmock/response.rb +11 -5
- 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 +4 -4
- 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 +10 -3
- data/lib/webmock.rb +53 -48
- data/minitest/webmock_spec.rb +2 -2
- 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 +33 -0
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +56 -0
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
- 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 +1 -1
- data/spec/acceptance/net_http/net_http_spec.rb +53 -1
- data/spec/acceptance/patron/patron_spec.rb +7 -0
- data/spec/acceptance/patron/patron_spec_helper.rb +3 -3
- data/spec/acceptance/shared/callbacks.rb +3 -2
- 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 +195 -49
- data/spec/unit/request_signature_snippet_spec.rb +2 -2
- data/spec/unit/response_spec.rb +22 -18
- 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 +54 -5
- data/test/shared_test.rb +15 -2
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +11 -3
- metadata +66 -17
- 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
|
|
@@ -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'
|
|
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'
|
|
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("")
|
|
@@ -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
|
-
|
|
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::
|
|
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
|
|
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,
|
|
@@ -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["
|
|
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["
|
|
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
|
|
@@ -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
|
|
@@ -6,7 +6,7 @@ module TyphoeusHydraSpecHelper
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def http_request(method, uri, options = {}, &block)
|
|
9
|
-
uri.gsub
|
|
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],
|