webmock 1.8.6 → 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.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/CI.yml +37 -0
  3. data/.gitignore +6 -0
  4. data/CHANGELOG.md +1198 -0
  5. data/Gemfile +3 -15
  6. data/README.md +761 -305
  7. data/Rakefile +13 -40
  8. data/lib/webmock/api.rb +63 -17
  9. data/lib/webmock/callback_registry.rb +1 -1
  10. data/lib/webmock/config.rb +8 -0
  11. data/lib/webmock/cucumber.rb +2 -0
  12. data/lib/webmock/errors.rb +8 -24
  13. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
  14. data/lib/webmock/http_lib_adapters/curb_adapter.rb +148 -84
  15. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +224 -4
  16. data/lib/webmock/http_lib_adapters/excon_adapter.rb +104 -34
  17. data/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
  18. data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +64 -0
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +152 -86
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +145 -0
  25. data/lib/webmock/http_lib_adapters/net_http.rb +155 -46
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +16 -15
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +76 -82
  29. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +4 -12
  33. data/lib/webmock/minitest.rb +29 -3
  34. data/lib/webmock/rack_response.rb +14 -7
  35. data/lib/webmock/request_body_diff.rb +64 -0
  36. data/lib/webmock/request_execution_verifier.rb +38 -17
  37. data/lib/webmock/request_pattern.rb +158 -38
  38. data/lib/webmock/request_registry.rb +3 -3
  39. data/lib/webmock/request_signature.rb +7 -3
  40. data/lib/webmock/request_signature_snippet.rb +61 -0
  41. data/lib/webmock/request_stub.rb +9 -6
  42. data/lib/webmock/response.rb +30 -15
  43. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +38 -2
  44. data/lib/webmock/rspec/matchers/webmock_matcher.rb +23 -2
  45. data/lib/webmock/rspec/matchers.rb +0 -1
  46. data/lib/webmock/rspec.rb +11 -2
  47. data/lib/webmock/stub_registry.rb +31 -10
  48. data/lib/webmock/stub_request_snippet.rb +14 -6
  49. data/lib/webmock/test_unit.rb +4 -4
  50. data/lib/webmock/util/hash_counter.rb +20 -6
  51. data/lib/webmock/util/hash_keys_stringifier.rb +5 -3
  52. data/lib/webmock/util/hash_validator.rb +17 -0
  53. data/lib/webmock/util/headers.rb +23 -2
  54. data/lib/webmock/util/json.rb +20 -7
  55. data/lib/webmock/util/query_mapper.rb +281 -0
  56. data/lib/webmock/util/uri.rb +29 -19
  57. data/lib/webmock/util/values_stringifier.rb +20 -0
  58. data/lib/webmock/util/version_checker.rb +40 -2
  59. data/lib/webmock/version.rb +1 -1
  60. data/lib/webmock/webmock.rb +56 -17
  61. data/lib/webmock.rb +56 -46
  62. data/minitest/test_helper.rb +8 -3
  63. data/minitest/test_webmock.rb +4 -1
  64. data/minitest/webmock_spec.rb +16 -6
  65. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  66. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  67. data/spec/acceptance/curb/curb_spec.rb +227 -68
  68. data/spec/acceptance/curb/curb_spec_helper.rb +11 -8
  69. data/spec/acceptance/em_http_request/em_http_request_spec.rb +322 -28
  70. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +15 -10
  71. data/spec/acceptance/excon/excon_spec.rb +66 -4
  72. data/spec/acceptance/excon/excon_spec_helper.rb +21 -7
  73. data/spec/acceptance/http_rb/http_rb_spec.rb +93 -0
  74. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
  75. data/spec/acceptance/httpclient/httpclient_spec.rb +152 -11
  76. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +25 -16
  77. data/spec/acceptance/manticore/manticore_spec.rb +107 -0
  78. data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
  79. data/spec/acceptance/net_http/net_http_shared.rb +52 -24
  80. data/spec/acceptance/net_http/net_http_spec.rb +164 -50
  81. data/spec/acceptance/net_http/net_http_spec_helper.rb +19 -10
  82. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  83. data/spec/acceptance/patron/patron_spec.rb +29 -40
  84. data/spec/acceptance/patron/patron_spec_helper.rb +15 -11
  85. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +229 -58
  86. data/spec/acceptance/shared/callbacks.rb +32 -30
  87. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +20 -5
  88. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +14 -14
  89. data/spec/acceptance/shared/precedence_of_stubs.rb +6 -6
  90. data/spec/acceptance/shared/request_expectations.rb +560 -296
  91. data/spec/acceptance/shared/returning_declared_responses.rb +180 -138
  92. data/spec/acceptance/shared/stubbing_requests.rb +385 -154
  93. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +78 -17
  94. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +19 -15
  95. data/spec/acceptance/webmock_shared.rb +2 -2
  96. data/spec/fixtures/test.txt +1 -0
  97. data/spec/quality_spec.rb +27 -3
  98. data/spec/spec_helper.rb +11 -20
  99. data/spec/support/failures.rb +9 -0
  100. data/spec/support/my_rack_app.rb +8 -3
  101. data/spec/support/network_connection.rb +7 -13
  102. data/spec/support/webmock_server.rb +8 -3
  103. data/spec/unit/api_spec.rb +175 -0
  104. data/spec/unit/errors_spec.rb +116 -19
  105. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +1 -1
  106. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +2 -2
  107. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  108. data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
  109. data/spec/unit/rack_response_spec.rb +54 -16
  110. data/spec/unit/request_body_diff_spec.rb +90 -0
  111. data/spec/unit/request_execution_verifier_spec.rb +147 -39
  112. data/spec/unit/request_pattern_spec.rb +462 -198
  113. data/spec/unit/request_registry_spec.rb +29 -9
  114. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  115. data/spec/unit/request_signature_spec.rb +91 -49
  116. data/spec/unit/request_stub_spec.rb +71 -70
  117. data/spec/unit/response_spec.rb +100 -81
  118. data/spec/unit/stub_registry_spec.rb +37 -20
  119. data/spec/unit/stub_request_snippet_spec.rb +51 -31
  120. data/spec/unit/util/hash_counter_spec.rb +6 -6
  121. data/spec/unit/util/hash_keys_stringifier_spec.rb +4 -4
  122. data/spec/unit/util/headers_spec.rb +4 -4
  123. data/spec/unit/util/json_spec.rb +29 -3
  124. data/spec/unit/util/query_mapper_spec.rb +157 -0
  125. data/spec/unit/util/uri_spec.rb +150 -36
  126. data/spec/unit/util/version_checker_spec.rb +15 -9
  127. data/spec/unit/webmock_spec.rb +57 -4
  128. data/test/http_request.rb +3 -3
  129. data/test/shared_test.rb +45 -13
  130. data/test/test_helper.rb +1 -1
  131. data/test/test_webmock.rb +6 -0
  132. data/webmock.gemspec +30 -11
  133. metadata +308 -199
  134. data/.rvmrc +0 -1
  135. data/.travis.yml +0 -11
  136. data/Guardfile +0 -24
  137. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_0_x.rb +0 -151
  138. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +0 -210
@@ -0,0 +1,93 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+ require "acceptance/webmock_shared"
5
+ require "acceptance/http_rb/http_rb_spec_helper"
6
+
7
+ describe "HTTP.rb" do
8
+ include HttpRbSpecHelper
9
+
10
+ include_examples "with WebMock", :no_status_message
11
+
12
+ context "streaming body" do
13
+ let(:response) { HTTP.get "http://example.com" }
14
+ before { stub_simple_request "example.com", 302, {}, "abc" }
15
+
16
+ it "works as if it was streamed from socket" do
17
+ expect(response.body.readpartial 1).to eq "a"
18
+ end
19
+
20
+ it "fails if body was already streamed" do
21
+ response.body.to_s
22
+ expect { response.body.readpartial 1 }.to raise_error(HTTP::StateError)
23
+ end
24
+ end
25
+
26
+ context "without following redirects" do
27
+ let(:response) { http_request(:get, "http://example.com") }
28
+ let(:headers) { response.headers }
29
+
30
+ it "stops on first request" do
31
+ stub_simple_request("example.com", 302, "Location" => "http://www.example.com")
32
+ stub_simple_request("www.example.com")
33
+
34
+ expect(headers).to include "Host" => "example.com"
35
+ end
36
+ end
37
+
38
+ context "following redirects" do
39
+ let(:options) { { follow: true } }
40
+ let(:response) { http_request(:get, "http://example.com", options) }
41
+ let(:headers) { response.headers }
42
+
43
+ it "returns response of destination" do
44
+ stub_simple_request("example.com", 302, "Location" => "http://www.example.com")
45
+ stub_simple_request("www.example.com")
46
+
47
+ expect(headers).to include "Host" => "www.example.com"
48
+ end
49
+ end
50
+
51
+ context "restored request uri on replayed response object" do
52
+ it "keeps non-default port" do
53
+ stub_request :get, "example.com:1234/foo"
54
+ response = HTTP.get "http://example.com:1234/foo"
55
+
56
+ expect(response.uri.to_s).to eq "http://example.com:1234/foo"
57
+ end
58
+
59
+ it "does not injects default port" do
60
+ stub_request :get, "example.com/foo"
61
+ response = HTTP.get "http://example.com/foo"
62
+
63
+ expect(response.uri.to_s).to eq "http://example.com/foo"
64
+ end
65
+
66
+ it "strips out default port even if it was explicitly given" do
67
+ stub_request :get, "example.com/foo"
68
+ response = HTTP.get "http://example.com:80/foo"
69
+
70
+ expect(response.uri.to_s).to eq "http://example.com/foo"
71
+ end
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
93
+ end
@@ -0,0 +1,54 @@
1
+ require "ostruct"
2
+
3
+ module HttpRbSpecHelper
4
+ def http_request(method, uri, options = {})
5
+ chain = HTTP
6
+
7
+ if basic_auth = options.delete(:basic_auth)
8
+ chain = chain.basic_auth(user: basic_auth[0], pass: basic_auth[1])
9
+ end
10
+
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))
15
+
16
+ OpenStruct.new({
17
+ body: response.body.to_s,
18
+ headers: normalize_headers(response.headers.to_h),
19
+ status: response.code.to_s,
20
+ message: response.reason
21
+ })
22
+ end
23
+
24
+ def client_timeout_exception_class
25
+ return Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0"
26
+ HTTP::TimeoutError
27
+ end
28
+
29
+ def connection_refused_exception_class
30
+ return Errno::ECONNREFUSED if HTTP::VERSION < "1.0.0"
31
+ HTTP::ConnectionError
32
+ end
33
+
34
+ def http_library
35
+ :http_rb
36
+ end
37
+
38
+ def normalize_uri(uri)
39
+ Addressable::URI.heuristic_parse(uri).normalize.to_s
40
+ end
41
+
42
+ def normalize_headers(headers)
43
+ headers = Hash[headers.map { |k, v| [k, Array(v).join(", ")] }]
44
+ WebMock::Util::Headers.normalize_headers headers
45
+ end
46
+
47
+ def stub_simple_request(host, status = 200, headers = {}, body = nil)
48
+ stub_request(:any, host).to_return({
49
+ status: status,
50
+ headers: headers.merge({ "Host" => host }),
51
+ body: body
52
+ })
53
+ end
54
+ end
@@ -8,25 +8,41 @@ describe "HTTPClient" do
8
8
  include HTTPClientSpecHelper
9
9
 
10
10
  before(:each) do
11
+ WebMock.reset_callbacks
11
12
  HTTPClientSpecHelper.async_mode = false
12
13
  end
13
14
 
14
15
  include_examples "with WebMock"
15
16
 
17
+ it "should raise a clearly readable error if request with multipart body is sent" do
18
+ stub_request(:post, 'www.example.com').with(body: {type: 'image'})
19
+
20
+ expect {
21
+ HTTPClient.new.post_content('www.example.com', type: 'image', file: File.new('spec/fixtures/test.txt'))
22
+ }.to raise_error(ArgumentError, "WebMock does not support matching body for multipart/form-data requests yet :(")
23
+ end
24
+
16
25
  it "should yield block on response if block provided" do
17
- stub_request(:get, "www.example.com").to_return(:body => "abc")
26
+ stub_request(:get, "www.example.com").to_return(body: "abc")
18
27
  response_body = ""
19
28
  http_request(:get, "http://www.example.com/") do |body|
20
29
  response_body = body
21
30
  end
22
- response_body.should == "abc"
31
+ expect(response_body).to eq("abc")
32
+ end
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("")
23
39
  end
24
40
 
25
41
  it "should match requests if headers are the same but in different order" do
26
- stub_request(:get, "www.example.com").with(:headers => {"a" => ["b", "c"]} )
27
- http_request(
42
+ stub_request(:get, "www.example.com").with(headers: {"a" => ["b", "c"]} )
43
+ expect(http_request(
28
44
  :get, "http://www.example.com/",
29
- :headers => {"a" => ["c", "b"]}).status.should == "200"
45
+ headers: {"a" => ["c", "b"]}).status).to eq("200")
30
46
  end
31
47
 
32
48
  describe "when using async requests" do
@@ -38,14 +54,50 @@ describe "HTTPClient" do
38
54
  end
39
55
 
40
56
  it "should work with get_content" do
41
- stub_request(:get, 'www.example.com').to_return(:status => 200, :body => 'test', :headers => {})
42
- str = ''
57
+ stub_request(:get, 'www.example.com').to_return(status: 200, body: 'test', headers: {})
58
+ str = ''.dup
43
59
  HTTPClient.get_content('www.example.com') do |content|
44
60
  str << content
45
61
  end
46
- str.should == 'test'
62
+ expect(str).to eq('test')
63
+ end
64
+
65
+ it "should work via JSONClient subclass" do
66
+ stub_request(:get, 'www.example.com').to_return(
67
+ status: 200,
68
+ body: '{"test": "foo"}',
69
+ headers: {'Content-Type' => 'application/json'}
70
+ )
71
+ content = JSONClient.get('www.example.com').content
72
+ expect(content).to eq("test" => "foo")
73
+ end
74
+
75
+ context "multipart bodies" do
76
+ let(:header) {{
77
+ 'Accept' => 'application/json',
78
+ 'Content-Type' => 'multipart/form-data'
79
+ }}
80
+
81
+ let(:body) {[
82
+ {
83
+ 'Content-Type' => 'application/json',
84
+ 'Content-Disposition' => 'form-data',
85
+ :content => '{"foo": "bar", "baz": 2}'
86
+ }
87
+ ]}
88
+
89
+ let(:make_request) {HTTPClient.new.post("http://www.example.com", body: body, header: header)}
90
+
91
+ before do
92
+ stub_request(:post, "www.example.com")
93
+ end
94
+
95
+ it "should work with multipart bodies" do
96
+ make_request
97
+ end
47
98
  end
48
99
 
100
+
49
101
  context "Filters" do
50
102
  class Filter
51
103
  def filter_request(request)
@@ -60,17 +112,106 @@ describe "HTTPClient" do
60
112
  before do
61
113
  @client = HTTPClient.new
62
114
  @client.request_filter << Filter.new
63
- stub_request(:get, 'www.example.com').with(:headers => {'Authorization' => 'Bearer 0123456789'})
115
+ stub_request(:get, 'www.example.com').with(headers: {'Authorization' => 'Bearer 0123456789'})
64
116
  end
65
117
 
66
118
  it "supports request filters" do
67
- @client.request(:get, 'http://www.example.com/').status.should == 200
119
+ expect(@client.request(:get, 'http://www.example.com/').status).to eq(200)
68
120
  end
69
121
 
70
122
  it "supports response filters" do
71
123
  res = @client.request(:get, 'http://www.example.com/')
72
- res.header['X-Powered-By'].first.should == 'webmock'
124
+ expect(res.header['X-Powered-By'].first).to eq('webmock')
73
125
  end
74
126
  end
75
127
 
128
+ context 'when a client instance is re-used for another identical request' do
129
+ let(:client) { HTTPClient.new }
130
+ let(:webmock_server_url) {"http://#{WebMockServer.instance.host_with_port}/"}
131
+
132
+ before { WebMock.allow_net_connect! }
133
+
134
+ it 'invokes the global_stub_request hook for each request' do
135
+ # Mock time to ensure that date headers match
136
+ now = Time.now
137
+ allow(Time).to receive(:now).and_return(now)
138
+
139
+ request_signatures = []
140
+ WebMock.globally_stub_request do |request_sig|
141
+ request_signatures << request_sig
142
+ nil # to let the request be made for real
143
+ end
144
+
145
+ http_request(:get, webmock_server_url, client: client, headers: { "Cookie" => "bar=; foo=" })
146
+
147
+ if defined? HTTP::CookieJar
148
+ http_request(:get, webmock_server_url, client: client, headers: { "Cookie" => "bar=; foo=" })
149
+ else
150
+ # If http-cookie is not present, then the cookie headers will saved between requests
151
+ http_request(:get, webmock_server_url, client: client)
152
+ end
153
+
154
+ expect(request_signatures.size).to eq(2)
155
+ # Verify the request signatures were identical as needed by this example
156
+ expect(request_signatures.first).to eq(request_signatures.last)
157
+ end
158
+ end
159
+
160
+ context 'session headers' do
161
+ it "client sends a User-Agent header when given an agent_name explicitly to the client" do
162
+ user_agent = "Client/0.1"
163
+ stub_request(:get, "www.example.com").with(headers: { 'User-agent' => "#{user_agent} #{HTTPClient::LIB_NAME}" })
164
+ HTTPClient.new(agent_name: user_agent).get("www.example.com")
165
+ end
166
+
167
+ it "client sends the Accept, User-Agent, and Date by default" do
168
+ WebMock.disable_net_connect!
169
+ stub_request(:get, "www.example.com").with do |req|
170
+ req.headers["Accept"] == "*/*" &&
171
+ req.headers["User-Agent"] == "#{HTTPClient::DEFAULT_AGENT_NAME} #{HTTPClient::LIB_NAME}" &&
172
+ req.headers["Date"]
173
+ end
174
+ http_request(:get, "www.example.com")
175
+ end
176
+
177
+ it "explicitly defined headers take precedence over session defaults" do
178
+ headers = { 'Accept' => 'foo/bar', 'User-Agent' => 'custom', 'Date' => 'today' }
179
+ stub_request(:get, "www.example.com").with(headers: headers)
180
+ HTTPClient.new.get("www.example.com", nil, headers)
181
+ end
182
+ end
183
+
184
+ context 'httpclient response header' do
185
+ it 'receives request_method, request_uri, and request_query from the request header' do
186
+ stub_request :get, 'www.example.com'
187
+ message = HTTPClient.new.get 'www.example.com'
188
+ expect(message.header.request_uri.to_s).to eq('www.example.com')
189
+ end
190
+ end
191
+
192
+ context 'httpclient streams response', net_connect: true do
193
+ before do
194
+ WebMock.allow_net_connect!
195
+ WebMock.after_request(except: [:other_lib]) do |_, response|
196
+ @response = response
197
+ end
198
+ end
199
+
200
+ it 'sets the full body on the webmock response' do
201
+ body = ''
202
+ HTTPClient.new.request(:get, 'http://www.example.com/') do |http_res, chunk|
203
+ body += chunk
204
+ end
205
+ expect(@response.body).to eq body
206
+ end
207
+ end
208
+
209
+ context 'credentials' do
210
+ it 'are detected when manually specifying Authorization header' do
211
+ stub_request(:get, "http://www.example.com/").with(basic_auth: ['username', 'password']).to_return(status: 200)
212
+
213
+ headers = {'Authorization' => 'Basic dXNlcm5hbWU6cGFzc3dvcmQ='}
214
+ expect(http_request(:get, 'http://www.example.com/', {headers: headers}).status).to eql('200')
215
+ end
216
+ end
76
217
  end
@@ -5,11 +5,15 @@ module HTTPClientSpecHelper
5
5
 
6
6
  def http_request(method, uri, options = {}, &block)
7
7
  uri = Addressable::URI.heuristic_parse(uri)
8
- c = HTTPClient.new
8
+ c = options.fetch(:client) { HTTPClient.new }
9
9
  c.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
10
- c.set_basic_auth(nil, uri.user, uri.password) if uri.user
11
- params = [method, "#{uri.omit(:userinfo, :query).normalize.to_s}",
12
- uri.query_values, options[:body], options[:headers] || {}]
10
+ c.reset_all
11
+ if options[:basic_auth]
12
+ c.force_basic_auth = true
13
+ c.set_basic_auth(nil, options[:basic_auth][0], options[:basic_auth][1])
14
+ end
15
+ params = [method, uri.normalize.to_s,
16
+ WebMock::Util::QueryMapper.query_to_values(uri.query, notation: WebMock::Config.instance.query_values_notation), options[:body], options[:headers] || {}]
13
17
  if HTTPClientSpecHelper.async_mode
14
18
  connection = c.request_async(*params)
15
19
  connection.join
@@ -17,19 +21,12 @@ module HTTPClientSpecHelper
17
21
  else
18
22
  response = c.request(*params, &block)
19
23
  end
20
- headers = response.header.all.inject({}) do |headers, header|
21
- if !headers.has_key?(header[0])
22
- headers[header[0]] = header[1]
23
- else
24
- headers[header[0]] = [headers[header[0]], header[1]].join(', ')
25
- end
26
- headers
27
- end
24
+ headers = merge_headers(response)
28
25
  OpenStruct.new({
29
- :body => HTTPClientSpecHelper.async_mode ? response.content.read : response.content,
30
- :headers => headers,
31
- :status => response.code.to_s,
32
- :message => response.reason
26
+ body: HTTPClientSpecHelper.async_mode ? response.content.read : response.content,
27
+ headers: headers,
28
+ status: response.code.to_s,
29
+ message: response.reason
33
30
  })
34
31
  end
35
32
 
@@ -45,4 +42,16 @@ module HTTPClientSpecHelper
45
42
  :httpclient
46
43
  end
47
44
 
45
+ private
46
+
47
+ def merge_headers(response)
48
+ response.header.all.inject({}) do |headers, header|
49
+ if !headers.has_key?(header[0])
50
+ headers[header[0]] = header[1]
51
+ else
52
+ headers[header[0]] = [headers[header[0]], header[1]].join(', ')
53
+ end
54
+ headers
55
+ end
56
+ end
48
57
  end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ require 'acceptance/webmock_shared'
3
+
4
+ if RUBY_PLATFORM =~ /java/
5
+ require 'acceptance/manticore/manticore_spec_helper'
6
+
7
+ describe "Manticore" do
8
+ include ManticoreSpecHelper
9
+
10
+ include_context "with WebMock", :no_status_message
11
+
12
+ context "calling http methods on Manticore directly using Manticore's facade" do
13
+ it "handles GET" do
14
+ stub_request(:get, "http://example-foo.com").to_return(status: 301)
15
+ response = Manticore.get("http://example-foo.com")
16
+ expect(response.code).to eq(301)
17
+ end
18
+
19
+ it "handles POST" do
20
+ stub_request(:post, "http://example-foo.com").to_return(status: 201)
21
+ response = Manticore.post("http://example-foo.com", {hello: "world"})
22
+ expect(response.code).to eq(201)
23
+ end
24
+
25
+ it "handles PUT" do
26
+ stub_request(:put, "http://example-foo.com").to_return(status: 409)
27
+ response = Manticore.put("http://example-foo.com", {hello: "world"})
28
+ expect(response.code).to eq(409)
29
+ end
30
+
31
+ it "handles PATCH" do
32
+ stub_request(:patch, "http://example-foo.com").to_return(status: 409)
33
+ response = Manticore.patch("http://example-foo.com", {hello: "world"})
34
+ expect(response.code).to eq(409)
35
+ end
36
+
37
+ it "handles DELETE" do
38
+ stub_request(:delete, "http://example-foo.com").to_return(status: 204)
39
+ response = Manticore.delete("http://example-foo.com", {id: 1})
40
+ expect(response.code).to eq(204)
41
+ end
42
+
43
+ it "handles OPTIONS" do
44
+ stub_request(:options, "http://example-foo.com").to_return(status: 200)
45
+ response = Manticore.options("http://example-foo.com")
46
+ expect(response.code).to eq(200)
47
+ end
48
+
49
+ it "handles HEAD" do
50
+ stub_request(:head, "http://example-foo.com").to_return(status: 204)
51
+ response = Manticore.head("http://example-foo.com")
52
+ expect(response.code).to eq(204)
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
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,35 @@
1
+ module ManticoreSpecHelper
2
+ def http_request(method, uri, options = {})
3
+ client = Manticore::Client.new
4
+
5
+ if basic_auth = options[:basic_auth]
6
+ options = options.merge(auth: {user: basic_auth[0], pass: basic_auth[1]})
7
+ end
8
+
9
+ response = client.http(method, uri, options)
10
+ OpenStruct.new({
11
+ body: response.body || '',
12
+ headers: WebMock::Util::Headers.normalize_headers(join_array_values(response.headers)),
13
+ status: response.code.to_s
14
+ })
15
+ end
16
+
17
+ def join_array_values(hash)
18
+ hash.reduce({}) do |h, (k,v)|
19
+ v = v.join(', ') if v.is_a?(Array)
20
+ h.merge(k => v)
21
+ end
22
+ end
23
+
24
+ def client_timeout_exception_class
25
+ Manticore::ConnectTimeout
26
+ end
27
+
28
+ def connection_refused_exception_class
29
+ Manticore::SocketException
30
+ end
31
+
32
+ def http_library
33
+ :manticore
34
+ end
35
+ end