koala 1.0.0.beta → 1.1.0

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 (69) hide show
  1. data/.autotest +12 -0
  2. data/.gitignore +5 -0
  3. data/.travis.yml +8 -0
  4. data/CHANGELOG +42 -4
  5. data/Gemfile +7 -0
  6. data/LICENSE +1 -1
  7. data/Manifest +5 -2
  8. data/Rakefile +13 -14
  9. data/autotest/discover.rb +1 -0
  10. data/koala.gemspec +35 -20
  11. data/lib/koala/batch_operation.rb +74 -0
  12. data/lib/koala/graph_api.rb +196 -143
  13. data/lib/koala/graph_batch_api.rb +87 -0
  14. data/lib/koala/graph_collection.rb +54 -0
  15. data/lib/koala/http_services/net_http_service.rb +92 -0
  16. data/lib/koala/http_services/typhoeus_service.rb +37 -0
  17. data/lib/koala/http_services.rb +15 -124
  18. data/lib/koala/oauth.rb +181 -0
  19. data/lib/koala/realtime_updates.rb +5 -14
  20. data/lib/koala/rest_api.rb +13 -8
  21. data/lib/koala/test_users.rb +21 -8
  22. data/lib/koala/uploadable_io.rb +175 -0
  23. data/lib/koala.rb +48 -240
  24. data/readme.md +60 -28
  25. data/spec/cases/api_base_spec.rb +101 -0
  26. data/spec/cases/graph_and_rest_api_spec.rb +31 -0
  27. data/spec/cases/graph_api_batch_spec.rb +609 -0
  28. data/spec/cases/graph_api_spec.rb +25 -0
  29. data/spec/cases/http_services/http_service_spec.rb +129 -0
  30. data/spec/cases/http_services/net_http_service_spec.rb +532 -0
  31. data/spec/cases/http_services/typhoeus_service_spec.rb +152 -0
  32. data/spec/cases/koala_spec.rb +55 -0
  33. data/spec/cases/oauth_spec.rb +409 -0
  34. data/spec/cases/realtime_updates_spec.rb +184 -0
  35. data/spec/cases/rest_api_spec.rb +25 -0
  36. data/spec/{koala/test_users/test_users_tests.rb → cases/test_users_spec.rb} +47 -34
  37. data/spec/cases/uploadable_io_spec.rb +193 -0
  38. data/spec/fixtures/cat.m4v +0 -0
  39. data/spec/{facebook_data.yml → fixtures/facebook_data.yml} +12 -14
  40. data/spec/{mock_facebook_responses.yml → fixtures/mock_facebook_responses.yml} +408 -306
  41. data/spec/spec_helper.rb +19 -0
  42. data/spec/support/graph_api_shared_examples.rb +495 -0
  43. data/spec/support/json_testing_fix.rb +18 -0
  44. data/spec/{koala → support}/live_testing_data_helper.rb +39 -42
  45. data/spec/support/mock_http_service.rb +96 -0
  46. data/spec/support/rest_api_shared_examples.rb +285 -0
  47. data/spec/support/setup_mocks_or_live.rb +51 -0
  48. data/spec/support/uploadable_io_shared_examples.rb +76 -0
  49. metadata +110 -64
  50. data/init.rb +0 -2
  51. data/spec/koala/api_base_tests.rb +0 -102
  52. data/spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb +0 -14
  53. data/spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb +0 -16
  54. data/spec/koala/graph_api/graph_api_no_access_token_tests.rb +0 -63
  55. data/spec/koala/graph_api/graph_api_tests.rb +0 -86
  56. data/spec/koala/graph_api/graph_api_with_access_token_tests.rb +0 -154
  57. data/spec/koala/graph_api/graph_collection_tests.rb +0 -104
  58. data/spec/koala/net_http_service_tests.rb +0 -430
  59. data/spec/koala/oauth/oauth_tests.rb +0 -409
  60. data/spec/koala/realtime_updates/realtime_updates_tests.rb +0 -187
  61. data/spec/koala/rest_api/rest_api_no_access_token_tests.rb +0 -25
  62. data/spec/koala/rest_api/rest_api_tests.rb +0 -118
  63. data/spec/koala/rest_api/rest_api_with_access_token_tests.rb +0 -38
  64. data/spec/koala/typhoeus_service_tests.rb +0 -156
  65. data/spec/koala_spec.rb +0 -18
  66. data/spec/koala_spec_helper.rb +0 -70
  67. data/spec/koala_spec_without_mocks.rb +0 -19
  68. data/spec/mock_http_service.rb +0 -96
  69. /data/spec/{koala/assets → fixtures}/beach.jpg +0 -0
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ Deer = Koala::TyphoeusService
5
+
6
+ describe "TyphoeusService" do
7
+
8
+ describe "TyphoeusService module holder class Deer" do
9
+ before :each do
10
+ # reset global settings
11
+ Deer.always_use_ssl = Deer.proxy = Deer.timeout = nil
12
+ end
13
+
14
+ it "should define a make_request static module method" do
15
+ Deer.respond_to?(:make_request).should be_true
16
+ end
17
+
18
+ it "should include the Koala::HTTPService module defining common features" do
19
+ Deer.included_modules.include?(Koala::HTTPService).should be_true
20
+ end
21
+
22
+ describe "when making a request" do
23
+ before(:each) do
24
+ # Setup stubs for make_request to execute without exceptions
25
+ @mock_body = stub('Typhoeus response body')
26
+ @mock_headers_hash = stub({:value => "headers hash"})
27
+ @mock_http_response = stub(Typhoeus::Response, :code => 1, :headers_hash => @mock_headers_hash, :body => @mock_body)
28
+
29
+ # Typhoeus is an included module, so we stub methods on Deer itself
30
+ Typhoeus::Request.stub(:post).and_return(@mock_http_response)
31
+ Typhoeus::Request.stub(:get).and_return(@mock_http_response)
32
+ end
33
+
34
+ it "should use POST if verb is not GET" do
35
+ Typhoeus::Request.should_receive(:post).and_return(@mock_http_response)
36
+ Deer.make_request('anything', {}, 'anything')
37
+ end
38
+
39
+ it "should use GET if that verb is specified" do
40
+ Typhoeus::Request.should_receive(:get).and_return(@mock_http_response)
41
+ Deer.make_request('anything', {}, 'get')
42
+ end
43
+
44
+ describe "the connection" do
45
+ it "should use SSL if the request has an access token" do
46
+ Typhoeus::Request.should_receive(:post).with(/https\:/, anything)
47
+
48
+ Deer.make_request('anything', {"access_token" => "123"}, 'anything')
49
+ end
50
+
51
+ it "should use SSL if always_use_ssl is true, even if there's no token" do
52
+ Typhoeus::Request.should_receive(:post).with(/https\:/, anything)
53
+
54
+ Deer.always_use_ssl = true
55
+ Deer.make_request('anything', {}, 'anything')
56
+ end
57
+
58
+ it "should use SSL if the :use_ssl option is provided, even if there's no token" do
59
+ Typhoeus::Request.should_receive(:post).with(/https\:/, anything)
60
+
61
+ Deer.always_use_ssl = true
62
+ Deer.make_request('anything', {}, 'anything', :use_ssl => true)
63
+ end
64
+
65
+ it "should not use SSL if always_use_ssl is false and there's no token" do
66
+ Typhoeus::Request.should_receive(:post).with(/http\:/, anything)
67
+
68
+ Deer.make_request('anything', {}, 'anything')
69
+ end
70
+
71
+ it "should use the graph server by default" do
72
+ Typhoeus::Request.should_receive(:post).with(Regexp.new(Koala::Facebook::GRAPH_SERVER), anything)
73
+
74
+ Deer.make_request('anything', {}, 'anything')
75
+ end
76
+
77
+ it "should use the REST server if the :rest_api option is true" do
78
+ Typhoeus::Request.should_receive(:post).with(Regexp.new(Koala::Facebook::REST_SERVER), anything)
79
+
80
+ Deer.make_request('anything', {}, 'anything', :rest_api => true)
81
+ end
82
+ end
83
+
84
+ it "should pass the arguments to Typhoeus under the :params key" do
85
+ args = {:a => 2}
86
+ Typhoeus::Request.should_receive(:post).with(anything, hash_including(:params => args))
87
+
88
+ Deer.make_request('anything', args, "post")
89
+ end
90
+
91
+ it "should add the method to the arguments if the method isn't get or post" do
92
+ method = "telekenesis"
93
+ Typhoeus::Request.should_receive(:post).with(anything, hash_including(:params => {:method => method}))
94
+
95
+ Deer.make_request('anything', {}, method)
96
+ end
97
+
98
+ it "should pass :typhoeus_options to Typhoeus if provided" do
99
+ t_options = {:a => :b}
100
+ Typhoeus::Request.should_receive(:post).with(anything, hash_including(t_options))
101
+
102
+ Deer.make_request("anything", {}, "post", :typhoeus_options => t_options)
103
+ end
104
+
105
+ it "should pass proxy and timeout :typhoeus_options to Typhoeus if set globally" do
106
+ Deer.proxy = "http://defaultproxy"
107
+ Deer.timeout = 20
108
+
109
+ t_options = {:proxy => "http://defaultproxy", :timeout => 20}
110
+ Typhoeus::Request.should_receive(:post).with(anything, hash_including(t_options))
111
+
112
+ Deer.make_request("anything", {}, "post")
113
+ end
114
+
115
+ # for live tests, run the Graph API tests with Typhoues, which will run file uploads
116
+ it "should pass any files directly on to Typhoues" do
117
+ args = {:file => File.new(__FILE__, "r")}
118
+ Typhoeus::Request.should_receive(:post).with(anything, hash_including(:params => args)).and_return(Typhoeus::Response.new)
119
+ Deer.make_request("anything", args, :post)
120
+ end
121
+
122
+ it "should include the path in the request" do
123
+ path = "/a/b/c/1"
124
+ Typhoeus::Request.should_receive(:post).with(Regexp.new(path), anything)
125
+
126
+ Deer.make_request(path, {}, "post")
127
+ end
128
+
129
+ describe "the returned value" do
130
+ before(:each) do
131
+ @response = Deer.make_request('anything', {}, 'anything')
132
+ end
133
+
134
+ it "should return a Koala::Response object" do
135
+ @response.class.should == Koala::Response
136
+ end
137
+
138
+ it "should return a Koala::Response with the right status" do
139
+ @response.status.should == @mock_http_response.code
140
+ end
141
+
142
+ it "should reutrn a Koala::Response with the right body" do
143
+ @response.body.should == @mock_body
144
+ end
145
+
146
+ it "should return a Koala::Response with the Typhoeus headers as headers" do
147
+ @response.headers.should == @mock_headers_hash
148
+ end
149
+ end # describe return value
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala" do
4
+ it "has an http_service accessor" do
5
+ Koala.respond_to?(:http_service)
6
+ end
7
+
8
+ it "should let an http service be set" do
9
+ current_service = Koala.http_service
10
+ Koala.http_service = Koala::MockHTTPService
11
+ Koala.http_service.should == Koala::MockHTTPService
12
+ # reset the service back to the original one (important for live tests)
13
+ Koala.http_service = current_service
14
+ end
15
+
16
+ it "sets Net::HTTP as the base service" do
17
+ Koala.base_http_service.should == Koala::NetHTTPService
18
+ end
19
+
20
+ describe ".always_use_ssl" do
21
+ it "should be added" do
22
+ # in Ruby 1.8, .methods returns strings
23
+ # in Ruby 1.9, .method returns symbols
24
+ Koala.methods.collect {|m| m.to_sym}.should include(:always_use_ssl)
25
+ Koala.methods.collect {|m| m.to_sym}.should include(:always_use_ssl=)
26
+ end
27
+ end
28
+
29
+ describe ".make_request" do
30
+
31
+ before :each do
32
+ @old_service = Koala.http_service
33
+ Koala.http_service = Koala::MockHTTPService
34
+ end
35
+
36
+ after :each do
37
+ Koala.http_service = @old_service
38
+ end
39
+
40
+ it "should use SSL if Koala.always_use_ssl is set to true, even if there's no token" do
41
+ Koala.http_service.should_receive(:make_request).with(anything, anything, anything, hash_including(:use_ssl => true))
42
+
43
+ Koala.always_use_ssl = true
44
+ Koala.make_request('anything', {}, 'anything')
45
+ end
46
+
47
+ it "should allow the caller to override the http_service" do
48
+ http_service = stub
49
+ http_service.should_receive(:make_request)
50
+
51
+ Koala.make_request(anything, anything, anything, :http_service => http_service)
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,409 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala::Facebook::OAuth" do
4
+ before :each do
5
+ # make the relevant test data easily accessible
6
+ @oauth_data = $testing_data["oauth_test_data"]
7
+ @app_id = @oauth_data["app_id"]
8
+ @secret = @oauth_data["secret"]
9
+ @code = @oauth_data["code"]
10
+ @callback_url = @oauth_data["callback_url"]
11
+ @raw_token_string = @oauth_data["raw_token_string"]
12
+ @raw_offline_access_token_string = @oauth_data["raw_offline_access_token_string"]
13
+
14
+ # for signed requests (http://developers.facebook.com/docs/authentication/canvas/encryption_proposal)
15
+ @signed_params = @oauth_data["signed_params"]
16
+ @signed_params_result = @oauth_data["signed_params_result"]
17
+
18
+ # this should expanded to cover all variables
19
+ raise Exception, "Must supply app data to run FacebookOAuthTests!" unless @app_id && @secret && @callback_url &&
20
+ @code && @raw_token_string &&
21
+ @raw_offline_access_token_string
22
+
23
+ @oauth = Koala::Facebook::OAuth.new(@app_id, @secret, @callback_url)
24
+
25
+ @time = Time.now
26
+ Time.stub!(:now).and_return(@time)
27
+ @time.stub!(:to_i).and_return(1273363199)
28
+ end
29
+
30
+ # initialization
31
+ it "should properly initialize" do
32
+ @oauth.should
33
+ end
34
+
35
+ it "should properly set attributes" do
36
+ (@oauth.app_id == @app_id &&
37
+ @oauth.app_secret == @secret &&
38
+ @oauth.oauth_callback_url == @callback_url).should be_true
39
+ end
40
+
41
+ it "should properly initialize without a callback_url" do
42
+ @oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
43
+ end
44
+
45
+ it "should properly set attributes without a callback URL" do
46
+ @oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
47
+ (@oauth.app_id == @app_id &&
48
+ @oauth.app_secret == @secret &&
49
+ @oauth.oauth_callback_url == nil).should be_true
50
+ end
51
+
52
+ describe "for cookie parsing" do
53
+ describe "get_user_info_from_cookies" do
54
+ it "should properly parse valid cookies" do
55
+ result = @oauth.get_user_info_from_cookies(@oauth_data["valid_cookies"])
56
+ result.should be_a(Hash)
57
+ end
58
+
59
+ it "should return all the cookie components from valid cookie string" do
60
+ cookie_data = @oauth_data["valid_cookies"]
61
+ parsing_results = @oauth.get_user_info_from_cookies(cookie_data)
62
+ number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
63
+ parsing_results.length.should == number_of_components
64
+ end
65
+
66
+ it "should properly parse valid offline access cookies (e.g. no expiration)" do
67
+ result = @oauth.get_user_info_from_cookies(@oauth_data["offline_access_cookies"])
68
+ result["uid"].should
69
+ end
70
+
71
+ it "should return all the cookie components from offline access cookies" do
72
+ cookie_data = @oauth_data["offline_access_cookies"]
73
+ parsing_results = @oauth.get_user_info_from_cookies(cookie_data)
74
+ number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
75
+ parsing_results.length.should == number_of_components
76
+ end
77
+
78
+ it "shouldn't parse expired cookies" do
79
+ result = @oauth.get_user_info_from_cookies(@oauth_data["expired_cookies"])
80
+ result.should be_nil
81
+ end
82
+
83
+ it "shouldn't parse invalid cookies" do
84
+ # make an invalid string by replacing some values
85
+ bad_cookie_hash = @oauth_data["valid_cookies"].inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
86
+ result = @oauth.get_user_info_from_cookies(bad_cookie_hash)
87
+ result.should be_nil
88
+ end
89
+ end
90
+
91
+ describe "get_user_from_cookies" do
92
+ it "should use get_user_info_from_cookies to parse the cookies" do
93
+ data = @oauth_data["valid_cookies"]
94
+ @oauth.should_receive(:get_user_info_from_cookies).with(data).and_return({})
95
+ @oauth.get_user_from_cookies(data)
96
+ end
97
+
98
+ it "should use return a string if the cookies are valid" do
99
+ result = @oauth.get_user_from_cookies(@oauth_data["valid_cookies"])
100
+ result.should be_a(String)
101
+ end
102
+
103
+ it "should return nil if the cookies are invalid" do
104
+ # make an invalid string by replacing some values
105
+ bad_cookie_hash = @oauth_data["valid_cookies"].inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
106
+ result = @oauth.get_user_from_cookies(bad_cookie_hash)
107
+ result.should be_nil
108
+ end
109
+ end
110
+ end
111
+
112
+ # OAuth URLs
113
+
114
+ describe "for URL generation" do
115
+
116
+ describe "for OAuth codes" do
117
+ # url_for_oauth_code
118
+ it "should generate a properly formatted OAuth code URL with the default values" do
119
+ url = @oauth.url_for_oauth_code
120
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}"
121
+ end
122
+
123
+ it "should generate a properly formatted OAuth code URL when a callback is given" do
124
+ callback = "foo.com"
125
+ url = @oauth.url_for_oauth_code(:callback => callback)
126
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}"
127
+ end
128
+
129
+ it "should generate a properly formatted OAuth code URL when permissions are requested as a string" do
130
+ permissions = "publish_stream,read_stream"
131
+ url = @oauth.url_for_oauth_code(:permissions => permissions)
132
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}&scope=#{permissions}"
133
+ end
134
+
135
+ it "should generate a properly formatted OAuth code URL when permissions are requested as a string" do
136
+ permissions = ["publish_stream", "read_stream"]
137
+ url = @oauth.url_for_oauth_code(:permissions => permissions)
138
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}&scope=#{permissions.join(",")}"
139
+ end
140
+
141
+ it "should generate a properly formatted OAuth code URL when both permissions and callback are provided" do
142
+ permissions = "publish_stream,read_stream"
143
+ callback = "foo.com"
144
+ url = @oauth.url_for_oauth_code(:callback => callback, :permissions => permissions)
145
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}&scope=#{permissions}"
146
+ end
147
+
148
+ it "should generate a properly formatted OAuth code URL when a display is given as a string" do
149
+ url = @oauth.url_for_oauth_code(:display => "page")
150
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}&display=page"
151
+ end
152
+
153
+ it "should raise an exception if no callback is given in initialization or the call" do
154
+ oauth2 = Koala::Facebook::OAuth.new(@app_id, @secret)
155
+ lambda { oauth2.url_for_oauth_code }.should raise_error(ArgumentError)
156
+ end
157
+ end
158
+
159
+ describe "for access token URLs" do
160
+ # url_for_access_token
161
+ it "should generate a properly formatted OAuth token URL when provided a code" do
162
+ url = @oauth.url_for_access_token(@code)
163
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{@callback_url}&client_secret=#{@secret}&code=#{@code}"
164
+ end
165
+
166
+ it "should generate a properly formatted OAuth token URL when provided a callback" do
167
+ callback = "foo.com"
168
+ url = @oauth.url_for_access_token(@code, :callback => callback)
169
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@secret}&code=#{@code}"
170
+ end
171
+ end
172
+ end
173
+
174
+ describe "for fetching access tokens" do
175
+ describe "get_access_token_info" do
176
+ it "should properly get and parse an access token token results into a hash" do
177
+ result = @oauth.get_access_token_info(@code)
178
+ result.should be_a(Hash)
179
+ end
180
+
181
+ it "should properly include the access token results" do
182
+ result = @oauth.get_access_token_info(@code)
183
+ result["access_token"].should
184
+ end
185
+
186
+ it "should raise an error when get_access_token is called with a bad code" do
187
+ lambda { @oauth.get_access_token_info("foo") }.should raise_error(Koala::Facebook::APIError)
188
+ end
189
+ end
190
+
191
+ describe "get_access_token" do
192
+ it "should use get_access_token_info to get and parse an access token token results" do
193
+ result = @oauth.get_access_token(@code)
194
+ result.should be_a(String)
195
+ end
196
+
197
+ it "should return the access token as a string" do
198
+ result = @oauth.get_access_token(@code)
199
+ original = @oauth.get_access_token_info(@code)
200
+ result.should == original["access_token"]
201
+ end
202
+
203
+ it "should raise an error when get_access_token is called with a bad code" do
204
+ lambda { @oauth.get_access_token("foo") }.should raise_error(Koala::Facebook::APIError)
205
+ end
206
+
207
+ it "should pass on any options provided to make_request" do
208
+ options = {:a => 2}
209
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::Response.new(200, "", {}))
210
+ @oauth.get_access_token(@code, options)
211
+ end
212
+ end
213
+
214
+ describe "get_app_access_token_info" do
215
+ it "should properly get and parse an app's access token as a hash" do
216
+ result = @oauth.get_app_access_token_info
217
+ result.should be_a(Hash)
218
+ end
219
+
220
+ it "should include the access token" do
221
+ result = @oauth.get_app_access_token_info
222
+ result["access_token"].should
223
+ end
224
+
225
+ it "should pass on any options provided to make_request" do
226
+ options = {:a => 2}
227
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::Response.new(200, "", {}))
228
+ @oauth.get_app_access_token_info(options)
229
+ end
230
+ end
231
+
232
+ describe "get_app_acess_token" do
233
+ it "should use get_access_token_info to get and parse an access token token results" do
234
+ result = @oauth.get_app_access_token
235
+ result.should be_a(String)
236
+ end
237
+
238
+ it "should return the access token as a string" do
239
+ result = @oauth.get_app_access_token
240
+ original = @oauth.get_app_access_token_info
241
+ result.should == original["access_token"]
242
+ end
243
+
244
+ it "should pass on any options provided to make_request" do
245
+ options = {:a => 2}
246
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::Response.new(200, "", {}))
247
+ @oauth.get_app_access_token(options)
248
+ end
249
+ end
250
+
251
+ describe "protected methods" do
252
+
253
+ # protected methods
254
+ # since these are pretty fundamental and pretty testable, we want to test them
255
+
256
+ # parse_access_token
257
+ it "should properly parse access token results" do
258
+ result = @oauth.send(:parse_access_token, @raw_token_string)
259
+ has_both_parts = result["access_token"] && result["expires"]
260
+ has_both_parts.should
261
+ end
262
+
263
+ it "should properly parse offline access token results" do
264
+ result = @oauth.send(:parse_access_token, @raw_offline_access_token_string)
265
+ has_both_parts = result["access_token"] && !result["expires"]
266
+ has_both_parts.should
267
+ end
268
+
269
+ # fetch_token_string
270
+ # somewhat duplicative with the tests for get_access_token and get_app_access_token
271
+ # but no harm in thoroughness
272
+ it "should fetch a proper token string from Facebook when given a code" do
273
+ result = @oauth.send(:fetch_token_string, :code => @code, :redirect_uri => @callback_url)
274
+ result.should =~ /^access_token/
275
+ end
276
+
277
+ it "should fetch a proper token string from Facebook when asked for the app token" do
278
+ result = @oauth.send(:fetch_token_string, {:type => 'client_cred'}, true)
279
+ result.should =~ /^access_token/
280
+ end
281
+ end
282
+ end
283
+
284
+ describe "for exchanging session keys" do
285
+ describe "with get_token_info_from_session_keys" do
286
+ it "should get an array of session keys from Facebook when passed a single key" do
287
+ result = @oauth.get_tokens_from_session_keys([@oauth_data["session_key"]])
288
+ result.should be_an(Array)
289
+ result.length.should == 1
290
+ end
291
+
292
+ it "should get an array of session keys from Facebook when passed multiple keys" do
293
+ result = @oauth.get_tokens_from_session_keys(@oauth_data["multiple_session_keys"])
294
+ result.should be_an(Array)
295
+ result.length.should == 2
296
+ end
297
+
298
+ it "should return the original hashes" do
299
+ result = @oauth.get_token_info_from_session_keys(@oauth_data["multiple_session_keys"])
300
+ result[0].should be_a(Hash)
301
+ end
302
+
303
+ it "should properly handle invalid session keys" do
304
+ result = @oauth.get_token_info_from_session_keys(["foo", "bar"])
305
+ #it should return nil for each of the invalid ones
306
+ result.each {|r| r.should be_nil}
307
+ end
308
+
309
+ it "should properly handle a mix of valid and invalid session keys" do
310
+ result = @oauth.get_token_info_from_session_keys(["foo"].concat(@oauth_data["multiple_session_keys"]))
311
+ # it should return nil for each of the invalid ones
312
+ result.each_with_index {|r, index| index > 0 ? r.should(be_a(Hash)) : r.should(be_nil)}
313
+ end
314
+
315
+ it "should throw an APIError if Facebook returns an empty body (as happens for instance when the API breaks)" do
316
+ @oauth.should_receive(:fetch_token_string).and_return("")
317
+ lambda { @oauth.get_token_info_from_session_keys(@oauth_data["multiple_session_keys"]) }.should raise_error(Koala::Facebook::APIError)
318
+ end
319
+
320
+ it "should pass on any options provided to make_request" do
321
+ options = {:a => 2}
322
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::Response.new(200, "[{}]", {}))
323
+ @oauth.get_token_info_from_session_keys([], options)
324
+ end
325
+ end
326
+
327
+ describe "with get_tokens_from_session_keys" do
328
+ it "should call get_token_info_from_session_keys" do
329
+ args = @oauth_data["multiple_session_keys"]
330
+ @oauth.should_receive(:get_token_info_from_session_keys).with(args, anything).and_return([])
331
+ @oauth.get_tokens_from_session_keys(args)
332
+ end
333
+
334
+ it "should return an array of strings" do
335
+ args = @oauth_data["multiple_session_keys"]
336
+ result = @oauth.get_tokens_from_session_keys(args)
337
+ result.each {|r| r.should be_a(String) }
338
+ end
339
+
340
+ it "should properly handle invalid session keys" do
341
+ result = @oauth.get_tokens_from_session_keys(["foo", "bar"])
342
+ # it should return nil for each of the invalid ones
343
+ result.each {|r| r.should be_nil}
344
+ end
345
+
346
+ it "should properly handle a mix of valid and invalid session keys" do
347
+ result = @oauth.get_tokens_from_session_keys(["foo"].concat(@oauth_data["multiple_session_keys"]))
348
+ # it should return nil for each of the invalid ones
349
+ result.each_with_index {|r, index| index > 0 ? r.should(be_a(String)) : r.should(be_nil)}
350
+ end
351
+
352
+ it "should pass on any options provided to make_request" do
353
+ options = {:a => 2}
354
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::Response.new(200, "[{}]", {}))
355
+ @oauth.get_tokens_from_session_keys([], options)
356
+ end
357
+ end
358
+
359
+ describe "get_token_from_session_key" do
360
+ it "should call get_tokens_from_session_keys when the get_token_from_session_key is called" do
361
+ key = @oauth_data["session_key"]
362
+ @oauth.should_receive(:get_tokens_from_session_keys).with([key], anything).and_return([])
363
+ @oauth.get_token_from_session_key(key)
364
+ end
365
+
366
+ it "should get back the access token string from get_token_from_session_key" do
367
+ result = @oauth.get_token_from_session_key(@oauth_data["session_key"])
368
+ result.should be_a(String)
369
+ end
370
+
371
+ it "should be the first value in the array" do
372
+ result = @oauth.get_token_from_session_key(@oauth_data["session_key"])
373
+ array = @oauth.get_tokens_from_session_keys([@oauth_data["session_key"]])
374
+ result.should == array[0]
375
+ end
376
+
377
+ it "should properly handle an invalid session key" do
378
+ result = @oauth.get_token_from_session_key("foo")
379
+ result.should be_nil
380
+ end
381
+
382
+ it "should pass on any options provided to make_request" do
383
+ options = {:a => 2}
384
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::Response.new(200, "[{}]", {}))
385
+ @oauth.get_token_from_session_key("", options)
386
+ end
387
+ end
388
+ end
389
+
390
+ describe "for parsing signed requests" do
391
+ # the signed request code is ported directly from Facebook
392
+ # so we only need to test at a high level that it works
393
+ it "should throw an error if the algorithm is unsupported" do
394
+ MultiJson.stub(:decode).and_return("algorithm" => "my fun algorithm")
395
+ lambda { @oauth.parse_signed_request(@signed_request) }.should raise_error
396
+ end
397
+
398
+ it "should throw an error if the signature is invalid" do
399
+ OpenSSL::HMAC.stub!(:hexdigest).and_return("i'm an invalid signature")
400
+ lambda { @oauth.parse_signed_request(@signed_request) }.should raise_error
401
+ end
402
+
403
+ it "properly parses requests" do
404
+ @oauth = Koala::Facebook::OAuth.new(@app_id, @secret || @app_secret)
405
+ @oauth.parse_signed_request(@signed_params).should == @signed_params_result
406
+ end
407
+ end
408
+
409
+ end # describe