koala 0.4 → 3.7.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.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +32 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.yardopts +3 -0
- data/Gemfile +25 -0
- data/ISSUE_TEMPLATE +25 -0
- data/LICENSE +22 -0
- data/Manifest +32 -5
- data/PULL_REQUEST_TEMPLATE +11 -0
- data/Rakefile +12 -12
- data/changelog.md +781 -0
- data/code_of_conduct.md +74 -0
- data/koala.gemspec +28 -24
- data/lib/koala/api/batch_operation.rb +86 -0
- data/lib/koala/api/graph_api_methods.rb +504 -0
- data/lib/koala/api/graph_batch_api.rb +167 -0
- data/lib/koala/api/graph_collection.rb +129 -0
- data/lib/koala/api/graph_error_checker.rb +72 -0
- data/lib/koala/api.rb +159 -0
- data/lib/koala/configuration.rb +56 -0
- data/lib/koala/errors.rb +126 -0
- data/lib/koala/http_service/request.rb +133 -0
- data/lib/koala/http_service/response.rb +20 -0
- data/lib/koala/http_service/uploadable_io.rb +183 -0
- data/lib/koala/http_service.rb +108 -0
- data/lib/koala/oauth.rb +342 -0
- data/lib/koala/realtime_updates.rb +151 -0
- data/lib/koala/test_users.rb +189 -0
- data/lib/koala/utils.rb +41 -0
- data/lib/koala/version.rb +3 -0
- data/lib/koala.rb +51 -291
- data/readme.md +269 -21
- data/spec/cases/api_spec.rb +362 -0
- data/spec/cases/configuration_spec.rb +11 -0
- data/spec/cases/error_spec.rb +143 -0
- data/spec/cases/graph_api_batch_spec.rb +788 -0
- data/spec/cases/graph_api_spec.rb +76 -0
- data/spec/cases/graph_collection_spec.rb +192 -0
- data/spec/cases/graph_error_checker_spec.rb +147 -0
- data/spec/cases/http_service/request_spec.rb +250 -0
- data/spec/cases/http_service/response_spec.rb +24 -0
- data/spec/cases/http_service_spec.rb +280 -0
- data/spec/cases/koala_spec.rb +57 -0
- data/spec/cases/koala_test_spec.rb +5 -0
- data/spec/cases/oauth_spec.rb +647 -0
- data/spec/cases/realtime_updates_spec.rb +327 -0
- data/spec/cases/test_users_spec.rb +383 -0
- data/spec/cases/uploadable_io_spec.rb +266 -0
- data/spec/cases/utils_spec.rb +55 -0
- data/spec/fixtures/beach.jpg +0 -0
- data/spec/fixtures/cat.m4v +0 -0
- data/spec/fixtures/facebook_data.yml +63 -0
- data/spec/fixtures/mock_facebook_responses.yml +483 -0
- data/spec/fixtures/vcr_cassettes/app_test_accounts.yml +97 -0
- data/spec/fixtures/vcr_cassettes/friend_list_next_page.yml +121 -0
- data/spec/integration/graph_collection_spec.rb +24 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/custom_matchers.rb +28 -0
- data/spec/support/graph_api_shared_examples.rb +534 -0
- data/spec/support/koala_test.rb +251 -0
- data/spec/support/mock_http_service.rb +140 -0
- data/spec/support/uploadable_io_shared_examples.rb +70 -0
- metadata +206 -62
- data/CHANGELOG +0 -24
- data/init.rb +0 -2
- data/lib/http_services.rb +0 -60
- data/test/facebook_data.yml +0 -5
- data/test/koala/facebook_no_access_token_tests.rb +0 -119
- data/test/koala/facebook_with_access_token_tests.rb +0 -106
- data/test/koala_tests.rb +0 -30
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Koala::Facebook::OAuth" do
|
|
4
|
+
before :all do
|
|
5
|
+
# make the relevant test data easily accessible
|
|
6
|
+
@app_id = KoalaTest.app_id
|
|
7
|
+
@secret = KoalaTest.secret
|
|
8
|
+
@code = KoalaTest.code
|
|
9
|
+
@callback_url = KoalaTest.oauth_test_data["callback_url"]
|
|
10
|
+
@access_token = KoalaTest.oauth_test_data["access_token"]
|
|
11
|
+
@raw_token_string = KoalaTest.oauth_test_data["raw_token_string"]
|
|
12
|
+
@raw_offline_access_token_string = KoalaTest.oauth_test_data["raw_offline_access_token_string"]
|
|
13
|
+
|
|
14
|
+
# for signed requests (http://developers.facebook.com/docs/authentication/canvas/encryption_proposal)
|
|
15
|
+
@signed_params = KoalaTest.oauth_test_data["signed_params"]
|
|
16
|
+
@signed_params_result = KoalaTest.oauth_test_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
|
+
@raw_token_string &&
|
|
21
|
+
@raw_offline_access_token_string
|
|
22
|
+
|
|
23
|
+
# we can just test against the same key twice
|
|
24
|
+
@multiple_session_keys = [KoalaTest.session_key, KoalaTest.session_key] if KoalaTest.session_key
|
|
25
|
+
|
|
26
|
+
@oauth = Koala::Facebook::OAuth.new(@app_id, @secret, @callback_url)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
before :each do
|
|
30
|
+
@time = Time.now
|
|
31
|
+
allow(Time).to receive(:now).and_return(@time)
|
|
32
|
+
allow(@time).to receive(:to_i).and_return(1273363199)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe ".new" do
|
|
36
|
+
it "properly initializes" do
|
|
37
|
+
expect(@oauth).to be_truthy
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "properly sets attributes" do
|
|
41
|
+
expect(@oauth.app_id == @app_id &&
|
|
42
|
+
@oauth.app_secret == @secret &&
|
|
43
|
+
@oauth.oauth_callback_url == @callback_url).to be_truthy
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "properly initializes without a callback_url" do
|
|
47
|
+
@oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "properly sets attributes without a callback URL" do
|
|
51
|
+
@oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
|
|
52
|
+
expect(@oauth.app_id == @app_id &&
|
|
53
|
+
@oauth.app_secret == @secret &&
|
|
54
|
+
@oauth.oauth_callback_url == nil).to be_truthy
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context "with global defaults" do
|
|
58
|
+
let(:app_id) { :app_id }
|
|
59
|
+
let(:app_secret) { :app_secret }
|
|
60
|
+
let(:oauth_callback_url) { :oauth_callback_url }
|
|
61
|
+
|
|
62
|
+
before :each do
|
|
63
|
+
Koala.configure do |config|
|
|
64
|
+
config.app_id = app_id
|
|
65
|
+
config.app_secret = app_secret
|
|
66
|
+
config.oauth_callback_url = oauth_callback_url
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "defaults to the configured data if not otherwise provided" do
|
|
71
|
+
oauth = Koala::Facebook::OAuth.new
|
|
72
|
+
expect(oauth.app_id).to eq(app_id)
|
|
73
|
+
expect(oauth.app_secret).to eq(app_secret)
|
|
74
|
+
expect(oauth.oauth_callback_url).to eq(oauth_callback_url)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "lets you override app_id" do
|
|
78
|
+
other_value = :another_id
|
|
79
|
+
oauth = Koala::Facebook::OAuth.new(other_value)
|
|
80
|
+
expect(oauth.app_id).to eq(other_value)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "lets you override secret" do
|
|
84
|
+
other_value = :another_secret
|
|
85
|
+
oauth = Koala::Facebook::OAuth.new(nil, other_value)
|
|
86
|
+
expect(oauth.app_secret).to eq(other_value)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "lets you override app_id" do
|
|
90
|
+
other_value = :another_token
|
|
91
|
+
oauth = Koala::Facebook::OAuth.new(nil, nil, other_value)
|
|
92
|
+
expect(oauth.oauth_callback_url).to eq(other_value)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "for cookie parsing" do
|
|
98
|
+
describe "get_user_info_from_cookies" do
|
|
99
|
+
context "for signed cookies" do
|
|
100
|
+
before :each do
|
|
101
|
+
# we don't actually want to make requests to Facebook to redeem the code
|
|
102
|
+
@cookie = KoalaTest.oauth_test_data["valid_signed_cookies"]
|
|
103
|
+
@token = "my token"
|
|
104
|
+
allow(@oauth).to receive(:get_access_token_info).and_return("access_token" => @token)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "parses valid cookies" do
|
|
108
|
+
result = @oauth.get_user_info_from_cookies(@cookie)
|
|
109
|
+
expect(result).to be_a(Hash)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "returns all the components in the signed request" do
|
|
113
|
+
result = @oauth.get_user_info_from_cookies(@cookie)
|
|
114
|
+
@oauth.parse_signed_request(@cookie.values.first).each_pair do |k, v|
|
|
115
|
+
expect(result[k]).to eq(v)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "makes a request to Facebook to redeem the code if present" do
|
|
120
|
+
code = "foo"
|
|
121
|
+
allow(@oauth).to receive(:parse_signed_request).and_return({"code" => code})
|
|
122
|
+
expect(@oauth).to receive(:get_access_token_info).with(code, anything)
|
|
123
|
+
@oauth.get_user_info_from_cookies(@cookie)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "sets the code redemption redirect_uri to ''" do
|
|
127
|
+
expect(@oauth).to receive(:get_access_token_info).with(anything, :redirect_uri => '')
|
|
128
|
+
@oauth.get_user_info_from_cookies(@cookie)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context "if the code is missing" do
|
|
132
|
+
it "doesn't make a request to Facebook" do
|
|
133
|
+
allow(@oauth).to receive(:parse_signed_request).and_return({})
|
|
134
|
+
expect(@oauth).to receive(:get_access_token_info).never
|
|
135
|
+
@oauth.get_user_info_from_cookies(@cookie)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "returns nil" do
|
|
139
|
+
allow(@oauth).to receive(:parse_signed_request).and_return({})
|
|
140
|
+
expect(@oauth.get_user_info_from_cookies(@cookie)).to be_nil
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "logs a warning" do
|
|
144
|
+
allow(@oauth).to receive(:parse_signed_request).and_return({})
|
|
145
|
+
expect(Koala::Utils.logger).to receive(:warn)
|
|
146
|
+
@oauth.get_user_info_from_cookies(@cookie)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
context "if the code is present" do
|
|
151
|
+
it "adds the access_token into the hash" do
|
|
152
|
+
expect(@oauth.get_user_info_from_cookies(@cookie)["access_token"]).to eq(@token)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "returns nil if the call to FB returns no data" do
|
|
156
|
+
allow(@oauth).to receive(:get_access_token_info).and_return(nil)
|
|
157
|
+
expect(@oauth.get_user_info_from_cookies(@cookie)).to be_nil
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "returns nil if the call to FB returns an expired code error" do
|
|
161
|
+
allow(@oauth).to receive(:get_access_token_info).and_raise(Koala::Facebook::OAuthTokenRequestError.new(400,
|
|
162
|
+
'{ "error": { "type": "OAuthException", "message": "Code was invalid or expired. Session has expired at unix time 1324044000. The current unix time is 1324300957." } }'
|
|
163
|
+
))
|
|
164
|
+
expect(@oauth.get_user_info_from_cookies(@cookie)).to be_nil
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "raises the error if the call to FB returns a different error" do
|
|
168
|
+
allow(@oauth).to receive(:get_access_token_info).and_raise(Koala::Facebook::OAuthTokenRequestError.new(400,
|
|
169
|
+
'{ "error": { "type": "OtherError", "message": "A Facebook Error" } }'))
|
|
170
|
+
expect { @oauth.get_user_info_from_cookies(@cookie) }.to raise_exception(Koala::Facebook::OAuthTokenRequestError)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "doesn't parse invalid cookies" do
|
|
175
|
+
# make an invalid string by replacing some values
|
|
176
|
+
bad_cookie_hash = @cookie.inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
|
|
177
|
+
result = @oauth.get_user_info_from_cookies(bad_cookie_hash)
|
|
178
|
+
expect(result).to be_nil
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
context "for unsigned cookies" do
|
|
183
|
+
it "properly parses valid cookies" do
|
|
184
|
+
result = @oauth.get_user_info_from_cookies(KoalaTest.oauth_test_data["valid_cookies"])
|
|
185
|
+
expect(result).to be_a(Hash)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "returns all the cookie components from valid cookie string" do
|
|
189
|
+
cookie_data = KoalaTest.oauth_test_data["valid_cookies"]
|
|
190
|
+
parsing_results = @oauth.get_user_info_from_cookies(cookie_data)
|
|
191
|
+
number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
|
|
192
|
+
expect(parsing_results.length).to eq(number_of_components)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it "properly parses valid offline access cookies (e.g. no expiration)" do
|
|
196
|
+
result = @oauth.get_user_info_from_cookies(KoalaTest.oauth_test_data["offline_access_cookies"])
|
|
197
|
+
expect(result["uid"]).to be_truthy
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it "returns all the cookie components from offline access cookies" do
|
|
201
|
+
cookie_data = KoalaTest.oauth_test_data["offline_access_cookies"]
|
|
202
|
+
parsing_results = @oauth.get_user_info_from_cookies(cookie_data)
|
|
203
|
+
number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
|
|
204
|
+
expect(parsing_results.length).to eq(number_of_components)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it "doesn't parse expired cookies" do
|
|
208
|
+
new_time = @time.to_i * 2
|
|
209
|
+
allow(@time).to receive(:to_i).and_return(new_time)
|
|
210
|
+
expect(@oauth.get_user_info_from_cookies(KoalaTest.oauth_test_data["valid_cookies"])).to be_nil
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "doesn't parse invalid cookies" do
|
|
214
|
+
# make an invalid string by replacing some values
|
|
215
|
+
bad_cookie_hash = KoalaTest.oauth_test_data["valid_cookies"].inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
|
|
216
|
+
result = @oauth.get_user_info_from_cookies(bad_cookie_hash)
|
|
217
|
+
expect(result).to be_nil
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
describe "for URL generation" do
|
|
223
|
+
describe "#url_for_oauth_code" do
|
|
224
|
+
it "generates a properly formatted OAuth code URL with the default values" do
|
|
225
|
+
url = @oauth.url_for_oauth_code
|
|
226
|
+
expect(url).to match_url("https://#{Koala.config.dialog_host}/dialog/oauth?client_id=#{@app_id}&redirect_uri=#{CGI.escape @callback_url}")
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it "includes the api version if specified" do
|
|
230
|
+
version = Koala.config.api_version = "v.2.2.2.2"
|
|
231
|
+
url = @oauth.url_for_oauth_code
|
|
232
|
+
expect(url).to match_url("https://#{Koala.config.dialog_host}/#{version}/dialog/oauth?client_id=#{@app_id}&redirect_uri=#{CGI.escape @callback_url}")
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "generates a properly formatted OAuth code URL when a callback is given" do
|
|
236
|
+
callback = "foo.com"
|
|
237
|
+
url = @oauth.url_for_oauth_code(:callback => callback)
|
|
238
|
+
expect(url).to match_url("https://#{Koala.config.dialog_host}/dialog/oauth?client_id=#{@app_id}&redirect_uri=#{callback}")
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
it "generates a properly formatted OAuth code URL when permissions are requested as a string" do
|
|
242
|
+
permissions = "publish_stream,read_stream"
|
|
243
|
+
url = @oauth.url_for_oauth_code(:permissions => permissions)
|
|
244
|
+
expect(url).to match_url("https://#{Koala.config.dialog_host}/dialog/oauth?client_id=#{@app_id}&scope=#{CGI.escape permissions}&redirect_uri=#{CGI.escape @callback_url}")
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "generates a properly formatted OAuth code URL when permissions are requested as a string" do
|
|
248
|
+
permissions = ["publish_stream", "read_stream"]
|
|
249
|
+
url = @oauth.url_for_oauth_code(:permissions => permissions)
|
|
250
|
+
expect(url).to match_url("https://#{Koala.config.dialog_host}/dialog/oauth?client_id=#{@app_id}&scope=#{CGI.escape permissions.join(",")}&redirect_uri=#{CGI.escape @callback_url}")
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it "generates a properly formatted OAuth code URL when both permissions and callback are provided" do
|
|
254
|
+
permissions = "publish_stream,read_stream"
|
|
255
|
+
callback = "foo.com"
|
|
256
|
+
url = @oauth.url_for_oauth_code(:callback => callback, :permissions => permissions)
|
|
257
|
+
expect(url).to match_url("https://#{Koala.config.dialog_host}/dialog/oauth?client_id=#{@app_id}&scope=#{CGI.escape permissions}&redirect_uri=#{CGI.escape callback}")
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
it "generates a properly formatted OAuth code URL when a display is given as a string" do
|
|
261
|
+
url = @oauth.url_for_oauth_code(:display => "page")
|
|
262
|
+
expect(url).to match_url("https://#{Koala.config.dialog_host}/dialog/oauth?client_id=#{@app_id}&display=page&redirect_uri=#{CGI.escape @callback_url}")
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "raises an exception if no callback is given in initialization or the call" do
|
|
266
|
+
oauth2 = Koala::Facebook::OAuth.new(@app_id, @secret)
|
|
267
|
+
expect { oauth2.url_for_oauth_code }.to raise_error(ArgumentError)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it "includes any additional options as URL parameters, appropriately escaped" do
|
|
271
|
+
params = {
|
|
272
|
+
:url => "http://foo.bar?c=2",
|
|
273
|
+
:email => "cdc@b.com"
|
|
274
|
+
}
|
|
275
|
+
url = @oauth.url_for_oauth_code(params)
|
|
276
|
+
params.each_pair do |key, value|
|
|
277
|
+
expect(url).to match(/[\&\?]#{key}=#{CGI.escape value}/)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
describe "#url_for_access_token" do
|
|
283
|
+
before :each do
|
|
284
|
+
# since we're just composing a URL here, we don't need to have a real code
|
|
285
|
+
@code ||= "test_code"
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it "generates a properly formatted OAuth token URL when provided a code" do
|
|
289
|
+
url = @oauth.url_for_access_token(@code)
|
|
290
|
+
expect(url).to match_url("https://#{Koala.config.graph_server}/oauth/access_token?client_id=#{@app_id}&code=#{@code}&client_secret=#{@secret}&redirect_uri=#{CGI.escape @callback_url}")
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it "includes the api version if specified" do
|
|
294
|
+
version = Koala.config.api_version = "v.2.2.2.2"
|
|
295
|
+
url = @oauth.url_for_access_token(@code)
|
|
296
|
+
expect(url).to match_url("https://#{Koala.config.graph_server}/#{version}/oauth/access_token?client_id=#{@app_id}&code=#{@code}&client_secret=#{@secret}&redirect_uri=#{CGI.escape @callback_url}")
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
it "generates a properly formatted OAuth token URL when provided a callback" do
|
|
300
|
+
callback = "foo.com"
|
|
301
|
+
url = @oauth.url_for_access_token(@code, :callback => callback)
|
|
302
|
+
expect(url).to match_url("https://#{Koala.config.graph_server}/oauth/access_token?client_id=#{@app_id}&code=#{@code}&client_secret=#{@secret}&redirect_uri=#{CGI.escape callback}")
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
it "includes any additional options as URL parameters, appropriately escaped" do
|
|
306
|
+
params = {
|
|
307
|
+
:url => "http://foo.bar?c=2",
|
|
308
|
+
:email => "cdc@b.com"
|
|
309
|
+
}
|
|
310
|
+
url = @oauth.url_for_access_token(@code, params)
|
|
311
|
+
params.each_pair do |key, value|
|
|
312
|
+
expect(url).to match(/[\&\?]#{key}=#{CGI.escape value}/)
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
describe "#url_for_dialog" do
|
|
318
|
+
it "builds the base properly" do
|
|
319
|
+
dialog_type = "my_dialog_type"
|
|
320
|
+
expect(@oauth.url_for_dialog(dialog_type)).to match(/^https:\/\/#{Koala.config.dialog_host}\/dialog\/#{dialog_type}/)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
it "includes the api version if specified" do
|
|
324
|
+
version = Koala.config.api_version = "v.2.2.2.2"
|
|
325
|
+
dialog_type = "my_dialog_type"
|
|
326
|
+
expect(@oauth.url_for_dialog(dialog_type)).to match("https:\/\/#{Koala.config.dialog_host}\/#{version}\/dialog\/#{dialog_type}")
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
it "adds the app_id/client_id to the url" do
|
|
330
|
+
automatic_params = {:app_id => @app_id, :client_id => @client_id}
|
|
331
|
+
url = @oauth.url_for_dialog("foo", automatic_params)
|
|
332
|
+
automatic_params.each_pair do |key, value|
|
|
333
|
+
# we're slightly simplifying how encode_params works, but for strings/ints, it's okay
|
|
334
|
+
expect(url).to match(/[\&\?]#{key}=#{CGI.escape value.to_s}/)
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
it "includes any additional options as URL parameters, appropriately escaped" do
|
|
339
|
+
params = {
|
|
340
|
+
:url => "http://foo.bar?c=2",
|
|
341
|
+
:email => "cdc@b.com"
|
|
342
|
+
}
|
|
343
|
+
url = @oauth.url_for_dialog("friends", params)
|
|
344
|
+
params.each_pair do |key, value|
|
|
345
|
+
# we're slightly simplifying how encode_params works, but strings/ints, it's okay
|
|
346
|
+
expect(url).to match(/[\&\?]#{key}=#{CGI.escape value.to_s}/)
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
describe "real examples from FB documentation" do
|
|
351
|
+
# see http://developers.facebook.com/docs/reference/dialogs/
|
|
352
|
+
# slightly brittle (e.g. if parameter order changes), but still useful
|
|
353
|
+
it "can generate a send dialog" do
|
|
354
|
+
url = @oauth.url_for_dialog("send", :name => "People Argue Just to Win", :link => "http://www.nytimes.com/2011/06/15/arts/people-argue-just-to-win-scholars-assert.html")
|
|
355
|
+
expect(url).to match_url("https://www.facebook.com/dialog/send?app_id=#{@app_id}&client_id=#{@app_id}&link=http%3A%2F%2Fwww.nytimes.com%2F2011%2F06%2F15%2Farts%2Fpeople-argue-just-to-win-scholars-assert.html&name=People+Argue+Just+to+Win&redirect_uri=#{CGI.escape @callback_url}")
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it "can generate a feed dialog" do
|
|
359
|
+
url = @oauth.url_for_dialog("feed", :name => "People Argue Just to Win", :link => "http://www.nytimes.com/2011/06/15/arts/people-argue-just-to-win-scholars-assert.html")
|
|
360
|
+
expect(url).to match_url("https://www.facebook.com/dialog/feed?app_id=#{@app_id}&client_id=#{@app_id}&link=http%3A%2F%2Fwww.nytimes.com%2F2011%2F06%2F15%2Farts%2Fpeople-argue-just-to-win-scholars-assert.html&name=People+Argue+Just+to+Win&redirect_uri=#{CGI.escape @callback_url}")
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
it "can generate a oauth dialog" do
|
|
364
|
+
url = @oauth.url_for_dialog("oauth", :scope => "email", :response_type => "token")
|
|
365
|
+
expect(url).to match_url("https://www.facebook.com/dialog/oauth?app_id=#{@app_id}&client_id=#{@app_id}&redirect_uri=#{CGI.escape @callback_url}&response_type=token&scope=email")
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
it "can generate a pay dialog" do
|
|
369
|
+
url = @oauth.url_for_dialog("pay", :order_id => "foo", :credits_purchase => false)
|
|
370
|
+
expect(url).to match_url("https://www.facebook.com/dialog/pay?app_id=#{@app_id}&client_id=#{@app_id}&order_id=foo&credits_purchase=false&redirect_uri=#{CGI.escape @callback_url}")
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
describe 'for generating a client code' do
|
|
377
|
+
describe '#generate_client_code' do
|
|
378
|
+
if KoalaTest.mock_interface? || KoalaTest.oauth_token
|
|
379
|
+
it 'makes a request using the correct endpoint' do
|
|
380
|
+
expect(Koala).to receive(:make_request).with('/oauth/client_code', anything, 'get', anything).and_return(Koala::HTTPService::Response.new(200, '{"code": "fake_client_code"}', {}))
|
|
381
|
+
@oauth.generate_client_code(KoalaTest.oauth_token)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
it 'gets a valid client code returned' do
|
|
385
|
+
expect(Koala).to receive(:make_request).with('/oauth/client_code', anything, 'get', anything).and_return(Koala::HTTPService::Response.new(200, '{"code": "fake_client_code"}', {}))
|
|
386
|
+
result = @oauth.generate_client_code(KoalaTest.oauth_token)
|
|
387
|
+
expect(result).to be_a(String)
|
|
388
|
+
expect(result).to eq('fake_client_code')
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
it 'raises a BadFacebookResponse error when empty response body is returned' do
|
|
392
|
+
expect(Koala).to receive(:make_request).with('/oauth/client_code', anything, 'get', anything).and_return(Koala::HTTPService::Response.new(200, '', {}))
|
|
393
|
+
expect { @oauth.generate_client_code(KoalaTest.oauth_token) }.to raise_error(Koala::Facebook::BadFacebookResponse)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
it 'raises an OAuthTokenRequestError when empty response body is returned' do
|
|
397
|
+
expect(Koala).to receive(:make_request).with('/oauth/client_code', anything, 'get', anything).and_return(Koala::HTTPService::Response.new(400, '', {}))
|
|
398
|
+
expect { @oauth.generate_client_code(KoalaTest.oauth_token) }.to raise_error(Koala::Facebook::OAuthTokenRequestError)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
it 'raises a ServerError when empty response body is returned' do
|
|
402
|
+
expect(Koala).to receive(:make_request).with('/oauth/client_code', anything, 'get', anything).and_return(Koala::HTTPService::Response.new(500, '', {}))
|
|
403
|
+
expect { @oauth.generate_client_code(KoalaTest.oauth_token) }.to raise_error(Koala::Facebook::ServerError)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
it 'raises a KoalaError when empty response body is returned' do
|
|
407
|
+
expect(Koala).to receive(:make_request).with('/oauth/client_code', anything, 'get', anything).and_return(Koala::HTTPService::Response.new(200, '{"client_code":"should_not_be_returned"}', {}))
|
|
408
|
+
expect { @oauth.generate_client_code(KoalaTest.oauth_token) }.to raise_error(Koala::KoalaError)
|
|
409
|
+
end
|
|
410
|
+
else
|
|
411
|
+
pending "Some OAuth token exchange tests will not be run since the access token field in facebook_data.yml is blank."
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
describe "for fetching access tokens" do
|
|
417
|
+
describe "#get_access_token_info" do
|
|
418
|
+
it "uses options[:redirect_uri] if provided" do
|
|
419
|
+
uri = "foo"
|
|
420
|
+
expect(Koala).to receive(:make_request).with(anything, hash_including(:redirect_uri => uri), anything, anything).and_return(Koala::HTTPService::Response.new(200, "", {}))
|
|
421
|
+
@oauth.get_access_token_info(@code, :redirect_uri => uri)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
it "uses the redirect_uri used to create the @oauth if no :redirect_uri option is provided" do
|
|
425
|
+
expect(Koala).to receive(:make_request).with(anything, hash_including(:redirect_uri => @callback_url), anything, anything).and_return(Koala::HTTPService::Response.new(200, "", {}))
|
|
426
|
+
@oauth.get_access_token_info(@code)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
it "makes a GET request" do
|
|
430
|
+
expect(Koala).to receive(:make_request).with(anything, anything, "get", anything).and_return(Koala::HTTPService::Response.new(200, "", {}))
|
|
431
|
+
@oauth.get_access_token_info(@code)
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
it "properly decodes JSON results" do
|
|
435
|
+
result = {
|
|
436
|
+
"access_token" => "foo",
|
|
437
|
+
"expires_in" => "baz",
|
|
438
|
+
"machine_id" => "bar"
|
|
439
|
+
}
|
|
440
|
+
allow(Koala).to receive(:make_request).and_return(
|
|
441
|
+
Koala::HTTPService::Response.new(
|
|
442
|
+
200,
|
|
443
|
+
JSON.dump(result),
|
|
444
|
+
{}
|
|
445
|
+
)
|
|
446
|
+
)
|
|
447
|
+
expect(@oauth.get_access_token_info(@code)).to eq(result)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
it "falls back to URL-style parsing " do
|
|
451
|
+
result = "access_token=foo&expires_in=baz&machine_id=bar"
|
|
452
|
+
allow(Koala).to receive(:make_request).and_return(
|
|
453
|
+
Koala::HTTPService::Response.new(200, result, {})
|
|
454
|
+
)
|
|
455
|
+
expect(@oauth.get_access_token_info(@code)).to eq({
|
|
456
|
+
"access_token" => "foo",
|
|
457
|
+
"expires_in" => "baz",
|
|
458
|
+
"machine_id" => "bar"
|
|
459
|
+
})
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
if KoalaTest.code
|
|
463
|
+
it "properly gets and parses an access token token results into a hash" do
|
|
464
|
+
result = @oauth.get_access_token_info(@code)
|
|
465
|
+
expect(result).to be_a(Hash)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
it "properly includes the access token results" do
|
|
469
|
+
result = @oauth.get_access_token_info(@code)
|
|
470
|
+
expect(result["access_token"]).to be_truthy
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
it "raises an error when get_access_token is called with a bad code" do
|
|
474
|
+
expect { @oauth.get_access_token_info("foo") }.to raise_error(Koala::Facebook::OAuthTokenRequestError)
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
describe "#get_access_token" do
|
|
480
|
+
# TODO refactor these to be proper tests with stubs and tests against real data
|
|
481
|
+
it "passes on any options provided to make_request" do
|
|
482
|
+
options = {:a => 2}
|
|
483
|
+
expect(Koala).to receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::HTTPService::Response.new(200, "", {}))
|
|
484
|
+
@oauth.get_access_token(@code, options)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
if KoalaTest.code
|
|
488
|
+
it "uses get_access_token_info to get and parse an access token token results" do
|
|
489
|
+
result = @oauth.get_access_token(@code)
|
|
490
|
+
expect(result).to be_a(String)
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
it "returns the access token as a string" do
|
|
494
|
+
result = @oauth.get_access_token(@code)
|
|
495
|
+
original = @oauth.get_access_token_info(@code)
|
|
496
|
+
expect(result).to eq(original["access_token"])
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
it "raises an error when get_access_token is called with a bad code" do
|
|
500
|
+
expect { @oauth.get_access_token("foo") }.to raise_error(Koala::Facebook::OAuthTokenRequestError)
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
unless KoalaTest.code
|
|
506
|
+
it "Some OAuth code tests will not be run since the code field in facebook_data.yml is blank."
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
describe "get_app_access_token_info" do
|
|
510
|
+
it "properly gets and parses an app's access token as a hash" do
|
|
511
|
+
result = @oauth.get_app_access_token_info
|
|
512
|
+
expect(result).to be_a(Hash)
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
it "includes the access token" do
|
|
516
|
+
result = @oauth.get_app_access_token_info
|
|
517
|
+
expect(result["access_token"]).to be_truthy
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
it "passes on any options provided to make_request" do
|
|
521
|
+
options = {:a => 2}
|
|
522
|
+
expect(Koala).to receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::HTTPService::Response.new(200, "", {}))
|
|
523
|
+
@oauth.get_app_access_token_info(options)
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
describe "get_app_access_token" do
|
|
528
|
+
it "uses get_access_token_info to get and parse an access token token results" do
|
|
529
|
+
result = @oauth.get_app_access_token
|
|
530
|
+
expect(result).to be_a(String)
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
it "returns the access token as a string" do
|
|
534
|
+
result = @oauth.get_app_access_token
|
|
535
|
+
original = @oauth.get_app_access_token_info
|
|
536
|
+
expect(result).to eq(original["access_token"])
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
it "passes on any options provided to make_request" do
|
|
540
|
+
options = {:a => 2}
|
|
541
|
+
expect(Koala).to receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::HTTPService::Response.new(200, "", {}))
|
|
542
|
+
@oauth.get_app_access_token(options)
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
describe "exchange_access_token_info" do
|
|
547
|
+
if KoalaTest.mock_interface? || KoalaTest.oauth_token
|
|
548
|
+
it "properly gets and parses an app's access token as a hash" do
|
|
549
|
+
result = @oauth.exchange_access_token_info(KoalaTest.oauth_token)
|
|
550
|
+
expect(result).to be_a(Hash)
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
it "includes the access token" do
|
|
554
|
+
result = @oauth.exchange_access_token_info(KoalaTest.oauth_token)
|
|
555
|
+
expect(result["access_token"]).not_to be_nil
|
|
556
|
+
end
|
|
557
|
+
else
|
|
558
|
+
pending "Some OAuth token exchange tests will not be run since the access token field in facebook_data.yml is blank."
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
it "passes on any options provided to make_request" do
|
|
562
|
+
options = {:a => 2}
|
|
563
|
+
expect(Koala).to receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::HTTPService::Response.new(200, "", {}))
|
|
564
|
+
@oauth.exchange_access_token_info(KoalaTest.oauth_token, options)
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
it "raises an error when exchange_access_token_info is called with a bad code" do
|
|
568
|
+
expect { @oauth.exchange_access_token_info("foo") }.to raise_error(Koala::Facebook::OAuthTokenRequestError)
|
|
569
|
+
end
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
describe "exchange_access_token" do
|
|
573
|
+
it "uses get_access_token_info to get and parse an access token token results" do
|
|
574
|
+
hash = {"access_token" => Time.now.to_i * rand}
|
|
575
|
+
allow(@oauth).to receive(:exchange_access_token_info).and_return(hash)
|
|
576
|
+
expect(@oauth.exchange_access_token(KoalaTest.oauth_token)).to eq(hash["access_token"])
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
it "passes on any options provided to make_request" do
|
|
580
|
+
options = {:a => 2}
|
|
581
|
+
expect(Koala).to receive(:make_request).with(anything, anything, anything, hash_including(options)).and_return(Koala::HTTPService::Response.new(200, "", {}))
|
|
582
|
+
@oauth.exchange_access_token(KoalaTest.oauth_token, options)
|
|
583
|
+
end
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
describe "protected methods" do
|
|
587
|
+
# protected methods
|
|
588
|
+
# since these are pretty fundamental and pretty testable, we want to test them
|
|
589
|
+
|
|
590
|
+
# parse_access_token
|
|
591
|
+
it "properly parses access token results" do
|
|
592
|
+
result = @oauth.send(:parse_access_token, @raw_token_string)
|
|
593
|
+
has_both_parts = result["access_token"] && result["expires"]
|
|
594
|
+
expect(has_both_parts).to be_truthy
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
it "properly parses offline access token results" do
|
|
598
|
+
result = @oauth.send(:parse_access_token, @raw_offline_access_token_string)
|
|
599
|
+
has_both_parts = result["access_token"] && !result["expires"]
|
|
600
|
+
expect(has_both_parts).to be true
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
# fetch_token_string
|
|
604
|
+
# somewhat duplicative with the tests for get_access_token and get_app_access_token
|
|
605
|
+
# but no harm in thoroughness
|
|
606
|
+
if KoalaTest.code
|
|
607
|
+
it "fetches a proper token string from Facebook when given a code" do
|
|
608
|
+
result = @oauth.send(:fetch_token_string, :code => @code, :redirect_uri => @callback_url)
|
|
609
|
+
expect(result).to match(/^access_token/)
|
|
610
|
+
end
|
|
611
|
+
else
|
|
612
|
+
it "fetch_token_string code test will not be run since the code field in facebook_data.yml is blank."
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
it "fetches a proper token string from Facebook when asked for the app token" do
|
|
616
|
+
result = @oauth.send(:fetch_token_string, {:grant_type => 'client_credentials'}, true)
|
|
617
|
+
expect(result).to match(/^access_token/)
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
describe "for parsing signed requests" do
|
|
623
|
+
# the signed request code is ported directly from Facebook
|
|
624
|
+
# so we only need to test at a high level that it works
|
|
625
|
+
it "throws an error if the algorithm is unsupported" do
|
|
626
|
+
allow(JSON).to receive(:parse).and_return("algorithm" => "my fun algorithm")
|
|
627
|
+
expect { @oauth.parse_signed_request(@signed_params) }.to raise_error(Koala::Facebook::OAuthSignatureError)
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
it "throws an error if the signature is invalid" do
|
|
631
|
+
allow(OpenSSL::HMAC).to receive(:hexdigest).and_return("i'm an invalid signature")
|
|
632
|
+
expect { @oauth.parse_signed_request(@signed_params) }.to raise_error(Koala::Facebook::OAuthSignatureError)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
it "throws an error if the signature string is empty" do
|
|
636
|
+
# this occasionally happens due to Facebook error
|
|
637
|
+
expect { @oauth.parse_signed_request("") }.to raise_error(Koala::Facebook::OAuthSignatureError)
|
|
638
|
+
expect { @oauth.parse_signed_request("abc-def") }.to raise_error(Koala::Facebook::OAuthSignatureError)
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
it "properly parses requests" do
|
|
642
|
+
@oauth = Koala::Facebook::OAuth.new(@app_id, @secret || @app_secret)
|
|
643
|
+
expect(@oauth.parse_signed_request(@signed_params)).to eq(@signed_params_result)
|
|
644
|
+
end
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
end # describe
|