httpserious 0.13.5.lstoll1
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 +7 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +92 -0
- data/.rubocop_todo.yml +124 -0
- data/.simplecov +1 -0
- data/.travis.yml +7 -0
- data/CONTRIBUTING.md +23 -0
- data/Gemfile +19 -0
- data/Guardfile +16 -0
- data/History +370 -0
- data/MIT-LICENSE +20 -0
- data/README.md +78 -0
- data/Rakefile +10 -0
- data/bin/httparty +116 -0
- data/cucumber.yml +1 -0
- data/examples/README.md +67 -0
- data/examples/aaws.rb +32 -0
- data/examples/basic.rb +28 -0
- data/examples/crack.rb +19 -0
- data/examples/custom_parsers.rb +64 -0
- data/examples/delicious.rb +37 -0
- data/examples/google.rb +16 -0
- data/examples/headers_and_user_agents.rb +6 -0
- data/examples/logging.rb +36 -0
- data/examples/nokogiri_html_parser.rb +19 -0
- data/examples/rescue_json.rb +17 -0
- data/examples/rubyurl.rb +14 -0
- data/examples/stackexchange.rb +24 -0
- data/examples/tripit_sign_in.rb +33 -0
- data/examples/twitter.rb +31 -0
- data/examples/whoismyrep.rb +10 -0
- data/features/basic_authentication.feature +20 -0
- data/features/command_line.feature +90 -0
- data/features/deals_with_http_error_codes.feature +26 -0
- data/features/digest_authentication.feature +20 -0
- data/features/handles_compressed_responses.feature +27 -0
- data/features/handles_multiple_formats.feature +57 -0
- data/features/steps/env.rb +27 -0
- data/features/steps/httparty_response_steps.rb +52 -0
- data/features/steps/httparty_steps.rb +43 -0
- data/features/steps/mongrel_helper.rb +94 -0
- data/features/steps/remote_service_steps.rb +86 -0
- data/features/supports_read_timeout_option.feature +13 -0
- data/features/supports_redirection.feature +22 -0
- data/features/supports_timeout_option.feature +13 -0
- data/httparty.gemspec +28 -0
- data/httpserious.gemspec +25 -0
- data/lib/httparty.rb +612 -0
- data/lib/httparty/connection_adapter.rb +190 -0
- data/lib/httparty/cookie_hash.rb +21 -0
- data/lib/httparty/exceptions.rb +29 -0
- data/lib/httparty/hash_conversions.rb +49 -0
- data/lib/httparty/logger/apache_formatter.rb +22 -0
- data/lib/httparty/logger/curl_formatter.rb +48 -0
- data/lib/httparty/logger/logger.rb +26 -0
- data/lib/httparty/module_inheritable_attributes.rb +56 -0
- data/lib/httparty/net_digest_auth.rb +117 -0
- data/lib/httparty/parser.rb +141 -0
- data/lib/httparty/request.rb +361 -0
- data/lib/httparty/response.rb +77 -0
- data/lib/httparty/response/headers.rb +31 -0
- data/lib/httparty/version.rb +3 -0
- data/lib/httpserious.rb +1 -0
- data/script/release +42 -0
- data/spec/fixtures/delicious.xml +23 -0
- data/spec/fixtures/empty.xml +0 -0
- data/spec/fixtures/google.html +3 -0
- data/spec/fixtures/ssl/generate.sh +29 -0
- data/spec/fixtures/ssl/generated/1fe462c2.0 +16 -0
- data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
- data/spec/fixtures/ssl/generated/ca.crt +16 -0
- data/spec/fixtures/ssl/generated/ca.key +15 -0
- data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
- data/spec/fixtures/ssl/generated/server.crt +13 -0
- data/spec/fixtures/ssl/generated/server.key +15 -0
- data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
- data/spec/fixtures/twitter.csv +2 -0
- data/spec/fixtures/twitter.json +1 -0
- data/spec/fixtures/twitter.xml +403 -0
- data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
- data/spec/httparty/connection_adapter_spec.rb +468 -0
- data/spec/httparty/cookie_hash_spec.rb +83 -0
- data/spec/httparty/exception_spec.rb +38 -0
- data/spec/httparty/hash_conversions_spec.rb +41 -0
- data/spec/httparty/logger/apache_formatter_spec.rb +41 -0
- data/spec/httparty/logger/curl_formatter_spec.rb +18 -0
- data/spec/httparty/logger/logger_spec.rb +38 -0
- data/spec/httparty/net_digest_auth_spec.rb +191 -0
- data/spec/httparty/parser_spec.rb +167 -0
- data/spec/httparty/request_spec.rb +872 -0
- data/spec/httparty/response_spec.rb +241 -0
- data/spec/httparty/ssl_spec.rb +74 -0
- data/spec/httparty_spec.rb +823 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/support/ssl_test_helper.rb +47 -0
- data/spec/support/ssl_test_server.rb +80 -0
- data/spec/support/stub_response.rb +43 -0
- data/website/css/common.css +47 -0
- data/website/index.html +73 -0
- metadata +219 -0
@@ -0,0 +1,241 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
RSpec.describe HTTParty::Response do
|
4
|
+
before do
|
5
|
+
@last_modified = Date.new(2010, 1, 15).to_s
|
6
|
+
@content_length = '1024'
|
7
|
+
@request_object = HTTParty::Request.new Net::HTTP::Get, '/'
|
8
|
+
@response_object = Net::HTTPOK.new('1.1', 200, 'OK')
|
9
|
+
allow(@response_object).to receive_messages(body: "{foo:'bar'}")
|
10
|
+
@response_object['last-modified'] = @last_modified
|
11
|
+
@response_object['content-length'] = @content_length
|
12
|
+
@parsed_response = lambda { {"foo" => "bar"} }
|
13
|
+
@response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".underscore" do
|
17
|
+
it "works with one capitalized word" do
|
18
|
+
expect(HTTParty::Response.underscore("Accepted")).to eq("accepted")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "works with titlecase" do
|
22
|
+
expect(HTTParty::Response.underscore("BadGateway")).to eq("bad_gateway")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "works with all caps" do
|
26
|
+
expect(HTTParty::Response.underscore("OK")).to eq("ok")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "initialization" do
|
31
|
+
it "should set the Net::HTTP Response" do
|
32
|
+
expect(@response.response).to eq(@response_object)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should set body" do
|
36
|
+
expect(@response.body).to eq(@response_object.body)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should set code" do
|
40
|
+
expect(@response.code).to eq(@response_object.code)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should set code as a Fixnum" do
|
44
|
+
expect(@response.code).to be_an_instance_of(Fixnum)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "returns response headers" do
|
49
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
50
|
+
expect(response.headers).to eq({'last-modified' => [@last_modified], 'content-length' => [@content_length]})
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should send missing methods to delegate" do
|
54
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
55
|
+
expect(response['foo']).to eq('bar')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "response to request" do
|
59
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
60
|
+
expect(response.respond_to?(:request)).to be_truthy
|
61
|
+
end
|
62
|
+
|
63
|
+
it "responds to response" do
|
64
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
65
|
+
expect(response.respond_to?(:response)).to be_truthy
|
66
|
+
end
|
67
|
+
|
68
|
+
it "responds to body" do
|
69
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
70
|
+
expect(response.respond_to?(:body)).to be_truthy
|
71
|
+
end
|
72
|
+
|
73
|
+
it "responds to headers" do
|
74
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
75
|
+
expect(response.respond_to?(:headers)).to be_truthy
|
76
|
+
end
|
77
|
+
|
78
|
+
it "responds to parsed_response" do
|
79
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
80
|
+
expect(response.respond_to?(:parsed_response)).to be_truthy
|
81
|
+
end
|
82
|
+
|
83
|
+
it "responds to anything parsed_response responds to" do
|
84
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
85
|
+
expect(response.respond_to?(:[])).to be_truthy
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should be able to iterate if it is array" do
|
89
|
+
response = HTTParty::Response.new(@request_object, @response_object, lambda { [{'foo' => 'bar'}, {'foo' => 'baz'}] })
|
90
|
+
expect(response.size).to eq(2)
|
91
|
+
expect {
|
92
|
+
response.each { |item| }
|
93
|
+
}.to_not raise_error
|
94
|
+
end
|
95
|
+
|
96
|
+
it "allows headers to be accessed by mixed-case names in hash notation" do
|
97
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
98
|
+
expect(response.headers['Content-LENGTH']).to eq(@content_length)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "returns a comma-delimited value when multiple values exist" do
|
102
|
+
@response_object.add_field 'set-cookie', 'csrf_id=12345; path=/'
|
103
|
+
@response_object.add_field 'set-cookie', '_github_ses=A123CdE; path=/'
|
104
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
105
|
+
expect(response.headers['set-cookie']).to eq("csrf_id=12345; path=/, _github_ses=A123CdE; path=/")
|
106
|
+
end
|
107
|
+
|
108
|
+
# Backwards-compatibility - previously, #headers returned a Hash
|
109
|
+
it "responds to hash methods" do
|
110
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
111
|
+
hash_methods = {}.methods - response.headers.methods
|
112
|
+
hash_methods.each do |method_name|
|
113
|
+
expect(response.headers.respond_to?(method_name)).to be_truthy
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "semantic methods for response codes" do
|
118
|
+
def response_mock(klass)
|
119
|
+
response = klass.new('', '', '')
|
120
|
+
allow(response).to receive(:body)
|
121
|
+
response
|
122
|
+
end
|
123
|
+
|
124
|
+
context "major codes" do
|
125
|
+
it "is information" do
|
126
|
+
net_response = response_mock(Net::HTTPInformation)
|
127
|
+
response = HTTParty::Response.new(@request_object, net_response, '')
|
128
|
+
expect(response.information?).to be_truthy
|
129
|
+
end
|
130
|
+
|
131
|
+
it "is success" do
|
132
|
+
net_response = response_mock(Net::HTTPSuccess)
|
133
|
+
response = HTTParty::Response.new(@request_object, net_response, '')
|
134
|
+
expect(response.success?).to be_truthy
|
135
|
+
end
|
136
|
+
|
137
|
+
it "is redirection" do
|
138
|
+
net_response = response_mock(Net::HTTPRedirection)
|
139
|
+
response = HTTParty::Response.new(@request_object, net_response, '')
|
140
|
+
expect(response.redirection?).to be_truthy
|
141
|
+
end
|
142
|
+
|
143
|
+
it "is client error" do
|
144
|
+
net_response = response_mock(Net::HTTPClientError)
|
145
|
+
response = HTTParty::Response.new(@request_object, net_response, '')
|
146
|
+
expect(response.client_error?).to be_truthy
|
147
|
+
end
|
148
|
+
|
149
|
+
it "is server error" do
|
150
|
+
net_response = response_mock(Net::HTTPServerError)
|
151
|
+
response = HTTParty::Response.new(@request_object, net_response, '')
|
152
|
+
expect(response.server_error?).to be_truthy
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "for specific codes" do
|
157
|
+
SPECIFIC_CODES = {
|
158
|
+
accepted?: Net::HTTPAccepted,
|
159
|
+
bad_gateway?: Net::HTTPBadGateway,
|
160
|
+
bad_request?: Net::HTTPBadRequest,
|
161
|
+
conflict?: Net::HTTPConflict,
|
162
|
+
continue?: Net::HTTPContinue,
|
163
|
+
created?: Net::HTTPCreated,
|
164
|
+
expectation_failed?: Net::HTTPExpectationFailed,
|
165
|
+
forbidden?: Net::HTTPForbidden,
|
166
|
+
found?: Net::HTTPFound,
|
167
|
+
gateway_time_out?: Net::HTTPGatewayTimeOut,
|
168
|
+
gone?: Net::HTTPGone,
|
169
|
+
internal_server_error?: Net::HTTPInternalServerError,
|
170
|
+
length_required?: Net::HTTPLengthRequired,
|
171
|
+
method_not_allowed?: Net::HTTPMethodNotAllowed,
|
172
|
+
moved_permanently?: Net::HTTPMovedPermanently,
|
173
|
+
multiple_choice?: Net::HTTPMultipleChoice,
|
174
|
+
no_content?: Net::HTTPNoContent,
|
175
|
+
non_authoritative_information?: Net::HTTPNonAuthoritativeInformation,
|
176
|
+
not_acceptable?: Net::HTTPNotAcceptable,
|
177
|
+
not_found?: Net::HTTPNotFound,
|
178
|
+
not_implemented?: Net::HTTPNotImplemented,
|
179
|
+
not_modified?: Net::HTTPNotModified,
|
180
|
+
ok?: Net::HTTPOK,
|
181
|
+
partial_content?: Net::HTTPPartialContent,
|
182
|
+
payment_required?: Net::HTTPPaymentRequired,
|
183
|
+
precondition_failed?: Net::HTTPPreconditionFailed,
|
184
|
+
proxy_authentication_required?: Net::HTTPProxyAuthenticationRequired,
|
185
|
+
request_entity_too_large?: Net::HTTPRequestEntityTooLarge,
|
186
|
+
request_time_out?: Net::HTTPRequestTimeOut,
|
187
|
+
request_uri_too_long?: Net::HTTPRequestURITooLong,
|
188
|
+
requested_range_not_satisfiable?: Net::HTTPRequestedRangeNotSatisfiable,
|
189
|
+
reset_content?: Net::HTTPResetContent,
|
190
|
+
see_other?: Net::HTTPSeeOther,
|
191
|
+
service_unavailable?: Net::HTTPServiceUnavailable,
|
192
|
+
switch_protocol?: Net::HTTPSwitchProtocol,
|
193
|
+
temporary_redirect?: Net::HTTPTemporaryRedirect,
|
194
|
+
unauthorized?: Net::HTTPUnauthorized,
|
195
|
+
unsupported_media_type?: Net::HTTPUnsupportedMediaType,
|
196
|
+
use_proxy?: Net::HTTPUseProxy,
|
197
|
+
version_not_supported?: Net::HTTPVersionNotSupported
|
198
|
+
}
|
199
|
+
|
200
|
+
# Ruby 2.0, new name for this response.
|
201
|
+
if RUBY_VERSION >= "2.0.0" && ::RUBY_PLATFORM != "java"
|
202
|
+
SPECIFIC_CODES[:multiple_choices?] = Net::HTTPMultipleChoices
|
203
|
+
end
|
204
|
+
|
205
|
+
SPECIFIC_CODES.each do |method, klass|
|
206
|
+
it "responds to #{method}" do
|
207
|
+
net_response = response_mock(klass)
|
208
|
+
response = HTTParty::Response.new(@request_object, net_response, '')
|
209
|
+
expect(response.__send__(method)).to be_truthy
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "headers" do
|
216
|
+
it "can initialize without headers" do
|
217
|
+
headers = HTTParty::Response::Headers.new
|
218
|
+
expect(headers).to eq({})
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe "#tap" do
|
223
|
+
it "is possible to tap into a response" do
|
224
|
+
result = @response.tap(&:code)
|
225
|
+
|
226
|
+
expect(result).to eq @response
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "#inspect" do
|
231
|
+
it "works" do
|
232
|
+
inspect = @response.inspect
|
233
|
+
expect(inspect).to include("HTTParty::Response:0x")
|
234
|
+
expect(inspect).to include("parsed_response={\"foo\"=>\"bar\"}")
|
235
|
+
expect(inspect).to include("@response=#<Net::HTTPOK 200 OK readbody=false>")
|
236
|
+
expect(inspect).to include("@headers={")
|
237
|
+
expect(inspect).to include("last-modified")
|
238
|
+
expect(inspect).to include("content-length")
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
RSpec.describe HTTParty::Request do
|
4
|
+
context "SSL certificate verification" do
|
5
|
+
before do
|
6
|
+
FakeWeb.allow_net_connect = true
|
7
|
+
end
|
8
|
+
|
9
|
+
after do
|
10
|
+
FakeWeb.allow_net_connect = false
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should fail when no trusted CA list is specified, by default" do
|
14
|
+
expect do
|
15
|
+
ssl_verify_test(nil, nil, "selfsigned.crt")
|
16
|
+
end.to raise_error OpenSSL::SSL::SSLError
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should work when no trusted CA list is specified, when the verify option is set to false" do
|
20
|
+
expect(ssl_verify_test(nil, nil, "selfsigned.crt", verify: false).parsed_response).to eq({'success' => true})
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should fail when no trusted CA list is specified, with a bogus hostname, by default" do
|
24
|
+
expect do
|
25
|
+
ssl_verify_test(nil, nil, "bogushost.crt")
|
26
|
+
end.to raise_error OpenSSL::SSL::SSLError
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should work when no trusted CA list is specified, even with a bogus hostname, when the verify option is set to true" do
|
30
|
+
expect(ssl_verify_test(nil, nil, "bogushost.crt", verify: false).parsed_response).to eq({'success' => true})
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should work when using ssl_ca_file with a self-signed CA" do
|
34
|
+
expect(ssl_verify_test(:ssl_ca_file, "selfsigned.crt", "selfsigned.crt").parsed_response).to eq({'success' => true})
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should work when using ssl_ca_file with a certificate authority" do
|
38
|
+
expect(ssl_verify_test(:ssl_ca_file, "ca.crt", "server.crt").parsed_response).to eq({'success' => true})
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should work when using ssl_ca_path with a certificate authority" do
|
42
|
+
http = Net::HTTP.new('www.google.com', 443)
|
43
|
+
response = double(Net::HTTPResponse, :[] => '', body: '', to_hash: {})
|
44
|
+
allow(http).to receive(:request).and_return(response)
|
45
|
+
expect(Net::HTTP).to receive(:new).with('www.google.com', 443).and_return(http)
|
46
|
+
expect(http).to receive(:ca_path=).with('/foo/bar')
|
47
|
+
HTTParty.get('https://www.google.com', ssl_ca_path: '/foo/bar')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should fail when using ssl_ca_file and the server uses an unrecognized certificate authority" do
|
51
|
+
expect do
|
52
|
+
ssl_verify_test(:ssl_ca_file, "ca.crt", "selfsigned.crt")
|
53
|
+
end.to raise_error(OpenSSL::SSL::SSLError)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should fail when using ssl_ca_path and the server uses an unrecognized certificate authority" do
|
57
|
+
expect do
|
58
|
+
ssl_verify_test(:ssl_ca_path, ".", "selfsigned.crt")
|
59
|
+
end.to raise_error(OpenSSL::SSL::SSLError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should fail when using ssl_ca_file and the server uses a bogus hostname" do
|
63
|
+
expect do
|
64
|
+
ssl_verify_test(:ssl_ca_file, "ca.crt", "bogushost.crt")
|
65
|
+
end.to raise_error(OpenSSL::SSL::SSLError)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should fail when using ssl_ca_path and the server uses a bogus hostname" do
|
69
|
+
expect do
|
70
|
+
ssl_verify_test(:ssl_ca_path, ".", "bogushost.crt")
|
71
|
+
end.to raise_error(OpenSSL::SSL::SSLError)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,823 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
RSpec.describe HTTParty do
|
4
|
+
before(:each) do
|
5
|
+
@klass = Class.new
|
6
|
+
@klass.instance_eval { include HTTParty }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "pem" do
|
10
|
+
it 'should set the pem content' do
|
11
|
+
@klass.pem 'PEM-CONTENT'
|
12
|
+
expect(@klass.default_options[:pem]).to eq('PEM-CONTENT')
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should set the password to nil if it's not provided" do
|
16
|
+
@klass.pem 'PEM-CONTENT'
|
17
|
+
expect(@klass.default_options[:pem_password]).to be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should set the password' do
|
21
|
+
@klass.pem 'PEM-CONTENT', 'PASSWORD'
|
22
|
+
expect(@klass.default_options[:pem_password]).to eq('PASSWORD')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "pkcs12" do
|
27
|
+
it 'should set the p12 content' do
|
28
|
+
@klass.pkcs12 'P12-CONTENT', 'PASSWORD'
|
29
|
+
expect(@klass.default_options[:p12]).to eq('P12-CONTENT')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should set the password' do
|
33
|
+
@klass.pkcs12 'P12-CONTENT', 'PASSWORD'
|
34
|
+
expect(@klass.default_options[:p12_password]).to eq('PASSWORD')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'ssl_version' do
|
39
|
+
it 'should set the ssl_version content' do
|
40
|
+
@klass.ssl_version :SSLv3
|
41
|
+
expect(@klass.default_options[:ssl_version]).to eq(:SSLv3)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'ciphers' do
|
46
|
+
it 'should set the ciphers content' do
|
47
|
+
expect(@klass.default_options[:ciphers]).to be_nil
|
48
|
+
@klass.ciphers 'RC4-SHA'
|
49
|
+
expect(@klass.default_options[:ciphers]).to eq('RC4-SHA')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'http_proxy' do
|
54
|
+
it 'should set the address' do
|
55
|
+
@klass.http_proxy 'proxy.foo.com', 80
|
56
|
+
options = @klass.default_options
|
57
|
+
expect(options[:http_proxyaddr]).to eq('proxy.foo.com')
|
58
|
+
expect(options[:http_proxyport]).to eq(80)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should set the proxy user and pass when they are provided' do
|
62
|
+
@klass.http_proxy 'proxy.foo.com', 80, 'user', 'pass'
|
63
|
+
options = @klass.default_options
|
64
|
+
expect(options[:http_proxyuser]).to eq('user')
|
65
|
+
expect(options[:http_proxypass]).to eq('pass')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "base uri" do
|
70
|
+
before(:each) do
|
71
|
+
@klass.base_uri('api.foo.com/v1')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should have reader" do
|
75
|
+
expect(@klass.base_uri).to eq('http://api.foo.com/v1')
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should have writer' do
|
79
|
+
@klass.base_uri('http://api.foobar.com')
|
80
|
+
expect(@klass.base_uri).to eq('http://api.foobar.com')
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should not modify the parameter during assignment' do
|
84
|
+
uri = 'http://api.foobar.com'
|
85
|
+
@klass.base_uri(uri)
|
86
|
+
expect(uri).to eq('http://api.foobar.com')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe ".disable_rails_query_string_format" do
|
91
|
+
it "sets the query string normalizer to HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER" do
|
92
|
+
@klass.disable_rails_query_string_format
|
93
|
+
expect(@klass.default_options[:query_string_normalizer]).to eq(HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe ".normalize_base_uri" do
|
98
|
+
it "should add http if not present for non ssl requests" do
|
99
|
+
uri = HTTParty.normalize_base_uri('api.foobar.com')
|
100
|
+
expect(uri).to eq('http://api.foobar.com')
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should add https if not present for ssl requests" do
|
104
|
+
uri = HTTParty.normalize_base_uri('api.foo.com/v1:443')
|
105
|
+
expect(uri).to eq('https://api.foo.com/v1:443')
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should not remove https for ssl requests" do
|
109
|
+
uri = HTTParty.normalize_base_uri('https://api.foo.com/v1:443')
|
110
|
+
expect(uri).to eq('https://api.foo.com/v1:443')
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should not modify the parameter' do
|
114
|
+
uri = 'http://api.foobar.com'
|
115
|
+
HTTParty.normalize_base_uri(uri)
|
116
|
+
expect(uri).to eq('http://api.foobar.com')
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should not treat uri's with a port of 4430 as ssl" do
|
120
|
+
uri = HTTParty.normalize_base_uri('http://api.foo.com:4430/v1')
|
121
|
+
expect(uri).to eq('http://api.foo.com:4430/v1')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "headers" do
|
126
|
+
def expect_headers(header = {})
|
127
|
+
expect(HTTParty::Request).to receive(:new) \
|
128
|
+
.with(anything, anything, hash_including({ headers: header })) \
|
129
|
+
.and_return(double("mock response", perform: nil))
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should default to empty hash" do
|
133
|
+
expect(@klass.headers).to eq({})
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should be able to be updated" do
|
137
|
+
init_headers = {foo: 'bar', baz: 'spax'}
|
138
|
+
@klass.headers init_headers
|
139
|
+
expect(@klass.headers).to eq(init_headers)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "uses the class headers when sending a request" do
|
143
|
+
expect_headers(foo: 'bar')
|
144
|
+
@klass.headers(foo: 'bar')
|
145
|
+
@klass.get('')
|
146
|
+
end
|
147
|
+
|
148
|
+
it "merges class headers with request headers" do
|
149
|
+
expect_headers(baz: 'spax', foo: 'bar')
|
150
|
+
@klass.headers(foo: 'bar')
|
151
|
+
@klass.get('', headers: {baz: 'spax'})
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'overrides class headers with request headers' do
|
155
|
+
expect_headers(baz: 'spax', foo: 'baz')
|
156
|
+
@klass.headers(foo: 'bar')
|
157
|
+
@klass.get('', headers: {baz: 'spax', foo: 'baz'})
|
158
|
+
end
|
159
|
+
|
160
|
+
context "with cookies" do
|
161
|
+
it 'utilizes the class-level cookies' do
|
162
|
+
expect_headers(foo: 'bar', 'cookie' => 'type=snickerdoodle')
|
163
|
+
@klass.headers(foo: 'bar')
|
164
|
+
@klass.cookies(type: 'snickerdoodle')
|
165
|
+
@klass.get('')
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'adds cookies to the headers' do
|
169
|
+
expect_headers(foo: 'bar', 'cookie' => 'type=snickerdoodle')
|
170
|
+
@klass.headers(foo: 'bar')
|
171
|
+
@klass.get('', cookies: {type: 'snickerdoodle'})
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'doesnt modify default_options' do
|
175
|
+
expect(@klass.headers).to eq({})
|
176
|
+
expect_headers('cookie' => 'type=snickerdoodle')
|
177
|
+
@klass.get('', cookies: {type: 'snickerdoodle'})
|
178
|
+
expect(@klass.default_options[:headers]).to eq({})
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'adds optional cookies to the optional headers' do
|
182
|
+
expect_headers(baz: 'spax', 'cookie' => 'type=snickerdoodle')
|
183
|
+
@klass.get('', cookies: {type: 'snickerdoodle'}, headers: {baz: 'spax'})
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "cookies" do
|
189
|
+
def expect_cookie_header(s)
|
190
|
+
expect(HTTParty::Request).to receive(:new) \
|
191
|
+
.with(anything, anything, hash_including({ headers: { "cookie" => s } })) \
|
192
|
+
.and_return(double("mock response", perform: nil))
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should not be in the headers by default" do
|
196
|
+
allow(HTTParty::Request).to receive(:new).and_return(double(nil, perform: nil))
|
197
|
+
@klass.get("")
|
198
|
+
expect(@klass.headers.keys).not_to include("cookie")
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should raise an ArgumentError if passed a non-Hash" do
|
202
|
+
expect do
|
203
|
+
@klass.cookies("nonsense")
|
204
|
+
end.to raise_error(ArgumentError)
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should allow a cookie to be specified with a one-off request" do
|
208
|
+
expect_cookie_header "type=snickerdoodle"
|
209
|
+
@klass.get("", cookies: { type: "snickerdoodle" })
|
210
|
+
end
|
211
|
+
|
212
|
+
describe "when a cookie is set at the class level" do
|
213
|
+
before(:each) do
|
214
|
+
@klass.cookies({ type: "snickerdoodle" })
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should include that cookie in the request" do
|
218
|
+
expect_cookie_header "type=snickerdoodle"
|
219
|
+
@klass.get("")
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should pass the proper cookies when requested multiple times" do
|
223
|
+
2.times do
|
224
|
+
expect_cookie_header "type=snickerdoodle"
|
225
|
+
@klass.get("")
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should allow the class defaults to be overridden" do
|
230
|
+
expect_cookie_header "type=chocolate_chip"
|
231
|
+
|
232
|
+
@klass.get("", cookies: { type: "chocolate_chip" })
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe "in a class with multiple methods that use different cookies" do
|
237
|
+
before(:each) do
|
238
|
+
@klass.instance_eval do
|
239
|
+
def first_method
|
240
|
+
get("first_method", cookies: { first_method_cookie: "foo" })
|
241
|
+
end
|
242
|
+
|
243
|
+
def second_method
|
244
|
+
get("second_method", cookies: { second_method_cookie: "foo" })
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should not allow cookies used in one method to carry over into other methods" do
|
250
|
+
expect_cookie_header "first_method_cookie=foo"
|
251
|
+
@klass.first_method
|
252
|
+
|
253
|
+
expect_cookie_header "second_method_cookie=foo"
|
254
|
+
@klass.second_method
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe "default params" do
|
260
|
+
it "should default to empty hash" do
|
261
|
+
expect(@klass.default_params).to eq({})
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should be able to be updated" do
|
265
|
+
new_defaults = {foo: 'bar', baz: 'spax'}
|
266
|
+
@klass.default_params new_defaults
|
267
|
+
expect(@klass.default_params).to eq(new_defaults)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
describe "default timeout" do
|
272
|
+
it "should default to nil" do
|
273
|
+
expect(@klass.default_options[:timeout]).to eq(nil)
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should support updating" do
|
277
|
+
@klass.default_timeout 10
|
278
|
+
expect(@klass.default_options[:timeout]).to eq(10)
|
279
|
+
end
|
280
|
+
|
281
|
+
it "should support floats" do
|
282
|
+
@klass.default_timeout 0.5
|
283
|
+
expect(@klass.default_options[:timeout]).to eq(0.5)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe "debug_output" do
|
288
|
+
it "stores the given stream as a default_option" do
|
289
|
+
@klass.debug_output $stdout
|
290
|
+
expect(@klass.default_options[:debug_output]).to eq($stdout)
|
291
|
+
end
|
292
|
+
|
293
|
+
it "stores the $stderr stream by default" do
|
294
|
+
@klass.debug_output
|
295
|
+
expect(@klass.default_options[:debug_output]).to eq($stderr)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
describe "basic http authentication" do
|
300
|
+
it "should work" do
|
301
|
+
@klass.basic_auth 'foobar', 'secret'
|
302
|
+
expect(@klass.default_options[:basic_auth]).to eq({username: 'foobar', password: 'secret'})
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "digest http authentication" do
|
307
|
+
it "should work" do
|
308
|
+
@klass.digest_auth 'foobar', 'secret'
|
309
|
+
expect(@klass.default_options[:digest_auth]).to eq({username: 'foobar', password: 'secret'})
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe "parser" do
|
314
|
+
class CustomParser
|
315
|
+
def self.parse(body)
|
316
|
+
{sexy: true}
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
let(:parser) do
|
321
|
+
proc { |data, format| CustomParser.parse(data) }
|
322
|
+
end
|
323
|
+
|
324
|
+
it "should set parser options" do
|
325
|
+
@klass.parser parser
|
326
|
+
expect(@klass.default_options[:parser]).to eq(parser)
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should be able parse response with custom parser" do
|
330
|
+
@klass.parser parser
|
331
|
+
FakeWeb.register_uri(:get, 'http://twitter.com/statuses/public_timeline.xml', body: 'tweets')
|
332
|
+
custom_parsed_response = @klass.get('http://twitter.com/statuses/public_timeline.xml')
|
333
|
+
expect(custom_parsed_response[:sexy]).to eq(true)
|
334
|
+
end
|
335
|
+
|
336
|
+
it "raises UnsupportedFormat when the parser cannot handle the format" do
|
337
|
+
@klass.format :json
|
338
|
+
class MyParser < HTTParty::Parser
|
339
|
+
SupportedFormats = {}
|
340
|
+
end unless defined?(MyParser)
|
341
|
+
expect do
|
342
|
+
@klass.parser MyParser
|
343
|
+
end.to raise_error(HTTParty::UnsupportedFormat)
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'does not validate format whe custom parser is a proc' do
|
347
|
+
expect do
|
348
|
+
@klass.format :json
|
349
|
+
@klass.parser lambda {|body, format|}
|
350
|
+
end.to_not raise_error
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
describe "uri_adapter" do
|
355
|
+
|
356
|
+
require 'forwardable'
|
357
|
+
class CustomURIAdaptor
|
358
|
+
extend Forwardable
|
359
|
+
def_delegators :@uri, :userinfo, :relative?, :query, :query=, :scheme, :path, :host, :port
|
360
|
+
|
361
|
+
def initialize uri
|
362
|
+
@uri = uri
|
363
|
+
end
|
364
|
+
|
365
|
+
def self.parse uri
|
366
|
+
new URI.parse uri
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
let(:uri_adapter) { CustomURIAdaptor }
|
371
|
+
|
372
|
+
it "should set the uri_adapter" do
|
373
|
+
@klass.uri_adapter uri_adapter
|
374
|
+
expect(@klass.default_options[:uri_adapter]).to be uri_adapter
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should raise an ArgumentError if uri_adapter doesn't implement parse method" do
|
378
|
+
expect do
|
379
|
+
@klass.uri_adapter double()
|
380
|
+
end.to raise_error(ArgumentError)
|
381
|
+
end
|
382
|
+
|
383
|
+
|
384
|
+
it "should process a request with a uri instance parsed from the uri_adapter" do
|
385
|
+
uri = 'http://foo.com/bar'
|
386
|
+
FakeWeb.register_uri(:get, uri, body: 'stuff')
|
387
|
+
@klass.uri_adapter uri_adapter
|
388
|
+
expect(@klass.get(uri).parsed_response).to eq('stuff')
|
389
|
+
end
|
390
|
+
|
391
|
+
end
|
392
|
+
|
393
|
+
describe "connection_adapter" do
|
394
|
+
let(:uri) { 'http://google.com/api.json' }
|
395
|
+
let(:connection_adapter) { double('CustomConnectionAdapter') }
|
396
|
+
|
397
|
+
it "should set the connection_adapter" do
|
398
|
+
@klass.connection_adapter connection_adapter
|
399
|
+
expect(@klass.default_options[:connection_adapter]).to be connection_adapter
|
400
|
+
end
|
401
|
+
|
402
|
+
it "should set the connection_adapter_options when provided" do
|
403
|
+
options = {foo: :bar}
|
404
|
+
@klass.connection_adapter connection_adapter, options
|
405
|
+
expect(@klass.default_options[:connection_adapter_options]).to be options
|
406
|
+
end
|
407
|
+
|
408
|
+
it "should not set the connection_adapter_options when not provided" do
|
409
|
+
@klass.connection_adapter connection_adapter
|
410
|
+
expect(@klass.default_options[:connection_adapter_options]).to be_nil
|
411
|
+
end
|
412
|
+
|
413
|
+
it "should process a request with a connection from the adapter" do
|
414
|
+
connection_adapter_options = {foo: :bar}
|
415
|
+
expect(connection_adapter).to receive(:call) { |u, o|
|
416
|
+
expect(o[:connection_adapter_options]).to eq(connection_adapter_options)
|
417
|
+
HTTParty::ConnectionAdapter.call(u, o)
|
418
|
+
}.with(URI.parse(uri), kind_of(Hash))
|
419
|
+
FakeWeb.register_uri(:get, uri, body: 'stuff')
|
420
|
+
@klass.connection_adapter connection_adapter, connection_adapter_options
|
421
|
+
expect(@klass.get(uri).parsed_response).to eq('stuff')
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
describe "format" do
|
426
|
+
it "should allow xml" do
|
427
|
+
@klass.format :xml
|
428
|
+
expect(@klass.default_options[:format]).to eq(:xml)
|
429
|
+
end
|
430
|
+
|
431
|
+
it "should allow csv" do
|
432
|
+
@klass.format :csv
|
433
|
+
expect(@klass.default_options[:format]).to eq(:csv)
|
434
|
+
end
|
435
|
+
|
436
|
+
it "should allow json" do
|
437
|
+
@klass.format :json
|
438
|
+
expect(@klass.default_options[:format]).to eq(:json)
|
439
|
+
end
|
440
|
+
|
441
|
+
it "should allow plain" do
|
442
|
+
@klass.format :plain
|
443
|
+
expect(@klass.default_options[:format]).to eq(:plain)
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'should not allow funky format' do
|
447
|
+
expect do
|
448
|
+
@klass.format :foobar
|
449
|
+
end.to raise_error(HTTParty::UnsupportedFormat)
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'should only print each format once with an exception' do
|
453
|
+
expect do
|
454
|
+
@klass.format :foobar
|
455
|
+
end.to raise_error(HTTParty::UnsupportedFormat, "':foobar' Must be one of: csv, html, json, plain, xml")
|
456
|
+
end
|
457
|
+
|
458
|
+
it 'sets the default parser' do
|
459
|
+
expect(@klass.default_options[:parser]).to be_nil
|
460
|
+
@klass.format :json
|
461
|
+
expect(@klass.default_options[:parser]).to eq(HTTParty::Parser)
|
462
|
+
end
|
463
|
+
|
464
|
+
it 'does not reset parser to the default parser' do
|
465
|
+
my_parser = lambda {}
|
466
|
+
@klass.parser my_parser
|
467
|
+
@klass.format :json
|
468
|
+
expect(@klass.parser).to eq(my_parser)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
describe "#no_follow" do
|
473
|
+
it "sets no_follow to false by default" do
|
474
|
+
@klass.no_follow
|
475
|
+
expect(@klass.default_options[:no_follow]).to be_falsey
|
476
|
+
end
|
477
|
+
|
478
|
+
it "sets the no_follow option to true" do
|
479
|
+
@klass.no_follow true
|
480
|
+
expect(@klass.default_options[:no_follow]).to be_truthy
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
describe "#maintain_method_across_redirects" do
|
485
|
+
it "sets maintain_method_across_redirects to true by default" do
|
486
|
+
@klass.maintain_method_across_redirects
|
487
|
+
expect(@klass.default_options[:maintain_method_across_redirects]).to be_truthy
|
488
|
+
end
|
489
|
+
|
490
|
+
it "sets the maintain_method_across_redirects option to false" do
|
491
|
+
@klass.maintain_method_across_redirects false
|
492
|
+
expect(@klass.default_options[:maintain_method_across_redirects]).to be_falsey
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
describe "#resend_on_redirect" do
|
497
|
+
it "sets resend_on_redirect to true by default" do
|
498
|
+
@klass.resend_on_redirect
|
499
|
+
expect(@klass.default_options[:resend_on_redirect]).to be_truthy
|
500
|
+
end
|
501
|
+
|
502
|
+
it "sets resend_on_redirect option to false" do
|
503
|
+
@klass.resend_on_redirect false
|
504
|
+
expect(@klass.default_options[:resend_on_redirect]).to be_falsey
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
describe ".follow_redirects" do
|
509
|
+
it "sets follow redirects to true by default" do
|
510
|
+
@klass.follow_redirects
|
511
|
+
expect(@klass.default_options[:follow_redirects]).to be_truthy
|
512
|
+
end
|
513
|
+
|
514
|
+
it "sets the follow_redirects option to false" do
|
515
|
+
@klass.follow_redirects false
|
516
|
+
expect(@klass.default_options[:follow_redirects]).to be_falsey
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
describe ".query_string_normalizer" do
|
521
|
+
it "sets the query_string_normalizer option" do
|
522
|
+
normalizer = proc {}
|
523
|
+
@klass.query_string_normalizer normalizer
|
524
|
+
expect(@klass.default_options[:query_string_normalizer]).to eq(normalizer)
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
describe "with explicit override of automatic redirect handling" do
|
529
|
+
before do
|
530
|
+
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', format: :xml, no_follow: true)
|
531
|
+
@redirect = stub_response 'first redirect', 302
|
532
|
+
@redirect['location'] = 'http://foo.com/bar'
|
533
|
+
allow(HTTParty::Request).to receive_messages(new: @request)
|
534
|
+
end
|
535
|
+
|
536
|
+
it "should fail with redirected GET" do
|
537
|
+
expect do
|
538
|
+
@error = @klass.get('/foo', no_follow: true)
|
539
|
+
end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
|
540
|
+
end
|
541
|
+
|
542
|
+
it "should fail with redirected POST" do
|
543
|
+
expect do
|
544
|
+
@klass.post('/foo', no_follow: true)
|
545
|
+
end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
|
546
|
+
end
|
547
|
+
|
548
|
+
it "should fail with redirected PATCH" do
|
549
|
+
expect do
|
550
|
+
@klass.patch('/foo', no_follow: true)
|
551
|
+
end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
|
552
|
+
end
|
553
|
+
|
554
|
+
it "should fail with redirected DELETE" do
|
555
|
+
expect do
|
556
|
+
@klass.delete('/foo', no_follow: true)
|
557
|
+
end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
|
558
|
+
end
|
559
|
+
|
560
|
+
it "should fail with redirected MOVE" do
|
561
|
+
expect do
|
562
|
+
@klass.move('/foo', no_follow: true)
|
563
|
+
end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
|
564
|
+
end
|
565
|
+
|
566
|
+
it "should fail with redirected COPY" do
|
567
|
+
expect do
|
568
|
+
@klass.copy('/foo', no_follow: true)
|
569
|
+
end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
|
570
|
+
end
|
571
|
+
|
572
|
+
it "should fail with redirected PUT" do
|
573
|
+
expect do
|
574
|
+
@klass.put('/foo', no_follow: true)
|
575
|
+
end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
|
576
|
+
end
|
577
|
+
|
578
|
+
it "should fail with redirected HEAD" do
|
579
|
+
expect do
|
580
|
+
@klass.head('/foo', no_follow: true)
|
581
|
+
end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
|
582
|
+
end
|
583
|
+
|
584
|
+
it "should fail with redirected OPTIONS" do
|
585
|
+
expect do
|
586
|
+
@klass.options('/foo', no_follow: true)
|
587
|
+
end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
describe "with multiple class definitions" do
|
592
|
+
before(:each) do
|
593
|
+
@klass.instance_eval do
|
594
|
+
base_uri "http://first.com"
|
595
|
+
default_params one: 1
|
596
|
+
end
|
597
|
+
|
598
|
+
@additional_klass = Class.new
|
599
|
+
@additional_klass.instance_eval do
|
600
|
+
include HTTParty
|
601
|
+
base_uri "http://second.com"
|
602
|
+
default_params two: 2
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
it "should not run over each others options" do
|
607
|
+
expect(@klass.default_options).to eq({ base_uri: 'http://first.com', default_params: { one: 1 } })
|
608
|
+
expect(@additional_klass.default_options).to eq({ base_uri: 'http://second.com', default_params: { two: 2 } })
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
describe "two child classes inheriting from one parent" do
|
613
|
+
before(:each) do
|
614
|
+
@parent = Class.new do
|
615
|
+
include HTTParty
|
616
|
+
def self.name
|
617
|
+
"Parent"
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
@child1 = Class.new(@parent)
|
622
|
+
@child2 = Class.new(@parent)
|
623
|
+
end
|
624
|
+
|
625
|
+
it "does not modify each others inherited attributes" do
|
626
|
+
@child1.default_params joe: "alive"
|
627
|
+
@child2.default_params joe: "dead"
|
628
|
+
|
629
|
+
expect(@child1.default_options).to eq({ default_params: {joe: "alive"} })
|
630
|
+
expect(@child2.default_options).to eq({ default_params: {joe: "dead"} })
|
631
|
+
|
632
|
+
expect(@parent.default_options).to eq({ })
|
633
|
+
end
|
634
|
+
|
635
|
+
it "inherits default_options from the superclass" do
|
636
|
+
@parent.basic_auth 'user', 'password'
|
637
|
+
expect(@child1.default_options).to eq({basic_auth: {username: 'user', password: 'password'}})
|
638
|
+
@child1.basic_auth 'u', 'p' # modifying child1 has no effect on child2
|
639
|
+
expect(@child2.default_options).to eq({basic_auth: {username: 'user', password: 'password'}})
|
640
|
+
end
|
641
|
+
|
642
|
+
it "doesn't modify the parent's default options" do
|
643
|
+
@parent.basic_auth 'user', 'password'
|
644
|
+
|
645
|
+
@child1.basic_auth 'u', 'p'
|
646
|
+
expect(@child1.default_options).to eq({basic_auth: {username: 'u', password: 'p'}})
|
647
|
+
|
648
|
+
@child1.basic_auth 'email', 'token'
|
649
|
+
expect(@child1.default_options).to eq({basic_auth: {username: 'email', password: 'token'}})
|
650
|
+
|
651
|
+
expect(@parent.default_options).to eq({basic_auth: {username: 'user', password: 'password'}})
|
652
|
+
end
|
653
|
+
|
654
|
+
it "doesn't modify hashes in the parent's default options" do
|
655
|
+
@parent.headers 'Accept' => 'application/json'
|
656
|
+
@child1.headers 'Accept' => 'application/xml'
|
657
|
+
|
658
|
+
expect(@parent.default_options[:headers]).to eq({'Accept' => 'application/json'})
|
659
|
+
expect(@child1.default_options[:headers]).to eq({'Accept' => 'application/xml'})
|
660
|
+
end
|
661
|
+
|
662
|
+
it "works with lambda values" do
|
663
|
+
@child1.default_options[:imaginary_option] = lambda { "This is a new lambda "}
|
664
|
+
expect(@child1.default_options[:imaginary_option]).to be_a Proc
|
665
|
+
end
|
666
|
+
|
667
|
+
it 'should dup the proc on the child class' do
|
668
|
+
imaginary_option = lambda { 2 * 3.14 }
|
669
|
+
@parent.default_options[:imaginary_option] = imaginary_option
|
670
|
+
expect(@parent.default_options[:imaginary_option].call).to eq(imaginary_option.call)
|
671
|
+
@child1.default_options[:imaginary_option]
|
672
|
+
expect(@child1.default_options[:imaginary_option].call).to eq(imaginary_option.call)
|
673
|
+
expect(@child1.default_options[:imaginary_option]).not_to be_equal imaginary_option
|
674
|
+
end
|
675
|
+
|
676
|
+
it "inherits default_cookies from the parent class" do
|
677
|
+
@parent.cookies 'type' => 'chocolate_chip'
|
678
|
+
expect(@child1.default_cookies).to eq({"type" => "chocolate_chip"})
|
679
|
+
@child1.cookies 'type' => 'snickerdoodle'
|
680
|
+
expect(@child1.default_cookies).to eq({"type" => "snickerdoodle"})
|
681
|
+
expect(@child2.default_cookies).to eq({"type" => "chocolate_chip"})
|
682
|
+
end
|
683
|
+
|
684
|
+
it "doesn't modify the parent's default cookies" do
|
685
|
+
@parent.cookies 'type' => 'chocolate_chip'
|
686
|
+
|
687
|
+
@child1.cookies 'type' => 'snickerdoodle'
|
688
|
+
expect(@child1.default_cookies).to eq({"type" => "snickerdoodle"})
|
689
|
+
|
690
|
+
expect(@parent.default_cookies).to eq({"type" => "chocolate_chip"})
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
describe "grand parent with inherited callback" do
|
695
|
+
before do
|
696
|
+
@grand_parent = Class.new do
|
697
|
+
def self.inherited(subclass)
|
698
|
+
subclass.instance_variable_set(:@grand_parent, true)
|
699
|
+
end
|
700
|
+
end
|
701
|
+
@parent = Class.new(@grand_parent) do
|
702
|
+
include HTTParty
|
703
|
+
end
|
704
|
+
end
|
705
|
+
it "continues running the #inherited on the parent" do
|
706
|
+
child = Class.new(@parent)
|
707
|
+
expect(child.instance_variable_get(:@grand_parent)).to be_truthy
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
describe "#get" do
|
712
|
+
it "should be able to get html" do
|
713
|
+
stub_http_response_with('google.html')
|
714
|
+
expect(HTTParty.get('http://www.google.com').parsed_response).to eq(file_fixture('google.html'))
|
715
|
+
end
|
716
|
+
|
717
|
+
it "should be able to get chunked html" do
|
718
|
+
chunks = %w(Chunk1 Chunk2 Chunk3 Chunk4)
|
719
|
+
stub_chunked_http_response_with(chunks)
|
720
|
+
|
721
|
+
expect(
|
722
|
+
HTTParty.get('http://www.google.com') do |fragment|
|
723
|
+
expect(chunks).to include(fragment)
|
724
|
+
end.parsed_response
|
725
|
+
).to eq(chunks.join)
|
726
|
+
end
|
727
|
+
|
728
|
+
it "should return an empty body if stream_body option is turned on" do
|
729
|
+
chunks = %w(Chunk1 Chunk2 Chunk3 Chunk4)
|
730
|
+
options = {stream_body: true, format: 'html'}
|
731
|
+
stub_chunked_http_response_with(chunks, options)
|
732
|
+
|
733
|
+
expect(
|
734
|
+
HTTParty.get('http://www.google.com', options) do |fragment|
|
735
|
+
expect(chunks).to include(fragment)
|
736
|
+
end.parsed_response
|
737
|
+
).to eq(nil)
|
738
|
+
end
|
739
|
+
|
740
|
+
it "should be able parse response type json automatically" do
|
741
|
+
stub_http_response_with('twitter.json')
|
742
|
+
tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
|
743
|
+
expect(tweets.size).to eq(20)
|
744
|
+
expect(tweets.first['user']).to eq({
|
745
|
+
"name" => "Pyk",
|
746
|
+
"url" => nil,
|
747
|
+
"id" => "7694602",
|
748
|
+
"description" => nil,
|
749
|
+
"protected" => false,
|
750
|
+
"screen_name" => "Pyk",
|
751
|
+
"followers_count" => 1,
|
752
|
+
"location" => "Opera Plaza, California",
|
753
|
+
"profile_image_url" => "http://static.twitter.com/images/default_profile_normal.png"
|
754
|
+
})
|
755
|
+
end
|
756
|
+
|
757
|
+
it "should be able parse response type xml automatically" do
|
758
|
+
stub_http_response_with('twitter.xml')
|
759
|
+
tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.xml')
|
760
|
+
expect(tweets['statuses'].size).to eq(20)
|
761
|
+
expect(tweets['statuses'].first['user']).to eq({
|
762
|
+
"name" => "Magic 8 Bot",
|
763
|
+
"url" => nil,
|
764
|
+
"id" => "17656026",
|
765
|
+
"description" => "ask me a question",
|
766
|
+
"protected" => "false",
|
767
|
+
"screen_name" => "magic8bot",
|
768
|
+
"followers_count" => "90",
|
769
|
+
"profile_image_url" => "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg",
|
770
|
+
"location" => nil
|
771
|
+
})
|
772
|
+
end
|
773
|
+
|
774
|
+
it "should be able parse response type csv automatically" do
|
775
|
+
stub_http_response_with('twitter.csv')
|
776
|
+
profile = HTTParty.get('http://twitter.com/statuses/profile.csv')
|
777
|
+
expect(profile.size).to eq(2)
|
778
|
+
expect(profile[0]).to eq(%w(name url id description protected screen_name followers_count profile_image_url location))
|
779
|
+
expect(profile[1]).to eq(["Magic 8 Bot", nil, "17656026", "ask me a question", "false", "magic8bot", "90", "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg", nil])
|
780
|
+
end
|
781
|
+
|
782
|
+
it "should not get undefined method add_node for nil class for the following xml" do
|
783
|
+
stub_http_response_with('undefined_method_add_node_for_nil.xml')
|
784
|
+
result = HTTParty.get('http://foobar.com')
|
785
|
+
expect(result.parsed_response).to eq({"Entities" => {"href" => "https://s3-sandbox.parature.com/api/v1/5578/5633/Account", "results" => "0", "total" => "0", "page_size" => "25", "page" => "1"}})
|
786
|
+
end
|
787
|
+
|
788
|
+
it "should parse empty response fine" do
|
789
|
+
stub_http_response_with('empty.xml')
|
790
|
+
result = HTTParty.get('http://foobar.com')
|
791
|
+
expect(result).to be_nil
|
792
|
+
end
|
793
|
+
|
794
|
+
it "should accept http URIs" do
|
795
|
+
stub_http_response_with('google.html')
|
796
|
+
expect do
|
797
|
+
HTTParty.get('http://google.com')
|
798
|
+
end.not_to raise_error
|
799
|
+
end
|
800
|
+
|
801
|
+
it "should accept https URIs" do
|
802
|
+
stub_http_response_with('google.html')
|
803
|
+
expect do
|
804
|
+
HTTParty.get('https://google.com')
|
805
|
+
end.not_to raise_error
|
806
|
+
end
|
807
|
+
|
808
|
+
it "should accept webcal URIs" do
|
809
|
+
uri = 'http://google.com/'
|
810
|
+
FakeWeb.register_uri(:get, uri, body: 'stuff')
|
811
|
+
uri = 'webcal://google.com/'
|
812
|
+
expect do
|
813
|
+
HTTParty.get(uri)
|
814
|
+
end.not_to raise_error
|
815
|
+
end
|
816
|
+
|
817
|
+
it "should raise an InvalidURIError on URIs that can't be parsed at all" do
|
818
|
+
expect do
|
819
|
+
HTTParty.get("It's the one that says 'Bad URI'")
|
820
|
+
end.to raise_error(URI::InvalidURIError)
|
821
|
+
end
|
822
|
+
end
|
823
|
+
end
|