koala 1.1.0 → 1.2.0beta1

Sign up to get free protection for your applications and to get access to all the features.
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