koala 1.1.0 → 1.2.0beta1

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 (50) hide show
  1. data/.travis.yml +2 -1
  2. data/CHANGELOG +26 -0
  3. data/Gemfile +6 -2
  4. data/Rakefile +0 -1
  5. data/koala.gemspec +8 -8
  6. data/lib/koala.rb +42 -45
  7. data/lib/koala/batch_operation.rb +15 -15
  8. data/lib/koala/graph_api.rb +81 -58
  9. data/lib/koala/graph_batch_api.rb +10 -10
  10. data/lib/koala/graph_collection.rb +6 -6
  11. data/lib/koala/http_service.rb +177 -0
  12. data/lib/koala/oauth.rb +2 -2
  13. data/lib/koala/realtime_updates.rb +20 -17
  14. data/lib/koala/rest_api.rb +1 -1
  15. data/lib/koala/test_users.rb +33 -16
  16. data/lib/koala/uploadable_io.rb +47 -42
  17. data/lib/koala/utils.rb +11 -0
  18. data/readme.md +38 -38
  19. data/spec/cases/api_base_spec.rb +2 -2
  20. data/spec/cases/error_spec.rb +32 -0
  21. data/spec/cases/graph_and_rest_api_spec.rb +20 -3
  22. data/spec/cases/graph_api_batch_spec.rb +88 -97
  23. data/spec/cases/graph_api_spec.rb +21 -4
  24. data/spec/cases/http_service_spec.rb +446 -0
  25. data/spec/cases/koala_spec.rb +33 -38
  26. data/spec/cases/oauth_spec.rb +219 -200
  27. data/spec/cases/realtime_updates_spec.rb +45 -31
  28. data/spec/cases/rest_api_spec.rb +23 -7
  29. data/spec/cases/test_users_spec.rb +112 -52
  30. data/spec/cases/uploadable_io_spec.rb +49 -36
  31. data/spec/cases/utils_spec.rb +10 -0
  32. data/spec/fixtures/facebook_data.yml +23 -22
  33. data/spec/fixtures/mock_facebook_responses.yml +126 -96
  34. data/spec/spec_helper.rb +29 -5
  35. data/spec/support/graph_api_shared_examples.rb +59 -52
  36. data/spec/support/json_testing_fix.rb +35 -11
  37. data/spec/support/koala_test.rb +163 -0
  38. data/spec/support/mock_http_service.rb +6 -4
  39. data/spec/support/ordered_hash.rb +205 -0
  40. data/spec/support/rest_api_shared_examples.rb +37 -37
  41. data/spec/support/uploadable_io_shared_examples.rb +2 -8
  42. metadata +78 -79
  43. data/lib/koala/http_services.rb +0 -46
  44. data/lib/koala/http_services/net_http_service.rb +0 -92
  45. data/lib/koala/http_services/typhoeus_service.rb +0 -37
  46. data/spec/cases/http_services/http_service_spec.rb +0 -129
  47. data/spec/cases/http_services/net_http_service_spec.rb +0 -532
  48. data/spec/cases/http_services/typhoeus_service_spec.rb +0 -152
  49. data/spec/support/live_testing_data_helper.rb +0 -40
  50. data/spec/support/setup_mocks_or_live.rb +0 -51
@@ -1,46 +0,0 @@
1
- module Koala
2
- class Response
3
- attr_reader :status, :body, :headers
4
- def initialize(status, body, headers)
5
- @status = status
6
- @body = body
7
- @headers = headers
8
- end
9
- end
10
-
11
- module HTTPService
12
- # common functionality for all HTTP services
13
- def self.included(base)
14
- base.class_eval do
15
- class << self
16
- attr_accessor :always_use_ssl, :proxy, :timeout
17
- end
18
-
19
- def self.server(options = {})
20
- server = "#{options[:rest_api] ? Facebook::REST_SERVER : Facebook::GRAPH_SERVER}"
21
- server.gsub!(/\.facebook/, "-video.facebook") if options[:video]
22
- "#{options[:beta] ? "beta." : ""}#{server}"
23
- end
24
-
25
- def self.encode_params(param_hash)
26
- # unfortunately, we can't use to_query because that's Rails, not Ruby
27
- # if no hash (e.g. no auth token) return empty string
28
- ((param_hash || {}).collect do |key_and_value|
29
- key_and_value[1] = MultiJson.encode(key_and_value[1]) unless key_and_value[1].is_a? String
30
- "#{key_and_value[0].to_s}=#{CGI.escape key_and_value[1]}"
31
- end).join("&")
32
- end
33
-
34
- protected
35
-
36
- def self.params_require_multipart?(param_hash)
37
- param_hash.any? { |key, value| value.kind_of?(Koala::UploadableIO) }
38
- end
39
-
40
- def self.multipart_requires_content_type?
41
- true
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,92 +0,0 @@
1
- require "net/http" unless defined?(Net::HTTP)
2
- require "net/https"
3
- require "net/http/post/multipart"
4
-
5
- module Koala
6
- module NetHTTPService
7
- # this service uses Net::HTTP to send requests to the graph
8
- include Koala::HTTPService
9
-
10
- # Net::HTTP-specific values
11
- class << self
12
- attr_accessor :ca_file, :ca_path, :verify_mode
13
- end
14
-
15
- def self.make_request(path, args, verb, options = {})
16
- # We translate args to a valid query string. If post is specified,
17
- # we send a POST request to the given path with the given arguments.
18
-
19
- # by default, we use SSL only for private requests
20
- # this makes public requests faster
21
- private_request = args["access_token"] || @always_use_ssl || options[:use_ssl]
22
-
23
- # if the verb isn't get or post, send it as a post argument
24
- args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
25
-
26
- http = create_http(server(options), private_request, options)
27
-
28
- response = http.start do |http|
29
- if verb == "post"
30
- if params_require_multipart? args
31
- http.request Net::HTTP::Post::Multipart.new path, encode_multipart_params(args)
32
- else
33
- http.post(path, encode_params(args))
34
- end
35
- else
36
- http.get("#{path}?#{encode_params(args)}")
37
- end
38
- end
39
-
40
- Koala::Response.new(response.code.to_i, response.body, response)
41
- end
42
-
43
- protected
44
- def self.encode_params(param_hash)
45
- # unfortunately, we can't use to_query because that's Rails, not Ruby
46
- # if no hash (e.g. no auth token) return empty string
47
- ((param_hash || {}).collect do |key_and_value|
48
- key_and_value[1] = MultiJson.encode(key_and_value[1]) if key_and_value[1].class != String
49
- "#{key_and_value[0].to_s}=#{CGI.escape key_and_value[1]}"
50
- end).join("&")
51
- end
52
-
53
- def self.encode_multipart_params(param_hash)
54
- Hash[*param_hash.collect do |key, value|
55
- [key, value.kind_of?(Koala::UploadableIO) ? value.to_upload_io : value]
56
- end.flatten]
57
- end
58
-
59
- def self.create_http(server, private_request, options)
60
- if proxy_server = options[:proxy] || proxy
61
- proxy = URI.parse(proxy_server)
62
- http = Net::HTTP.new(server, private_request ? 443 : nil,
63
- proxy.host, proxy.port, proxy.user, proxy.password)
64
- else
65
- http = Net::HTTP.new(server, private_request ? 443 : nil)
66
- end
67
-
68
- if timeout_value = options[:timeout] || timeout
69
- http.open_timeout = timeout_value
70
- http.read_timeout = timeout_value
71
- end
72
-
73
- # For HTTPS requests, set the proper CA certificates
74
- if private_request
75
- http.use_ssl = true
76
- http.verify_mode = options[:verify_mode] || verify_mode || OpenSSL::SSL::VERIFY_PEER
77
-
78
- if cert_file = options[:ca_file] || ca_file
79
- raise Errno::ENOENT, "Certificate file #{cert_file.inspect} does not exist!" unless File.exists?(cert_file)
80
- http.ca_file = cert_file
81
- end
82
-
83
- if cert_path = options[:ca_path] || ca_path
84
- raise Errno::ENOENT, "Certificate path #{cert_path.inspect} does not exist!" unless File.directory?(cert_path)
85
- http.ca_path = cert_path
86
- end
87
- end
88
-
89
- http
90
- end
91
- end
92
- end
@@ -1,37 +0,0 @@
1
- require "typhoeus" unless defined?(Typhoeus)
2
-
3
- module Koala
4
- module TyphoeusService
5
- # this service uses Typhoeus to send requests to the graph
6
- include Typhoeus
7
- include Koala::HTTPService
8
-
9
- def self.make_request(path, args, verb, options = {})
10
- # if the verb isn't get or post, send it as a post argument
11
- args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
12
-
13
- # switch any UploadableIOs to the files Typhoeus expects
14
- args.each_pair {|key, value| args[key] = value.to_file if value.is_a?(UploadableIO)}
15
-
16
- # you can pass arguments directly to Typhoeus using the :typhoeus_options key
17
- typhoeus_options = {:params => args}.merge(options[:typhoeus_options] || {})
18
-
19
- # if proxy/timeout options aren't passed, check if defaults are set
20
- typhoeus_options[:proxy] ||= proxy
21
- typhoeus_options[:timeout] ||= timeout
22
-
23
- # by default, we use SSL only for private requests (e.g. with access token)
24
- # this makes public requests faster
25
- prefix = (args["access_token"] || @always_use_ssl || options[:use_ssl]) ? "https" : "http"
26
-
27
- response = Typhoeus::Request.send(verb, "#{prefix}://#{server(options)}#{path}", typhoeus_options)
28
- Koala::Response.new(response.code, response.body, response.headers_hash)
29
- end
30
-
31
- protected
32
-
33
- def self.multipart_requires_content_type?
34
- false # Typhoeus handles multipart file types, we don't have to require it
35
- end
36
- end
37
- end
@@ -1,129 +0,0 @@
1
- require 'spec_helper'
2
-
3
-
4
- class Bear
5
- include Koala::HTTPService
6
- end
7
-
8
- describe "Koala::HTTPService" do
9
-
10
- describe "common methods" do
11
- describe "always_use_ssl accessor" do
12
- it "should be added" do
13
- # in Ruby 1.8, .methods returns strings
14
- # in Ruby 1.9, .method returns symbols
15
- Bear.methods.collect {|m| m.to_sym}.should include(:always_use_ssl)
16
- Bear.methods.collect {|m| m.to_sym}.should include(:always_use_ssl=)
17
- end
18
- end
19
-
20
- describe "proxy accessor" do
21
- it "should be added" do
22
- # in Ruby 1.8, .methods returns strings
23
- # in Ruby 1.9, .method returns symbols
24
- Bear.methods.collect {|m| m.to_sym}.should include(:proxy)
25
- Bear.methods.collect {|m| m.to_sym}.should include(:proxy=)
26
- end
27
- end
28
-
29
- describe "timeout accessor" do
30
- it "should be added" do
31
- # in Ruby 1.8, .methods returns strings
32
- # in Ruby 1.9, .method returns symbols
33
- Bear.methods.collect {|m| m.to_sym}.should include(:timeout)
34
- Bear.methods.collect {|m| m.to_sym}.should include(:timeout=)
35
- end
36
- end
37
-
38
- describe "server" do
39
- describe "with no options" do
40
- it "returns the REST server if options[:rest_api]" do
41
- Bear.server(:rest_api => true).should == Koala::Facebook::REST_SERVER
42
- end
43
-
44
- it "returns the graph server if !options[:rest_api]" do
45
- Bear.server(:rest_api => false).should == Koala::Facebook::GRAPH_SERVER
46
- Bear.server({}).should == Koala::Facebook::GRAPH_SERVER
47
- end
48
- end
49
-
50
- describe "with options[:beta]" do
51
- before :each do
52
- @options = {:beta => true}
53
- end
54
-
55
- it "returns the beta REST server if options[:rest_api]" do
56
- server = Bear.server(@options.merge(:rest_api => true))
57
- server.should =~ Regexp.new("beta.#{Koala::Facebook::REST_SERVER}")
58
- end
59
-
60
- it "returns the beta rest server if !options[:rest_api]" do
61
- server = Bear.server(@options)
62
- server.should =~ Regexp.new("beta.#{Koala::Facebook::GRAPH_SERVER}")
63
- end
64
- end
65
-
66
- describe "with options[:video]" do
67
- before :each do
68
- @options = {:video => true}
69
- end
70
-
71
- it "should return the REST video server if options[:rest_api]" do
72
- server = Bear.server(@options.merge(:rest_api => true))
73
- server.should =~ Regexp.new(Koala::Facebook::REST_SERVER.gsub(/\.facebook/, "-video.facebook"))
74
- end
75
-
76
- it "should return the graph video server if !options[:rest_api]" do
77
- server = Bear.server(@options)
78
- server.should =~ Regexp.new(Koala::Facebook::GRAPH_SERVER.gsub(/\.facebook/, "-video.facebook"))
79
- end
80
- end
81
- end
82
-
83
- describe "#encode_params" do
84
- it "should return an empty string if param_hash evaluates to false" do
85
- Bear.encode_params(nil).should == ''
86
- end
87
-
88
- it "should convert values to JSON if the value is not a String" do
89
- val = 'json_value'
90
- not_a_string = 'not_a_string'
91
- not_a_string.stub(:is_a?).and_return(false)
92
- MultiJson.should_receive(:encode).with(not_a_string).and_return(val)
93
-
94
- string = "hi"
95
-
96
- args = {
97
- not_a_string => not_a_string,
98
- string => string
99
- }
100
-
101
- result = Bear.encode_params(args)
102
- result.split('&').find do |key_and_val|
103
- key_and_val.match("#{not_a_string}=#{val}")
104
- end.should be_true
105
- end
106
-
107
- it "should escape all values" do
108
- args = Hash[*(1..4).map {|i| [i.to_s, "Value #{i}($"]}.flatten]
109
-
110
- result = Bear.encode_params(args)
111
- result.split('&').each do |key_val|
112
- key, val = key_val.split('=')
113
- val.should == CGI.escape(args[key])
114
- end
115
- end
116
-
117
- it "should convert all keys to Strings" do
118
- args = Hash[*(1..4).map {|i| [i, "val#{i}"]}.flatten]
119
-
120
- result = Bear.encode_params(args)
121
- result.split('&').each do |key_val|
122
- key, val = key_val.split('=')
123
- key.should == args.find{|key_val_arr| key_val_arr.last == val}.first.to_s
124
- end
125
- end
126
- end
127
- end
128
-
129
- end
@@ -1,532 +0,0 @@
1
- require 'spec_helper'
2
-
3
-
4
- Horse = Koala::NetHTTPService
5
-
6
- describe "NetHTTPService module holder class Horse" do
7
- before :each do
8
- # reset global settings
9
- Horse.always_use_ssl = Horse.proxy = Horse.timeout = nil
10
- end
11
-
12
- it "has a ca_file accessor" do
13
- Horse.methods.collect {|m| m.to_sym}.should include(:ca_file)
14
- Horse.methods.collect {|m| m.to_sym}.should include(:ca_file=)
15
- end
16
-
17
- it "has a ca_path accessor" do
18
- Horse.methods.collect {|m| m.to_sym}.should include(:ca_path)
19
- Horse.methods.collect {|m| m.to_sym}.should include(:ca_path=)
20
- end
21
-
22
- it "has a verify_mode accessor" do
23
- Horse.methods.collect {|m| m.to_sym}.should include(:verify_mode)
24
- Horse.methods.collect {|m| m.to_sym}.should include(:verify_mode=)
25
- end
26
-
27
- it "should define a make_request static module method" do
28
- Horse.respond_to?(:make_request).should be_true
29
- end
30
-
31
- it "should include the Koala::HTTPService module defining common features" do
32
- Horse.included_modules.include?(Koala::HTTPService).should be_true
33
- end
34
-
35
- describe "when making a request" do
36
- before(:each) do
37
- # Setup stubs for make_request to execute without exceptions
38
- @mock_body = stub('Net::HTTPResponse body')
39
- @mock_http_response = stub('Net::HTTPResponse', :code => 1, :body => @mock_body)
40
-
41
- @http_yield_mock = mock('Net::HTTP start yielded object')
42
-
43
- @http_yield_mock.stub(:post).and_return(@mock_http_response)
44
- @http_yield_mock.stub(:get).and_return(@mock_http_response)
45
-
46
- @http_mock = stub('Net::HTTP object', 'use_ssl=' => true, 'verify_mode=' => true)
47
- @http_mock.stub(:start).and_yield(@http_yield_mock)
48
- @http_mock.stub(:ca_path=)
49
- @http_mock.stub(:ca_file=)
50
- @http_mock.stub(:verify_mode=)
51
-
52
- Net::HTTP.stub(:new).and_return(@http_mock)
53
- end
54
-
55
- describe "the connection" do
56
- it "should use POST if verb is not GET" do
57
- @http_yield_mock.should_receive(:post).and_return(@mock_http_response)
58
- @http_mock.should_receive(:start).and_yield(@http_yield_mock)
59
-
60
- Horse.make_request('anything', {}, 'anything')
61
- end
62
-
63
- it "should use GET if that verb is specified" do
64
- @http_yield_mock.should_receive(:get).and_return(@mock_http_response)
65
- @http_mock.should_receive(:start).and_yield(@http_yield_mock)
66
-
67
- Horse.make_request('anything', {}, 'get')
68
- end
69
-
70
- it "should add the method to the arguments if it's not get or post" do
71
- args = {}
72
- method = "telekenesis"
73
- # since the arguments get encoded later, we'll test for merge!
74
- # even though that's somewhat testing internal implementation
75
- args.should_receive(:merge!).with(:method => method)
76
-
77
- Horse.make_request('anything', args, method)
78
- end
79
- end
80
-
81
- describe "if the request has an access token" do
82
- before :each do
83
- @args = {"access_token" => "123"}
84
- end
85
-
86
- it "should use SSL" do
87
- @http_mock.should_receive('use_ssl=').with(true)
88
-
89
- Horse.make_request('anything', @args, 'anything')
90
- end
91
-
92
- it "should set the port to 443" do
93
- Net::HTTP.should_receive(:new).with(anything, 443).and_return(@http_mock)
94
-
95
- Horse.make_request('anything', @args, 'anything')
96
- end
97
- end
98
-
99
- describe "if always_use_ssl is true" do
100
- before :each do
101
- Horse.always_use_ssl = true
102
- end
103
-
104
- it "should use SSL" do
105
- @http_mock.should_receive('use_ssl=').with(true)
106
-
107
- Horse.make_request('anything', {}, 'anything')
108
- end
109
-
110
- it "should set the port to 443" do
111
- Net::HTTP.should_receive(:new).with(anything, 443).and_return(@http_mock)
112
-
113
- Horse.make_request('anything', {}, 'anything')
114
- end
115
- end
116
-
117
- describe "if the use_ssl option is provided" do
118
- it "should use SSL" do
119
- @http_mock.should_receive('use_ssl=').with(true)
120
-
121
- Horse.make_request('anything', {}, 'anything', :use_ssl => true)
122
- end
123
-
124
- it "should set the port to 443" do
125
- Net::HTTP.should_receive(:new).with(anything, 443).and_return(@http_mock)
126
-
127
- Horse.make_request('anything', {}, 'anything', :use_ssl => true)
128
- end
129
- end
130
-
131
- describe "if there's no token and always_use_ssl isn't true" do
132
- it "should not use SSL" do
133
- @http_mock.should_not_receive('use_ssl=')
134
- Horse.make_request('anything', {}, 'anything')
135
- end
136
-
137
- it "should not set the port" do
138
- Net::HTTP.should_receive(:new).with(anything, nil).and_return(@http_mock)
139
- Horse.make_request('anything', {}, 'anything')
140
- end
141
- end
142
-
143
- describe "proxy options" do
144
- before :each do
145
- Horse.proxy = "http://defaultproxy"
146
- end
147
- after :all do
148
- Horse.proxy = nil
149
- end
150
-
151
- it "should use passed proxy option if provided" do
152
- Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything, "passedproxy", 80, nil, nil).and_return(@http_mock)
153
- Horse.make_request('anything', {} , 'anything', {:proxy => "http://passedproxy"})
154
- end
155
-
156
- it "should use default proxy if default is provided and NO proxy option passed" do
157
- Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything, "defaultproxy", 80, nil, nil).and_return(@http_mock)
158
- Horse.make_request('anything', {} , 'anything', {})
159
- end
160
-
161
- it "should NOT use a proxy if default is NOT provided and NO proxy option passed" do
162
- Horse.proxy = nil
163
- Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything).and_return(@http_mock)
164
- Horse.make_request('anything', {} , 'anything', {})
165
- end
166
- end
167
-
168
- describe "timeout options" do
169
- before :each do
170
- Horse.timeout = 20 # seconds
171
- end
172
- after :all do
173
- Horse.timeout = nil # seconds
174
- end
175
-
176
- it "should use passed timeout option if provided" do
177
- @http_mock.should_receive('open_timeout=').with(10)
178
- @http_mock.should_receive('read_timeout=').with(10)
179
- Horse.make_request('anything', {} , 'anything', {:timeout => 10})
180
- end
181
-
182
- it "should use default timout if default is provided and NO timeout option passed" do
183
- @http_mock.should_receive('open_timeout=').with(20)
184
- @http_mock.should_receive('read_timeout=').with(20)
185
- Horse.make_request('anything', {} , 'anything', {})
186
- end
187
-
188
- it "should NOT use a timeout if default is NOT provided and NO timeout option passed" do
189
- Horse.timeout = nil # seconds
190
- @http_mock.should_not_receive('open_timeout=')
191
- @http_mock.should_not_receive('read_timeout=')
192
- Horse.make_request('anything', {} , 'anything', {})
193
- end
194
- end
195
-
196
- describe "ca_file options" do
197
- after :each do
198
- Horse.always_use_ssl = nil
199
- Horse.ca_file = nil
200
- end
201
-
202
- it "should not use a ca_file if the request is not via SSL" do
203
- Horse.always_use_ssl = false
204
- @http_mock.should_not_receive(:ca_file=)
205
- Horse.make_request('anything', {} , 'anything', {:ca_file => '/no/file'})
206
- end
207
-
208
- describe "when via SSL" do
209
- before :each do
210
- Horse.always_use_ssl = true
211
- @global_ca_file_path = '/global/ca/file/path'
212
- end
213
-
214
- context "if the file doesn't exist" do
215
- it "raises Errno::ENOENT if the default ca_file does not exist" do
216
- Horse.ca_file = @global_ca_file_path
217
-
218
- File.should_receive(:exists?).with(@global_ca_file_path).and_return(false)
219
- expect { Horse.make_request('anything', {} , 'anything', {}) }.to raise_exception(Errno::ENOENT)
220
- end
221
-
222
- it "raises Errno::ENOENT if options[:ca_file] does not exist" do
223
- File.should_receive(:exists?).with(@global_ca_file_path).and_return(false)
224
- expect { Horse.make_request('anything', {} , 'anything', {:ca_file => @global_ca_file_path}) }.to raise_exception(Errno::ENOENT)
225
- end
226
- end
227
-
228
- context "if the file exists" do
229
- before :each do
230
- File.stub(:exists?).and_return(true)
231
- end
232
-
233
- it "should use options[:ca_file] if provided" do
234
- given_ca_file = '/ca/file'
235
-
236
- Horse.ca_file = @global_ca_file_path
237
- @http_mock.should_not_receive(:ca_file=).with(@global_ca_file_path)
238
- @http_mock.should_receive(:ca_file=).with(given_ca_file)
239
-
240
- Horse.make_request('anything', {} , 'anything', {:ca_file => given_ca_file})
241
- end
242
-
243
- it "should use default ca_file if default is provided and NO ca_file option is passed" do
244
- Horse.ca_file = @global_ca_file_path
245
- @http_mock.should_receive(:ca_file=).with(@global_ca_file_path)
246
-
247
- Horse.make_request('anything', {} , 'anything', {})
248
- end
249
-
250
- it "should NOT use a ca_file if default is NOT provided and NO ca_file option is passed" do
251
- @http_mock.should_not_receive(:ca_file=)
252
-
253
- Horse.make_request('anything', {} , 'anything', {})
254
- end
255
- end
256
- end
257
- end
258
-
259
- describe "ca_path options" do
260
- after :each do
261
- Horse.always_use_ssl = nil
262
- Horse.ca_path = nil
263
- end
264
-
265
- it "should not use a ca_path if the request is not via SSL" do
266
- Horse.always_use_ssl = false
267
- @http_mock.should_not_receive('ca_path=')
268
- Horse.make_request('anything', {} , 'anything', {:ca_file => '/no/file'})
269
- end
270
-
271
- describe "when via SSL" do
272
- before :each do
273
- Horse.always_use_ssl = true
274
- @global_ca_path = '/global/ca/path'
275
- end
276
-
277
- context "if the directory doesn't exist" do
278
- it "should not use a default ca_path if the default ca_path does not exist" do
279
- Horse.ca_path = @global_ca_path
280
-
281
- File.should_receive(:directory?).with(@global_ca_path).and_return(false)
282
- expect { Horse.make_request('anything', {} , 'anything', {}) }.to raise_exception(Errno::ENOENT)
283
- end
284
-
285
- it "should not use a default ca_path if the default ca_path does not exist" do
286
- File.should_receive(:directory?).with(@global_ca_path).and_return(false)
287
- expect { Horse.make_request('anything', {} , 'anything', {:ca_path => @global_ca_path}) }.to raise_exception(Errno::ENOENT)
288
- end
289
- end
290
-
291
- context "if the directory exists" do
292
- before :each do
293
- File.stub(:directory?).and_return(true)
294
- end
295
-
296
- it "should use passed ca_path options if provided" do
297
- given_ca_path = '/ca/path'
298
-
299
- Horse.ca_path = @global_ca_path
300
- @http_mock.should_not_receive(:ca_ath=).with(@global_ca_path)
301
- @http_mock.should_receive(:ca_path=).with(given_ca_path)
302
-
303
- Horse.make_request('anything', {} , 'anything', {:ca_path => given_ca_path})
304
- end
305
-
306
- it "should use default ca_path if default is provided and NO ca_path option is passed" do
307
- Horse.ca_path = @global_ca_path
308
- @http_mock.should_receive(:ca_path=).with(@global_ca_path)
309
-
310
- Horse.make_request('anything', {} , 'anything', {})
311
- end
312
-
313
- it "should NOT use a ca_path if default is NOT provided and NO ca_path option is passed" do
314
- @http_mock.should_not_receive(:ca_path=)
315
-
316
- Horse.make_request('anything', {} , 'anything', {})
317
- end
318
- end
319
- end
320
- end
321
-
322
- describe "verify_mode options" do
323
- after :each do
324
- Horse.always_use_ssl = nil
325
- Horse.verify_mode = nil
326
- end
327
-
328
- it "does not set verify mode if it's not SSL" do
329
- Horse.always_use_ssl = nil
330
- @http_mock.should_not_receive(:verify_mode=)
331
- Horse.make_request('anything', {} , 'anything', {:verify_mode => "abc"})
332
- end
333
-
334
- context "when making an SSL request" do
335
- before :each do
336
- Horse.always_use_ssl = true
337
- end
338
-
339
- it "sets verify mode if provided in the options" do
340
- mode = "foo"
341
- @http_mock.should_receive(:verify_mode=).with(mode)
342
- Horse.make_request('anything', {} , 'anything', {:verify_mode => mode})
343
- end
344
-
345
- it "sets verify mode to the default if provided (and none set in options)" do
346
- Horse.verify_mode = "foo"
347
- @http_mock.should_receive(:verify_mode=).with(Horse.verify_mode)
348
- Horse.make_request('anything', {} , 'anything', {})
349
- end
350
-
351
- it "sets verify mode to the default if provided (and none set in options)" do
352
- mode = "bar"
353
- Horse.verify_mode = "foo"
354
- @http_mock.should_receive(:verify_mode=).with(mode)
355
- Horse.make_request('anything', {} , 'anything', {:verify_mode => mode})
356
- end
357
-
358
- it "sets verify mode to OpenSSL::SSL::VERIFY_PEER if no default or option is provided" do
359
- @http_mock.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
360
- Horse.make_request('anything', {} , 'anything', {})
361
- end
362
- end
363
- end
364
-
365
- it "should use the graph server by default" do
366
- Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything).and_return(@http_mock)
367
- Horse.make_request('anything', {}, 'anything')
368
- end
369
-
370
- it "should use the REST server if the :rest_api option is true" do
371
- Net::HTTP.should_receive(:new).with(Koala::Facebook::REST_SERVER, anything).and_return(@http_mock)
372
- Horse.make_request('anything', {}, 'anything', :rest_api => true)
373
- end
374
-
375
- it "should start an HTTP connection" do
376
- @http_mock.should_receive(:start).and_yield(@http_yield_mock)
377
- Horse.make_request('anything', {}, 'anything')
378
- end
379
-
380
- it 'creates a HTTP Proxy object when options contain a proxy' do
381
- Net::HTTP.should_receive(:new).with(anything, anything, 'proxy', 1234, 'user', 'pass').and_return(@http_mock)
382
- Horse.make_request('anything', {}, 'anything', {:proxy => 'http://user:pass@proxy:1234'})
383
- end
384
-
385
- it 'sets both timeouts when options contains a timeout' do
386
- @http_mock.should_receive(:open_timeout=).with(10)
387
- @http_mock.should_receive(:read_timeout=).with(10)
388
- Horse.make_request('anything', {}, 'anything', {:timeout => 10})
389
- end
390
-
391
- describe "via POST" do
392
- it "should use Net::HTTP to make a POST request" do
393
- @http_yield_mock.should_receive(:post).and_return(@mock_http_response)
394
-
395
- Horse.make_request('anything', {}, 'post')
396
- end
397
-
398
- it "should go to the specified path adding a / if it doesn't exist" do
399
- path = mock('Path')
400
- @http_yield_mock.should_receive(:post).with(path, anything).and_return(@mock_http_response)
401
-
402
- Horse.make_request(path, {}, 'post')
403
- end
404
-
405
- it "should use encoded parameters" do
406
- args = {}
407
- params = mock('Encoded parameters')
408
- Horse.should_receive(:encode_params).with(args).and_return(params)
409
-
410
- @http_yield_mock.should_receive(:post).with(anything, params).and_return(@mock_http_response)
411
-
412
- Horse.make_request('anything', args, 'post')
413
- end
414
-
415
- describe "with multipart/form-data" do
416
- before(:each) do
417
- Horse.stub(:encode_multipart_params)
418
- Horse.stub("params_require_multipart?").and_return(true)
419
-
420
- @multipart_request_stub = stub('Stub Multipart Request')
421
- Net::HTTP::Post::Multipart.stub(:new).and_return(@multipart_request_stub)
422
-
423
- @file_stub = stub('fake File', "kind_of?" => true, "path" => 'anypath.jpg')
424
-
425
- @http_yield_mock.stub(:request).with(@multipart_request_stub).and_return(@mock_http_response)
426
- end
427
-
428
- it "should use multipart/form-data if any parameter is a valid file hash" do
429
- @http_yield_mock.should_receive(:request).with(@multipart_request_stub).and_return(@mock_http_response)
430
-
431
- Horse.make_request('anything', {}, 'post')
432
- end
433
-
434
- it "should use the given request path for the request" do
435
- args = {"file" => @file_stub}
436
- expected_path = 'expected/path'
437
-
438
- Net::HTTP::Post::Multipart.should_receive(:new).with(expected_path, anything).and_return(@multipart_request_stub)
439
-
440
- Horse.make_request(expected_path, {}, 'post')
441
- end
442
-
443
- it "should use multipart encoded arguments for the request" do
444
- args = {"file" => @file_stub}
445
- expected_params = stub('Stub Multipart Params')
446
-
447
- Horse.should_receive(:encode_multipart_params).with(args).and_return(expected_params)
448
- Net::HTTP::Post::Multipart.should_receive(:new).with(anything, expected_params).and_return(@multipart_request_stub)
449
-
450
- Horse.make_request('anything', args, 'post')
451
- end
452
- end
453
- end
454
-
455
- describe "via GET" do
456
- it "should use Net::HTTP to make a GET request" do
457
- @http_yield_mock.should_receive(:get).and_return(@mock_http_response)
458
-
459
- Horse.make_request('anything', {}, 'get')
460
- end
461
-
462
- it "should use the correct path, including arguments" do
463
- path = mock('Path')
464
- params = mock('Encoded parameters')
465
- args = {}
466
-
467
- Horse.should_receive(:encode_params).with(args).and_return(params)
468
- @http_yield_mock.should_receive(:get).with("#{path}?#{params}").and_return(@mock_http_response)
469
-
470
- Horse.make_request(path, args, 'get')
471
- end
472
- end
473
-
474
- describe "the returned value" do
475
- before(:each) do
476
- @response = Horse.make_request('anything', {}, 'anything')
477
- end
478
-
479
- it "should return a Koala::Response object" do
480
- @response.class.should == Koala::Response
481
- end
482
-
483
- it "should return a Koala::Response with the right status" do
484
- @response.status.should == @mock_http_response.code
485
- end
486
-
487
- it "should reutrn a Koala::Response with the right body" do
488
- @response.body.should == @mock_body
489
- end
490
-
491
- it "should return a Koala::Response with the Net::HTTPResponse object as headers" do
492
- @response.headers.should == @mock_http_response
493
- end
494
- end # describe return value
495
- end # describe when making a request
496
-
497
- describe "when detecting if multipart posting is needed" do
498
- it "should be true if any parameter value requires multipart post" do
499
- koala_io = mock("Koala::IO")
500
- koala_io.should_receive(:kind_of?).with(Koala::UploadableIO).and_return(true)
501
-
502
- args = {
503
- "key1" => "val",
504
- "key2" => "val",
505
- "key3" => koala_io,
506
- "key4" => "val"
507
- }
508
-
509
- Horse.params_require_multipart?(args).should be_true
510
- end
511
-
512
- describe "when encoding multipart/form-data params" do
513
- it "should replace Koala::UploadableIO values with UploadIO values" do
514
- upload_io = UploadIO.new(__FILE__, "fake type")
515
-
516
- uploadable_io = stub('Koala::UploadableIO')
517
- uploadable_io.should_receive(:kind_of?).with(Koala::UploadableIO).and_return(true)
518
- uploadable_io.should_receive(:to_upload_io).and_return(upload_io)
519
- args = {
520
- "not_a_file" => "not a file",
521
- "file" => uploadable_io
522
- }
523
-
524
- result = Horse.encode_multipart_params(args)
525
-
526
- result["not_a_file"] == args["not_a_file"]
527
- result["file"] == upload_io
528
- end
529
- end
530
-
531
- end
532
- end