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.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rubocop.yml +92 -0
  4. data/.rubocop_todo.yml +124 -0
  5. data/.simplecov +1 -0
  6. data/.travis.yml +7 -0
  7. data/CONTRIBUTING.md +23 -0
  8. data/Gemfile +19 -0
  9. data/Guardfile +16 -0
  10. data/History +370 -0
  11. data/MIT-LICENSE +20 -0
  12. data/README.md +78 -0
  13. data/Rakefile +10 -0
  14. data/bin/httparty +116 -0
  15. data/cucumber.yml +1 -0
  16. data/examples/README.md +67 -0
  17. data/examples/aaws.rb +32 -0
  18. data/examples/basic.rb +28 -0
  19. data/examples/crack.rb +19 -0
  20. data/examples/custom_parsers.rb +64 -0
  21. data/examples/delicious.rb +37 -0
  22. data/examples/google.rb +16 -0
  23. data/examples/headers_and_user_agents.rb +6 -0
  24. data/examples/logging.rb +36 -0
  25. data/examples/nokogiri_html_parser.rb +19 -0
  26. data/examples/rescue_json.rb +17 -0
  27. data/examples/rubyurl.rb +14 -0
  28. data/examples/stackexchange.rb +24 -0
  29. data/examples/tripit_sign_in.rb +33 -0
  30. data/examples/twitter.rb +31 -0
  31. data/examples/whoismyrep.rb +10 -0
  32. data/features/basic_authentication.feature +20 -0
  33. data/features/command_line.feature +90 -0
  34. data/features/deals_with_http_error_codes.feature +26 -0
  35. data/features/digest_authentication.feature +20 -0
  36. data/features/handles_compressed_responses.feature +27 -0
  37. data/features/handles_multiple_formats.feature +57 -0
  38. data/features/steps/env.rb +27 -0
  39. data/features/steps/httparty_response_steps.rb +52 -0
  40. data/features/steps/httparty_steps.rb +43 -0
  41. data/features/steps/mongrel_helper.rb +94 -0
  42. data/features/steps/remote_service_steps.rb +86 -0
  43. data/features/supports_read_timeout_option.feature +13 -0
  44. data/features/supports_redirection.feature +22 -0
  45. data/features/supports_timeout_option.feature +13 -0
  46. data/httparty.gemspec +28 -0
  47. data/httpserious.gemspec +25 -0
  48. data/lib/httparty.rb +612 -0
  49. data/lib/httparty/connection_adapter.rb +190 -0
  50. data/lib/httparty/cookie_hash.rb +21 -0
  51. data/lib/httparty/exceptions.rb +29 -0
  52. data/lib/httparty/hash_conversions.rb +49 -0
  53. data/lib/httparty/logger/apache_formatter.rb +22 -0
  54. data/lib/httparty/logger/curl_formatter.rb +48 -0
  55. data/lib/httparty/logger/logger.rb +26 -0
  56. data/lib/httparty/module_inheritable_attributes.rb +56 -0
  57. data/lib/httparty/net_digest_auth.rb +117 -0
  58. data/lib/httparty/parser.rb +141 -0
  59. data/lib/httparty/request.rb +361 -0
  60. data/lib/httparty/response.rb +77 -0
  61. data/lib/httparty/response/headers.rb +31 -0
  62. data/lib/httparty/version.rb +3 -0
  63. data/lib/httpserious.rb +1 -0
  64. data/script/release +42 -0
  65. data/spec/fixtures/delicious.xml +23 -0
  66. data/spec/fixtures/empty.xml +0 -0
  67. data/spec/fixtures/google.html +3 -0
  68. data/spec/fixtures/ssl/generate.sh +29 -0
  69. data/spec/fixtures/ssl/generated/1fe462c2.0 +16 -0
  70. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  71. data/spec/fixtures/ssl/generated/ca.crt +16 -0
  72. data/spec/fixtures/ssl/generated/ca.key +15 -0
  73. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  74. data/spec/fixtures/ssl/generated/server.crt +13 -0
  75. data/spec/fixtures/ssl/generated/server.key +15 -0
  76. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  77. data/spec/fixtures/twitter.csv +2 -0
  78. data/spec/fixtures/twitter.json +1 -0
  79. data/spec/fixtures/twitter.xml +403 -0
  80. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  81. data/spec/httparty/connection_adapter_spec.rb +468 -0
  82. data/spec/httparty/cookie_hash_spec.rb +83 -0
  83. data/spec/httparty/exception_spec.rb +38 -0
  84. data/spec/httparty/hash_conversions_spec.rb +41 -0
  85. data/spec/httparty/logger/apache_formatter_spec.rb +41 -0
  86. data/spec/httparty/logger/curl_formatter_spec.rb +18 -0
  87. data/spec/httparty/logger/logger_spec.rb +38 -0
  88. data/spec/httparty/net_digest_auth_spec.rb +191 -0
  89. data/spec/httparty/parser_spec.rb +167 -0
  90. data/spec/httparty/request_spec.rb +872 -0
  91. data/spec/httparty/response_spec.rb +241 -0
  92. data/spec/httparty/ssl_spec.rb +74 -0
  93. data/spec/httparty_spec.rb +823 -0
  94. data/spec/spec_helper.rb +59 -0
  95. data/spec/support/ssl_test_helper.rb +47 -0
  96. data/spec/support/ssl_test_server.rb +80 -0
  97. data/spec/support/stub_response.rb +43 -0
  98. data/website/css/common.css +47 -0
  99. data/website/index.html +73 -0
  100. 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