httsoiree 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +7 -0
- data/Gemfile +14 -0
- data/Guardfile +16 -0
- data/History +303 -0
- data/MIT-LICENSE +20 -0
- data/README.md +77 -0
- data/Rakefile +12 -0
- data/bin/httparty +117 -0
- data/cucumber.yml +1 -0
- data/examples/aaws.rb +32 -0
- data/examples/basic.rb +28 -0
- data/examples/crack.rb +19 -0
- data/examples/custom_parsers.rb +67 -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 +38 -0
- data/examples/nokogiri_html_parser.rb +22 -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 +7 -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 +22 -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 +74 -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 +25 -0
- data/lib/httparty/connection_adapter.rb +188 -0
- data/lib/httparty/cookie_hash.rb +22 -0
- data/lib/httparty/core_extensions.rb +32 -0
- data/lib/httparty/exceptions.rb +29 -0
- data/lib/httparty/hash_conversions.rb +51 -0
- data/lib/httparty/logger/apache_logger.rb +22 -0
- data/lib/httparty/logger/curl_logger.rb +48 -0
- data/lib/httparty/logger/logger.rb +18 -0
- data/lib/httparty/module_inheritable_attributes.rb +56 -0
- data/lib/httparty/net_digest_auth.rb +84 -0
- data/lib/httparty/parser.rb +141 -0
- data/lib/httparty/request.rb +339 -0
- data/lib/httparty/response/headers.rb +31 -0
- data/lib/httparty/response.rb +72 -0
- data/lib/httparty/version.rb +3 -0
- data/lib/httparty.rb +618 -0
- data/lib/httsoiree.rb +3 -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 +370 -0
- data/spec/httparty/cookie_hash_spec.rb +83 -0
- data/spec/httparty/exception_spec.rb +23 -0
- data/spec/httparty/logger/apache_logger_spec.rb +41 -0
- data/spec/httparty/logger/curl_logger_spec.rb +18 -0
- data/spec/httparty/logger/logger_spec.rb +22 -0
- data/spec/httparty/net_digest_auth_spec.rb +152 -0
- data/spec/httparty/parser_spec.rb +165 -0
- data/spec/httparty/request_spec.rb +774 -0
- data/spec/httparty/response_spec.rb +221 -0
- data/spec/httparty/ssl_spec.rb +74 -0
- data/spec/httparty_spec.rb +783 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +37 -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 +215 -0
@@ -0,0 +1,783 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
describe HTTParty do
|
4
|
+
before(:each) do
|
5
|
+
@klass = Class.new
|
6
|
+
@klass.instance_eval { include HTTParty }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "AllowedFormats deprecated" do
|
10
|
+
before do
|
11
|
+
Kernel.stub(:warn)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "warns with a deprecation message" do
|
15
|
+
Kernel.should_receive(:warn).with("Deprecated: Use HTTParty::Parser::SupportedFormats")
|
16
|
+
HTTParty::AllowedFormats
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns HTTPart::Parser::SupportedFormats" do
|
20
|
+
HTTParty::AllowedFormats.should == HTTParty::Parser::SupportedFormats
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "pem" do
|
25
|
+
it 'should set the pem content' do
|
26
|
+
@klass.pem 'PEM-CONTENT'
|
27
|
+
@klass.default_options[:pem].should == 'PEM-CONTENT'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should set the password to nil if it's not provided" do
|
31
|
+
@klass.pem 'PEM-CONTENT'
|
32
|
+
@klass.default_options[:pem_password].should be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should set the password' do
|
36
|
+
@klass.pem 'PEM-CONTENT', 'PASSWORD'
|
37
|
+
@klass.default_options[:pem_password].should == 'PASSWORD'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "pkcs12" do
|
42
|
+
it 'should set the p12 content' do
|
43
|
+
@klass.pkcs12 'P12-CONTENT', 'PASSWORD'
|
44
|
+
@klass.default_options[:p12].should == 'P12-CONTENT'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should set the password' do
|
48
|
+
@klass.pkcs12 'P12-CONTENT', 'PASSWORD'
|
49
|
+
@klass.default_options[:p12_password].should == 'PASSWORD'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'ssl_version' do
|
54
|
+
it 'should set the ssl_version content' do
|
55
|
+
@klass.ssl_version :SSLv3
|
56
|
+
@klass.default_options[:ssl_version].should == :SSLv3
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'ciphers' do
|
61
|
+
it 'should set the ciphers content' do
|
62
|
+
@klass.default_options[:ciphers].should be_nil
|
63
|
+
@klass.ciphers 'RC4-SHA'
|
64
|
+
@klass.default_options[:ciphers].should == 'RC4-SHA'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'http_proxy' do
|
69
|
+
it 'should set the address' do
|
70
|
+
@klass.http_proxy 'proxy.foo.com', 80
|
71
|
+
options = @klass.default_options
|
72
|
+
options[:http_proxyaddr].should == 'proxy.foo.com'
|
73
|
+
options[:http_proxyport].should == 80
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should set the proxy user and pass when they are provided' do
|
77
|
+
@klass.http_proxy 'proxy.foo.com', 80, 'user', 'pass'
|
78
|
+
options = @klass.default_options
|
79
|
+
options[:http_proxyuser].should == 'user'
|
80
|
+
options[:http_proxypass].should == 'pass'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "base uri" do
|
85
|
+
before(:each) do
|
86
|
+
@klass.base_uri('api.foo.com/v1')
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should have reader" do
|
90
|
+
@klass.base_uri.should == 'http://api.foo.com/v1'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should have writer' do
|
94
|
+
@klass.base_uri('http://api.foobar.com')
|
95
|
+
@klass.base_uri.should == 'http://api.foobar.com'
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should not modify the parameter during assignment' do
|
99
|
+
uri = 'http://api.foobar.com'
|
100
|
+
@klass.base_uri(uri)
|
101
|
+
uri.should == 'http://api.foobar.com'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe ".disable_rails_query_string_format" do
|
106
|
+
it "sets the query string normalizer to HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER" do
|
107
|
+
@klass.disable_rails_query_string_format
|
108
|
+
@klass.default_options[:query_string_normalizer].should == HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe ".normalize_base_uri" do
|
113
|
+
it "should add http if not present for non ssl requests" do
|
114
|
+
uri = HTTParty.normalize_base_uri('api.foobar.com')
|
115
|
+
uri.should == 'http://api.foobar.com'
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should add https if not present for ssl requests" do
|
119
|
+
uri = HTTParty.normalize_base_uri('api.foo.com/v1:443')
|
120
|
+
uri.should == 'https://api.foo.com/v1:443'
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should not remove https for ssl requests" do
|
124
|
+
uri = HTTParty.normalize_base_uri('https://api.foo.com/v1:443')
|
125
|
+
uri.should == 'https://api.foo.com/v1:443'
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should not modify the parameter' do
|
129
|
+
uri = 'http://api.foobar.com'
|
130
|
+
HTTParty.normalize_base_uri(uri)
|
131
|
+
uri.should == 'http://api.foobar.com'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should not treat uri's with a port of 4430 as ssl" do
|
135
|
+
uri = HTTParty.normalize_base_uri('http://api.foo.com:4430/v1')
|
136
|
+
uri.should == 'http://api.foo.com:4430/v1'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "headers" do
|
141
|
+
def expect_headers(header={})
|
142
|
+
HTTParty::Request.should_receive(:new) \
|
143
|
+
.with(anything, anything, hash_including({ headers: header })) \
|
144
|
+
.and_return(mock("mock response", perform: nil))
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should default to empty hash" do
|
148
|
+
@klass.headers.should == {}
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should be able to be updated" do
|
152
|
+
init_headers = {foo: 'bar', baz: 'spax'}
|
153
|
+
@klass.headers init_headers
|
154
|
+
@klass.headers.should == init_headers
|
155
|
+
end
|
156
|
+
|
157
|
+
it "uses the class headers when sending a request" do
|
158
|
+
expect_headers(foo: 'bar')
|
159
|
+
@klass.headers(foo: 'bar')
|
160
|
+
@klass.get('')
|
161
|
+
end
|
162
|
+
|
163
|
+
it "merges class headers with request headers" do
|
164
|
+
expect_headers(baz: 'spax', foo: 'bar')
|
165
|
+
@klass.headers(foo: 'bar')
|
166
|
+
@klass.get('', headers: {baz: 'spax'})
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'overrides class headers with request headers' do
|
170
|
+
expect_headers(baz: 'spax', foo: 'baz')
|
171
|
+
@klass.headers(foo: 'bar')
|
172
|
+
@klass.get('', headers: {baz: 'spax', foo: 'baz'})
|
173
|
+
end
|
174
|
+
|
175
|
+
context "with cookies" do
|
176
|
+
it 'utilizes the class-level cookies' do
|
177
|
+
expect_headers(foo: 'bar', 'cookie' => 'type=snickerdoodle')
|
178
|
+
@klass.headers(foo: 'bar')
|
179
|
+
@klass.cookies(type: 'snickerdoodle')
|
180
|
+
@klass.get('')
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'adds cookies to the headers' do
|
184
|
+
expect_headers(foo: 'bar', 'cookie' => 'type=snickerdoodle')
|
185
|
+
@klass.headers(foo: 'bar')
|
186
|
+
@klass.get('', cookies: {type: 'snickerdoodle'})
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'doesnt modify default_options' do
|
190
|
+
@klass.headers.should == {}
|
191
|
+
expect_headers('cookie' => 'type=snickerdoodle')
|
192
|
+
@klass.get('', cookies: {type: 'snickerdoodle'})
|
193
|
+
@klass.default_options[:headers].should == {}
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'adds optional cookies to the optional headers' do
|
197
|
+
expect_headers(baz: 'spax', 'cookie' => 'type=snickerdoodle')
|
198
|
+
@klass.get('', cookies: {type: 'snickerdoodle'}, headers: {baz: 'spax'})
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "cookies" do
|
204
|
+
def expect_cookie_header(s)
|
205
|
+
HTTParty::Request.should_receive(:new) \
|
206
|
+
.with(anything, anything, hash_including({ headers: { "cookie" => s } })) \
|
207
|
+
.and_return(mock("mock response", perform: nil))
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should not be in the headers by default" do
|
211
|
+
HTTParty::Request.stub!(:new).and_return(stub(nil, perform: nil))
|
212
|
+
@klass.get("")
|
213
|
+
@klass.headers.keys.should_not include("cookie")
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should raise an ArgumentError if passed a non-Hash" do
|
217
|
+
lambda do
|
218
|
+
@klass.cookies("nonsense")
|
219
|
+
end.should raise_error(ArgumentError)
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should allow a cookie to be specified with a one-off request" do
|
223
|
+
expect_cookie_header "type=snickerdoodle"
|
224
|
+
@klass.get("", cookies: { type: "snickerdoodle" })
|
225
|
+
end
|
226
|
+
|
227
|
+
describe "when a cookie is set at the class level" do
|
228
|
+
before(:each) do
|
229
|
+
@klass.cookies({ type: "snickerdoodle" })
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should include that cookie in the request" do
|
233
|
+
expect_cookie_header "type=snickerdoodle"
|
234
|
+
@klass.get("")
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should pass the proper cookies when requested multiple times" do
|
238
|
+
2.times do
|
239
|
+
expect_cookie_header "type=snickerdoodle"
|
240
|
+
@klass.get("")
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should allow the class defaults to be overridden" do
|
245
|
+
expect_cookie_header "type=chocolate_chip"
|
246
|
+
|
247
|
+
@klass.get("", cookies: { type: "chocolate_chip" })
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "in a class with multiple methods that use different cookies" do
|
252
|
+
before(:each) do
|
253
|
+
@klass.instance_eval do
|
254
|
+
def first_method
|
255
|
+
get("first_method", cookies: { first_method_cookie: "foo" })
|
256
|
+
end
|
257
|
+
|
258
|
+
def second_method
|
259
|
+
get("second_method", cookies: { second_method_cookie: "foo" })
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should not allow cookies used in one method to carry over into other methods" do
|
265
|
+
expect_cookie_header "first_method_cookie=foo"
|
266
|
+
@klass.first_method
|
267
|
+
|
268
|
+
expect_cookie_header "second_method_cookie=foo"
|
269
|
+
@klass.second_method
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe "default params" do
|
275
|
+
it "should default to empty hash" do
|
276
|
+
@klass.default_params.should == {}
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should be able to be updated" do
|
280
|
+
new_defaults = {foo: 'bar', baz: 'spax'}
|
281
|
+
@klass.default_params new_defaults
|
282
|
+
@klass.default_params.should == new_defaults
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "default timeout" do
|
287
|
+
it "should default to nil" do
|
288
|
+
@klass.default_options[:timeout].should == nil
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should support updating" do
|
292
|
+
@klass.default_timeout 10
|
293
|
+
@klass.default_options[:timeout].should == 10
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should support floats" do
|
297
|
+
@klass.default_timeout 0.5
|
298
|
+
@klass.default_options[:timeout].should == 0.5
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe "debug_output" do
|
303
|
+
it "stores the given stream as a default_option" do
|
304
|
+
@klass.debug_output $stdout
|
305
|
+
@klass.default_options[:debug_output].should == $stdout
|
306
|
+
end
|
307
|
+
|
308
|
+
it "stores the $stderr stream by default" do
|
309
|
+
@klass.debug_output
|
310
|
+
@klass.default_options[:debug_output].should == $stderr
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
describe "basic http authentication" do
|
315
|
+
it "should work" do
|
316
|
+
@klass.basic_auth 'foobar', 'secret'
|
317
|
+
@klass.default_options[:basic_auth].should == {username: 'foobar', password: 'secret'}
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "digest http authentication" do
|
322
|
+
it "should work" do
|
323
|
+
@klass.digest_auth 'foobar', 'secret'
|
324
|
+
@klass.default_options[:digest_auth].should == {username: 'foobar', password: 'secret'}
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe "parser" do
|
329
|
+
class CustomParser
|
330
|
+
def self.parse(body)
|
331
|
+
return {sexy: true}
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
let(:parser) do
|
336
|
+
Proc.new{ |data, format| CustomParser.parse(data) }
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should set parser options" do
|
340
|
+
@klass.parser parser
|
341
|
+
@klass.default_options[:parser].should == parser
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should be able parse response with custom parser" do
|
345
|
+
@klass.parser parser
|
346
|
+
FakeWeb.register_uri(:get, 'http://twitter.com/statuses/public_timeline.xml', body: 'tweets')
|
347
|
+
custom_parsed_response = @klass.get('http://twitter.com/statuses/public_timeline.xml')
|
348
|
+
custom_parsed_response[:sexy].should == true
|
349
|
+
end
|
350
|
+
|
351
|
+
it "raises UnsupportedFormat when the parser cannot handle the format" do
|
352
|
+
@klass.format :json
|
353
|
+
class MyParser < HTTParty::Parser
|
354
|
+
SupportedFormats = {}
|
355
|
+
end unless defined?(MyParser)
|
356
|
+
expect do
|
357
|
+
@klass.parser MyParser
|
358
|
+
end.to raise_error(HTTParty::UnsupportedFormat)
|
359
|
+
end
|
360
|
+
|
361
|
+
it 'does not validate format whe custom parser is a proc' do
|
362
|
+
expect do
|
363
|
+
@klass.format :json
|
364
|
+
@klass.parser lambda {|body, format|}
|
365
|
+
end.to_not raise_error(HTTParty::UnsupportedFormat)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
describe "connection_adapter" do
|
370
|
+
let(:uri) { 'http://google.com/api.json' }
|
371
|
+
let(:connection_adapter) { mock('CustomConnectionAdapter') }
|
372
|
+
|
373
|
+
it "should set the connection_adapter" do
|
374
|
+
@klass.connection_adapter connection_adapter
|
375
|
+
@klass.default_options[:connection_adapter].should be connection_adapter
|
376
|
+
end
|
377
|
+
|
378
|
+
it "should set the connection_adapter_options when provided" do
|
379
|
+
options = {foo: :bar}
|
380
|
+
@klass.connection_adapter connection_adapter, options
|
381
|
+
@klass.default_options[:connection_adapter_options].should be options
|
382
|
+
end
|
383
|
+
|
384
|
+
it "should not set the connection_adapter_options when not provided" do
|
385
|
+
@klass.connection_adapter connection_adapter
|
386
|
+
@klass.default_options[:connection_adapter_options].should be_nil
|
387
|
+
end
|
388
|
+
|
389
|
+
it "should process a request with a connection from the adapter" do
|
390
|
+
connection_adapter_options = {foo: :bar}
|
391
|
+
connection_adapter.should_receive(:call) do |u,o|
|
392
|
+
o[:connection_adapter_options].should == connection_adapter_options
|
393
|
+
HTTParty::ConnectionAdapter.call(u,o)
|
394
|
+
end.with(URI.parse(uri), kind_of(Hash))
|
395
|
+
FakeWeb.register_uri(:get, uri, body: 'stuff')
|
396
|
+
@klass.connection_adapter connection_adapter, connection_adapter_options
|
397
|
+
@klass.get(uri).should == 'stuff'
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
describe "format" do
|
402
|
+
it "should allow xml" do
|
403
|
+
@klass.format :xml
|
404
|
+
@klass.default_options[:format].should == :xml
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should allow csv" do
|
408
|
+
@klass.format :csv
|
409
|
+
@klass.default_options[:format].should == :csv
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should allow json" do
|
413
|
+
@klass.format :json
|
414
|
+
@klass.default_options[:format].should == :json
|
415
|
+
end
|
416
|
+
|
417
|
+
it "should allow plain" do
|
418
|
+
@klass.format :plain
|
419
|
+
@klass.default_options[:format].should == :plain
|
420
|
+
end
|
421
|
+
|
422
|
+
it 'should not allow funky format' do
|
423
|
+
lambda do
|
424
|
+
@klass.format :foobar
|
425
|
+
end.should raise_error(HTTParty::UnsupportedFormat)
|
426
|
+
end
|
427
|
+
|
428
|
+
it 'should only print each format once with an exception' do
|
429
|
+
lambda do
|
430
|
+
@klass.format :foobar
|
431
|
+
end.should raise_error(HTTParty::UnsupportedFormat, "':foobar' Must be one of: csv, html, json, plain, xml")
|
432
|
+
end
|
433
|
+
|
434
|
+
it 'sets the default parser' do
|
435
|
+
@klass.default_options[:parser].should be_nil
|
436
|
+
@klass.format :json
|
437
|
+
@klass.default_options[:parser].should == HTTParty::Parser
|
438
|
+
end
|
439
|
+
|
440
|
+
it 'does not reset parser to the default parser' do
|
441
|
+
my_parser = lambda {}
|
442
|
+
@klass.parser my_parser
|
443
|
+
@klass.format :json
|
444
|
+
@klass.parser.should == my_parser
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
describe "#no_follow" do
|
449
|
+
it "sets no_follow to false by default" do
|
450
|
+
@klass.no_follow
|
451
|
+
@klass.default_options[:no_follow].should be_false
|
452
|
+
end
|
453
|
+
|
454
|
+
it "sets the no_follow option to true" do
|
455
|
+
@klass.no_follow true
|
456
|
+
@klass.default_options[:no_follow].should be_true
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
describe "#maintain_method_across_redirects" do
|
461
|
+
it "sets maintain_method_across_redirects to true by default" do
|
462
|
+
@klass.maintain_method_across_redirects
|
463
|
+
@klass.default_options[:maintain_method_across_redirects].should be_true
|
464
|
+
end
|
465
|
+
|
466
|
+
it "sets the maintain_method_across_redirects option to false" do
|
467
|
+
@klass.maintain_method_across_redirects false
|
468
|
+
@klass.default_options[:maintain_method_across_redirects].should be_false
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
describe "#resend_on_redirect" do
|
473
|
+
it "sets resend_on_redirect to true by default" do
|
474
|
+
@klass.resend_on_redirect
|
475
|
+
@klass.default_options[:resend_on_redirect].should be_true
|
476
|
+
end
|
477
|
+
|
478
|
+
it "sets resend_on_redirect option to false" do
|
479
|
+
@klass.resend_on_redirect false
|
480
|
+
@klass.default_options[:resend_on_redirect].should be_false
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
describe ".follow_redirects" do
|
485
|
+
it "sets follow redirects to true by default" do
|
486
|
+
@klass.follow_redirects
|
487
|
+
@klass.default_options[:follow_redirects].should be_true
|
488
|
+
end
|
489
|
+
|
490
|
+
it "sets the follow_redirects option to false" do
|
491
|
+
@klass.follow_redirects false
|
492
|
+
@klass.default_options[:follow_redirects].should be_false
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
describe ".query_string_normalizer" do
|
497
|
+
it "sets the query_string_normalizer option" do
|
498
|
+
normalizer = proc {}
|
499
|
+
@klass.query_string_normalizer normalizer
|
500
|
+
@klass.default_options[:query_string_normalizer].should == normalizer
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
describe "with explicit override of automatic redirect handling" do
|
505
|
+
before do
|
506
|
+
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', format: :xml, no_follow: true)
|
507
|
+
@redirect = stub_response 'first redirect', 302
|
508
|
+
@redirect['location'] = 'http://foo.com/bar'
|
509
|
+
HTTParty::Request.stub(new: @request)
|
510
|
+
end
|
511
|
+
|
512
|
+
it "should fail with redirected GET" do
|
513
|
+
lambda do
|
514
|
+
@error = @klass.get('/foo', no_follow: true)
|
515
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
516
|
+
end
|
517
|
+
|
518
|
+
it "should fail with redirected POST" do
|
519
|
+
lambda do
|
520
|
+
@klass.post('/foo', no_follow: true)
|
521
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
522
|
+
end
|
523
|
+
|
524
|
+
it "should fail with redirected PATCH" do
|
525
|
+
lambda do
|
526
|
+
@klass.patch('/foo', no_follow: true)
|
527
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
528
|
+
end
|
529
|
+
|
530
|
+
it "should fail with redirected DELETE" do
|
531
|
+
lambda do
|
532
|
+
@klass.delete('/foo', no_follow: true)
|
533
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
534
|
+
end
|
535
|
+
|
536
|
+
it "should fail with redirected MOVE" do
|
537
|
+
lambda do
|
538
|
+
@klass.move('/foo', no_follow: true)
|
539
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
540
|
+
end
|
541
|
+
|
542
|
+
it "should fail with redirected COPY" do
|
543
|
+
lambda do
|
544
|
+
@klass.copy('/foo', no_follow: true)
|
545
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
546
|
+
end
|
547
|
+
|
548
|
+
it "should fail with redirected PUT" do
|
549
|
+
lambda do
|
550
|
+
@klass.put('/foo', no_follow: true)
|
551
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
552
|
+
end
|
553
|
+
|
554
|
+
it "should fail with redirected HEAD" do
|
555
|
+
lambda do
|
556
|
+
@klass.head('/foo', no_follow: true)
|
557
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
558
|
+
end
|
559
|
+
|
560
|
+
it "should fail with redirected OPTIONS" do
|
561
|
+
lambda do
|
562
|
+
@klass.options('/foo', no_follow: true)
|
563
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
describe "with multiple class definitions" do
|
568
|
+
before(:each) do
|
569
|
+
@klass.instance_eval do
|
570
|
+
base_uri "http://first.com"
|
571
|
+
default_params one: 1
|
572
|
+
end
|
573
|
+
|
574
|
+
@additional_klass = Class.new
|
575
|
+
@additional_klass.instance_eval do
|
576
|
+
include HTTParty
|
577
|
+
base_uri "http://second.com"
|
578
|
+
default_params two: 2
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
it "should not run over each others options" do
|
583
|
+
@klass.default_options.should == { base_uri: 'http://first.com', default_params: { one: 1 } }
|
584
|
+
@additional_klass.default_options.should == { base_uri: 'http://second.com', default_params: { two: 2 } }
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
describe "two child classes inheriting from one parent" do
|
589
|
+
before(:each) do
|
590
|
+
@parent = Class.new do
|
591
|
+
include HTTParty
|
592
|
+
def self.name
|
593
|
+
"Parent"
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
@child1 = Class.new(@parent)
|
598
|
+
@child2 = Class.new(@parent)
|
599
|
+
end
|
600
|
+
|
601
|
+
it "does not modify each others inherited attributes" do
|
602
|
+
@child1.default_params joe: "alive"
|
603
|
+
@child2.default_params joe: "dead"
|
604
|
+
|
605
|
+
@child1.default_options.should == { default_params: {joe: "alive"} }
|
606
|
+
@child2.default_options.should == { default_params: {joe: "dead"} }
|
607
|
+
|
608
|
+
@parent.default_options.should == { }
|
609
|
+
end
|
610
|
+
|
611
|
+
it "inherits default_options from the superclass" do
|
612
|
+
@parent.basic_auth 'user', 'password'
|
613
|
+
@child1.default_options.should == {basic_auth: {username: 'user', password: 'password'}}
|
614
|
+
@child1.basic_auth 'u', 'p' # modifying child1 has no effect on child2
|
615
|
+
@child2.default_options.should == {basic_auth: {username: 'user', password: 'password'}}
|
616
|
+
end
|
617
|
+
|
618
|
+
it "doesn't modify the parent's default options" do
|
619
|
+
@parent.basic_auth 'user', 'password'
|
620
|
+
|
621
|
+
@child1.basic_auth 'u', 'p'
|
622
|
+
@child1.default_options.should == {basic_auth: {username: 'u', password: 'p'}}
|
623
|
+
|
624
|
+
@child1.basic_auth 'email', 'token'
|
625
|
+
@child1.default_options.should == {basic_auth: {username: 'email', password: 'token'}}
|
626
|
+
|
627
|
+
@parent.default_options.should == {basic_auth: {username: 'user', password: 'password'}}
|
628
|
+
end
|
629
|
+
|
630
|
+
it "doesn't modify hashes in the parent's default options" do
|
631
|
+
@parent.headers 'Accept' => 'application/json'
|
632
|
+
@child1.headers 'Accept' => 'application/xml'
|
633
|
+
|
634
|
+
@parent.default_options[:headers].should == {'Accept' => 'application/json'}
|
635
|
+
@child1.default_options[:headers].should == {'Accept' => 'application/xml'}
|
636
|
+
end
|
637
|
+
|
638
|
+
it "works with lambda values" do
|
639
|
+
@child1.default_options[:imaginary_option] = lambda { "This is a new lambda "}
|
640
|
+
@child1.default_options[:imaginary_option].should be_a Proc
|
641
|
+
end
|
642
|
+
|
643
|
+
it 'should dup the proc on the child class' do
|
644
|
+
imaginary_option = lambda { 2 * 3.14 }
|
645
|
+
@parent.default_options[:imaginary_option] = imaginary_option
|
646
|
+
@parent.default_options[:imaginary_option].call.should == imaginary_option.call
|
647
|
+
@child1.default_options[:imaginary_option]
|
648
|
+
@child1.default_options[:imaginary_option].call.should == imaginary_option.call
|
649
|
+
@child1.default_options[:imaginary_option].should_not be_equal imaginary_option
|
650
|
+
end
|
651
|
+
|
652
|
+
it "inherits default_cookies from the parent class" do
|
653
|
+
@parent.cookies 'type' => 'chocolate_chip'
|
654
|
+
@child1.default_cookies.should == {"type" => "chocolate_chip"}
|
655
|
+
@child1.cookies 'type' => 'snickerdoodle'
|
656
|
+
@child1.default_cookies.should == {"type" => "snickerdoodle"}
|
657
|
+
@child2.default_cookies.should == {"type" => "chocolate_chip"}
|
658
|
+
end
|
659
|
+
|
660
|
+
it "doesn't modify the parent's default cookies" do
|
661
|
+
@parent.cookies 'type' => 'chocolate_chip'
|
662
|
+
|
663
|
+
@child1.cookies 'type' => 'snickerdoodle'
|
664
|
+
@child1.default_cookies.should == {"type" => "snickerdoodle"}
|
665
|
+
|
666
|
+
@parent.default_cookies.should == {"type" => "chocolate_chip"}
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
describe "grand parent with inherited callback" do
|
671
|
+
before do
|
672
|
+
@grand_parent = Class.new do
|
673
|
+
def self.inherited(subclass)
|
674
|
+
subclass.instance_variable_set(:@grand_parent, true)
|
675
|
+
end
|
676
|
+
end
|
677
|
+
@parent = Class.new(@grand_parent) do
|
678
|
+
include HTTParty
|
679
|
+
end
|
680
|
+
end
|
681
|
+
it "continues running the #inherited on the parent" do
|
682
|
+
child = Class.new(@parent)
|
683
|
+
child.instance_variable_get(:@grand_parent).should be_true
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
describe "#get" do
|
688
|
+
it "should be able to get html" do
|
689
|
+
stub_http_response_with('google.html')
|
690
|
+
HTTParty.get('http://www.google.com').should == file_fixture('google.html')
|
691
|
+
end
|
692
|
+
|
693
|
+
it "should be able to get chunked html" do
|
694
|
+
chunks = ["Chunk1", "Chunk2", "Chunk3", "Chunk4"]
|
695
|
+
stub_chunked_http_response_with(chunks)
|
696
|
+
|
697
|
+
HTTParty.get('http://www.google.com') do |fragment|
|
698
|
+
chunks.should include(fragment)
|
699
|
+
end.should == chunks.join
|
700
|
+
end
|
701
|
+
|
702
|
+
it "should be able parse response type json automatically" do
|
703
|
+
stub_http_response_with('twitter.json')
|
704
|
+
tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
|
705
|
+
tweets.size.should == 20
|
706
|
+
tweets.first['user'].should == {
|
707
|
+
"name" => "Pyk",
|
708
|
+
"url" => nil,
|
709
|
+
"id" => "7694602",
|
710
|
+
"description" => nil,
|
711
|
+
"protected" => false,
|
712
|
+
"screen_name" => "Pyk",
|
713
|
+
"followers_count" => 1,
|
714
|
+
"location" => "Opera Plaza, California",
|
715
|
+
"profile_image_url" => "http://static.twitter.com/images/default_profile_normal.png"
|
716
|
+
}
|
717
|
+
end
|
718
|
+
|
719
|
+
it "should be able parse response type xml automatically" do
|
720
|
+
stub_http_response_with('twitter.xml')
|
721
|
+
tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.xml')
|
722
|
+
tweets['statuses'].size.should == 20
|
723
|
+
tweets['statuses'].first['user'].should == {
|
724
|
+
"name" => "Magic 8 Bot",
|
725
|
+
"url" => nil,
|
726
|
+
"id" => "17656026",
|
727
|
+
"description" => "ask me a question",
|
728
|
+
"protected" => "false",
|
729
|
+
"screen_name" => "magic8bot",
|
730
|
+
"followers_count" => "90",
|
731
|
+
"profile_image_url" => "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg",
|
732
|
+
"location" => nil
|
733
|
+
}
|
734
|
+
end
|
735
|
+
|
736
|
+
it "should be able parse response type csv automatically" do
|
737
|
+
stub_http_response_with('twitter.csv')
|
738
|
+
profile = HTTParty.get('http://twitter.com/statuses/profile.csv')
|
739
|
+
profile.size.should == 2
|
740
|
+
profile[0].should == ["name","url","id","description","protected","screen_name","followers_count","profile_image_url","location"]
|
741
|
+
profile[1].should == ["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]
|
742
|
+
end
|
743
|
+
|
744
|
+
it "should not get undefined method add_node for nil class for the following xml" do
|
745
|
+
stub_http_response_with('undefined_method_add_node_for_nil.xml')
|
746
|
+
result = HTTParty.get('http://foobar.com')
|
747
|
+
result.should == {"Entities"=>{"href"=>"https://s3-sandbox.parature.com/api/v1/5578/5633/Account", "results"=>"0", "total"=>"0", "page_size"=>"25", "page"=>"1"}}
|
748
|
+
end
|
749
|
+
|
750
|
+
it "should parse empty response fine" do
|
751
|
+
stub_http_response_with('empty.xml')
|
752
|
+
result = HTTParty.get('http://foobar.com')
|
753
|
+
result.should be_nil
|
754
|
+
end
|
755
|
+
|
756
|
+
it "should accept http URIs" do
|
757
|
+
stub_http_response_with('google.html')
|
758
|
+
lambda do
|
759
|
+
HTTParty.get('http://google.com')
|
760
|
+
end.should_not raise_error(HTTParty::UnsupportedURIScheme)
|
761
|
+
end
|
762
|
+
|
763
|
+
it "should accept https URIs" do
|
764
|
+
stub_http_response_with('google.html')
|
765
|
+
lambda do
|
766
|
+
HTTParty.get('https://google.com')
|
767
|
+
end.should_not raise_error(HTTParty::UnsupportedURIScheme)
|
768
|
+
end
|
769
|
+
|
770
|
+
it "should accept webcal URIs" do
|
771
|
+
stub_http_response_with('google.html')
|
772
|
+
lambda do
|
773
|
+
HTTParty.get('webcal://google.com')
|
774
|
+
end.should_not raise_error(HTTParty::UnsupportedURIScheme)
|
775
|
+
end
|
776
|
+
|
777
|
+
it "should raise an InvalidURIError on URIs that can't be parsed at all" do
|
778
|
+
lambda do
|
779
|
+
HTTParty.get("It's the one that says 'Bad URI'")
|
780
|
+
end.should raise_error(URI::InvalidURIError)
|
781
|
+
end
|
782
|
+
end
|
783
|
+
end
|