palidanx-koala 0.9.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.
- data/CHANGELOG +98 -0
- data/LICENSE +22 -0
- data/Manifest +44 -0
- data/Rakefile +16 -0
- data/init.rb +2 -0
- data/koala.gemspec +30 -0
- data/lib/koala.rb +296 -0
- data/lib/koala/graph_api.rb +192 -0
- data/lib/koala/http_services.rb +71 -0
- data/lib/koala/realtime_updates.rb +95 -0
- data/lib/koala/rest_api.rb +23 -0
- data/readme.md +129 -0
- data/spec/facebook_data.yml +56 -0
- data/spec/koala/api_base_tests.rb +95 -0
- data/spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb +10 -0
- data/spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb +11 -0
- data/spec/koala/graph_api/graph_api_no_access_token_tests.rb +114 -0
- data/spec/koala/graph_api/graph_api_with_access_token_tests.rb +150 -0
- data/spec/koala/live_testing_data_helper.rb +15 -0
- data/spec/koala/net_http_service_tests.rb +181 -0
- data/spec/koala/oauth/oauth_tests.rb +440 -0
- data/spec/koala/realtime_updates/realtime_updates_tests.rb +187 -0
- data/spec/koala/rest_api/rest_api_no_access_token_tests.rb +94 -0
- data/spec/koala/rest_api/rest_api_with_access_token_tests.rb +36 -0
- data/spec/koala_spec.rb +18 -0
- data/spec/koala_spec_helper.rb +31 -0
- data/spec/koala_spec_without_mocks.rb +19 -0
- data/spec/mock_facebook_responses.yml +241 -0
- data/spec/mock_http_service.rb +81 -0
- metadata +102 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'koala/http_services'
|
2
|
+
|
3
|
+
class NetHTTPServiceTests < Test::Unit::TestCase
|
4
|
+
module Bear
|
5
|
+
include Koala::NetHTTPService
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should define a make_request static module method" do
|
9
|
+
Bear.respond_to?(:make_request).should be_true
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "when making a request" do
|
13
|
+
before(:each) do
|
14
|
+
# Setup stubs for make_request to execute without exceptions
|
15
|
+
@mock_http_response = stub('Net::HTTPResponse', :code => 1)
|
16
|
+
@mock_body = stub('Net::HTTPResponse body')
|
17
|
+
@http_request_result = [@mock_http_response, @mock_body]
|
18
|
+
|
19
|
+
@http_yield_mock = mock('Net::HTTP start yielded object')
|
20
|
+
|
21
|
+
@http_yield_mock.stub(:post).and_return(@http_request_result)
|
22
|
+
@http_yield_mock.stub(:get).and_return(@http_request_result)
|
23
|
+
|
24
|
+
@http_mock = stub('Net::HTTP object', 'use_ssl=' => true, 'verify_mode=' => true)
|
25
|
+
@http_mock.stub(:start).and_yield(@http_yield_mock)
|
26
|
+
|
27
|
+
Net::HTTP.stub(:new).and_return(@http_mock)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should use POST if verb is not GET" do
|
31
|
+
@http_yield_mock.should_receive(:post).and_return(@mock_http_response)
|
32
|
+
@http_mock.should_receive(:start).and_yield(@http_yield_mock)
|
33
|
+
|
34
|
+
Bear.make_request('anything', {}, 'anything')
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should use port 443" do
|
38
|
+
Net::HTTP.should_receive(:new).with(anything, 443).and_return(@http_mock)
|
39
|
+
|
40
|
+
Bear.make_request('anything', {}, 'anything')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should use SSL" do
|
44
|
+
@http_mock.should_receive('use_ssl=').with(true)
|
45
|
+
|
46
|
+
Bear.make_request('anything', {}, 'anything')
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should use the graph server by default" do
|
50
|
+
Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything).and_return(@http_mock)
|
51
|
+
Bear.make_request('anything', {}, 'anything')
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should use the REST server if the :rest_api option is true" do
|
55
|
+
Net::HTTP.should_receive(:new).with(Koala::Facebook::REST_SERVER, anything).and_return(@http_mock)
|
56
|
+
Bear.make_request('anything', {}, 'anything', :rest_api => true)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should turn off vertificate validaiton warnings" do
|
60
|
+
@http_mock.should_receive('verify_mode=').with(OpenSSL::SSL::VERIFY_NONE)
|
61
|
+
|
62
|
+
Bear.make_request('anything', {}, 'anything')
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should start an HTTP connection" do
|
66
|
+
@http_mock.should_receive(:start).and_yield(@http_yield_mock)
|
67
|
+
Bear.make_request('anything', {}, 'anything')
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "via POST" do
|
71
|
+
it "should use Net::HTTP to make a POST request" do
|
72
|
+
@http_yield_mock.should_receive(:post).and_return(@http_request_result)
|
73
|
+
|
74
|
+
Bear.make_request('anything', {}, 'post')
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should go to the specified path adding a / if it doesn't exist" do
|
78
|
+
path = mock('Path')
|
79
|
+
@http_yield_mock.should_receive(:post).with(path, anything).and_return(@http_request_result)
|
80
|
+
|
81
|
+
Bear.make_request(path, {}, 'post')
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should use encoded parameters" do
|
85
|
+
args = {}
|
86
|
+
params = mock('Encoded parameters')
|
87
|
+
Bear.should_receive(:encode_params).with(args).and_return(params)
|
88
|
+
|
89
|
+
@http_yield_mock.should_receive(:post).with(anything, params).and_return(@http_request_result)
|
90
|
+
|
91
|
+
Bear.make_request('anything', args, 'post')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "via GET" do
|
96
|
+
it "should use Net::HTTP to make a GET request" do
|
97
|
+
@http_yield_mock.should_receive(:get).and_return(@http_request_result)
|
98
|
+
|
99
|
+
Bear.make_request('anything', {}, 'get')
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should use the correct path, including arguments" do
|
103
|
+
path = mock('Path')
|
104
|
+
params = mock('Encoded parameters')
|
105
|
+
args = {}
|
106
|
+
|
107
|
+
Bear.should_receive(:encode_params).with(args).and_return(params)
|
108
|
+
@http_yield_mock.should_receive(:get).with("#{path}?#{params}").and_return(@http_request_result)
|
109
|
+
|
110
|
+
Bear.make_request(path, args, 'get')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "the returned value" do
|
115
|
+
before(:each) do
|
116
|
+
@response = Bear.make_request('anything', {}, 'anything')
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should return a Koala::Response object" do
|
120
|
+
@response.class.should == Koala::Response
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should return a Koala::Response with the right status" do
|
124
|
+
@response.status.should == @mock_http_response.code
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should reutrn a Koala::Response with the right body" do
|
128
|
+
@response.body.should == @mock_body
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should return a Koala::Response with the Net::HTTPResponse object as headers" do
|
132
|
+
@response.headers.should == @mock_http_response
|
133
|
+
end
|
134
|
+
end # describe return value
|
135
|
+
end # describe when making a request
|
136
|
+
|
137
|
+
describe "when encoding parameters" do
|
138
|
+
it "should return an empty string if param_hash evaluates to false" do
|
139
|
+
Bear.encode_params(nil).should == ''
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should convert values to JSON if the value is not a String" do
|
143
|
+
val = 'json_value'
|
144
|
+
not_a_string = 'not_a_string'
|
145
|
+
not_a_string.stub(:class).and_return('NotAString')
|
146
|
+
not_a_string.should_receive(:to_json).and_return(val)
|
147
|
+
|
148
|
+
string = "hi"
|
149
|
+
|
150
|
+
args = {
|
151
|
+
not_a_string => not_a_string,
|
152
|
+
string => string
|
153
|
+
}
|
154
|
+
|
155
|
+
result = Bear.encode_params(args)
|
156
|
+
result.split('&').find do |key_and_val|
|
157
|
+
key_and_val.match("#{not_a_string}=#{val}")
|
158
|
+
end.should be_true
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should escape all values" do
|
162
|
+
args = Hash[*(1..4).map {|i| [i.to_s, "Value #{i}($"]}.flatten]
|
163
|
+
|
164
|
+
result = Bear.encode_params(args)
|
165
|
+
result.split('&').each do |key_val|
|
166
|
+
key, val = key_val.split('=')
|
167
|
+
val.should == CGI.escape(args[key])
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should convert all keys to Strings" do
|
172
|
+
args = Hash[*(1..4).map {|i| [i, "val#{i}"]}.flatten]
|
173
|
+
|
174
|
+
result = Bear.encode_params(args)
|
175
|
+
result.split('&').each do |key_val|
|
176
|
+
key, val = key_val.split('=')
|
177
|
+
key.should == args.find{|key_val_arr| key_val_arr.last == val}.first.to_s
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,440 @@
|
|
1
|
+
# stub the Time class to always return a time for which the valid cookie is still valid
|
2
|
+
class Time
|
3
|
+
def self.now
|
4
|
+
self
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.to_i
|
8
|
+
1273363199
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class FacebookOAuthTests < Test::Unit::TestCase
|
13
|
+
describe "Koala OAuth tests" do
|
14
|
+
before :each do
|
15
|
+
# make the relevant test data easily accessible
|
16
|
+
@oauth_data = $testing_data["oauth_test_data"]
|
17
|
+
@app_id = @oauth_data["app_id"]
|
18
|
+
@secret = @oauth_data["secret"]
|
19
|
+
@code = @oauth_data["code"]
|
20
|
+
@callback_url = @oauth_data["callback_url"]
|
21
|
+
@raw_token_string = @oauth_data["raw_token_string"]
|
22
|
+
@raw_offline_access_token_string = @oauth_data["raw_offline_access_token_string"]
|
23
|
+
|
24
|
+
# per Facebook's example:
|
25
|
+
# http://developers.facebook.com/docs/authentication/canvas
|
26
|
+
# this allows us to use Facebook's example data while running the other live data
|
27
|
+
@request_secret = @oauth_data["request_secret"] || @secret
|
28
|
+
@signed_request = @oauth_data["signed_request"]
|
29
|
+
@signed_request_result = @oauth_data["signed_request_result"]
|
30
|
+
|
31
|
+
# this should expanded to cover all variables
|
32
|
+
raise Exception, "Must supply app data to run FacebookOAuthTests!" unless @app_id && @secret && @callback_url &&
|
33
|
+
@code && @raw_token_string &&
|
34
|
+
@raw_offline_access_token_string
|
35
|
+
|
36
|
+
@oauth = Koala::Facebook::OAuth.new(@app_id, @secret, @callback_url)
|
37
|
+
end
|
38
|
+
|
39
|
+
# initialization
|
40
|
+
it "should properly initialize" do
|
41
|
+
@oauth.should
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should properly set attributes" do
|
45
|
+
(@oauth.app_id == @app_id &&
|
46
|
+
@oauth.app_secret == @secret &&
|
47
|
+
@oauth.oauth_callback_url == @callback_url).should be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should properly initialize without a callback_url" do
|
51
|
+
@oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should properly set attributes without a callback URL" do
|
55
|
+
@oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
|
56
|
+
(@oauth.app_id == @app_id &&
|
57
|
+
@oauth.app_secret == @secret &&
|
58
|
+
@oauth.oauth_callback_url == nil).should be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "for cookie parsing" do
|
62
|
+
describe "get_user_info_from_cookies" do
|
63
|
+
it "should properly parse valid cookies" do
|
64
|
+
result = @oauth.get_user_info_from_cookies(@oauth_data["valid_cookies"])
|
65
|
+
result.should be_a(Hash)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should return all the cookie components from valid cookie string" do
|
69
|
+
cookie_data = @oauth_data["valid_cookies"]
|
70
|
+
parsing_results = @oauth.get_user_info_from_cookies(cookie_data)
|
71
|
+
number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
|
72
|
+
parsing_results.length.should == number_of_components
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should properly parse valid offline access cookies (e.g. no expiration)" do
|
76
|
+
result = @oauth.get_user_info_from_cookies(@oauth_data["offline_access_cookies"])
|
77
|
+
result["uid"].should
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should return all the cookie components from offline access cookies" do
|
81
|
+
cookie_data = @oauth_data["offline_access_cookies"]
|
82
|
+
parsing_results = @oauth.get_user_info_from_cookies(cookie_data)
|
83
|
+
number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
|
84
|
+
parsing_results.length.should == number_of_components
|
85
|
+
end
|
86
|
+
|
87
|
+
it "shouldn't parse expired cookies" do
|
88
|
+
result = @oauth.get_user_info_from_cookies(@oauth_data["expired_cookies"])
|
89
|
+
result.should be_nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it "shouldn't parse invalid cookies" do
|
93
|
+
# make an invalid string by replacing some values
|
94
|
+
bad_cookie_hash = @oauth_data["valid_cookies"].inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
|
95
|
+
result = @oauth.get_user_info_from_cookies(bad_cookie_hash)
|
96
|
+
result.should be_nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "get_user_from_cookies" do
|
101
|
+
it "should use get_user_info_from_cookies to parse the cookies" do
|
102
|
+
data = @oauth_data["valid_cookies"]
|
103
|
+
@oauth.should_receive(:get_user_info_from_cookies).with(data).and_return({})
|
104
|
+
@oauth.get_user_from_cookies(data)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should use return a string if the cookies are valid" do
|
108
|
+
result = @oauth.get_user_from_cookies(@oauth_data["valid_cookies"])
|
109
|
+
result.should be_a(String)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should return nil if the cookies are invalid" do
|
113
|
+
# make an invalid string by replacing some values
|
114
|
+
bad_cookie_hash = @oauth_data["valid_cookies"].inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
|
115
|
+
result = @oauth.get_user_from_cookies(bad_cookie_hash)
|
116
|
+
result.should be_nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# OAuth URLs
|
122
|
+
|
123
|
+
describe "for URL generation" do
|
124
|
+
|
125
|
+
describe "for OAuth codes" do
|
126
|
+
# url_for_oauth_code
|
127
|
+
it "should generate a properly formatted OAuth code URL with the default values" do
|
128
|
+
url = @oauth.url_for_oauth_code
|
129
|
+
url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}"
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should generate a properly formatted OAuth code URL when a callback is given" do
|
133
|
+
callback = "foo.com"
|
134
|
+
url = @oauth.url_for_oauth_code(:callback => callback)
|
135
|
+
url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}"
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should generate a properly formatted OAuth code URL when permissions are requested as a string" do
|
139
|
+
permissions = "publish_stream,read_stream"
|
140
|
+
url = @oauth.url_for_oauth_code(:permissions => permissions)
|
141
|
+
url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}&scope=#{permissions}"
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should generate a properly formatted OAuth code URL when permissions are requested as a string" do
|
145
|
+
permissions = ["publish_stream", "read_stream"]
|
146
|
+
url = @oauth.url_for_oauth_code(:permissions => permissions)
|
147
|
+
url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}&scope=#{permissions.join(",")}"
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should generate a properly formatted OAuth code URL when both permissions and callback are provided" do
|
151
|
+
permissions = "publish_stream,read_stream"
|
152
|
+
callback = "foo.com"
|
153
|
+
url = @oauth.url_for_oauth_code(:callback => callback, :permissions => permissions)
|
154
|
+
url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}&scope=#{permissions}"
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should raise an exception if no callback is given in initialization or the call" do
|
158
|
+
oauth2 = Koala::Facebook::OAuth.new(@app_id, @secret)
|
159
|
+
lambda { oauth2.url_for_oauth_code }.should raise_error(ArgumentError)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "for access token URLs" do
|
164
|
+
|
165
|
+
# url_for_access_token
|
166
|
+
it "should generate a properly formatted OAuth token URL when provided a code" do
|
167
|
+
url = @oauth.url_for_access_token(@code)
|
168
|
+
url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{@callback_url}&client_secret=#{@secret}&code=#{@code}"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should generate a properly formatted OAuth token URL when provided a callback" do
|
172
|
+
callback = "foo.com"
|
173
|
+
url = @oauth.url_for_access_token(@code, :callback => callback)
|
174
|
+
url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@secret}&code=#{@code}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "for fetching access tokens" do
|
180
|
+
describe "get_access_token_info" do
|
181
|
+
it "should properly get and parse an access token token results into a hash" do
|
182
|
+
result = @oauth.get_access_token_info(@code)
|
183
|
+
result.should be_a(Hash)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should properly include the access token results" do
|
187
|
+
result = @oauth.get_access_token_info(@code)
|
188
|
+
result["access_token"].should
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should raise an error when get_access_token is called with a bad code" do
|
192
|
+
lambda { @oauth.get_access_token_info("foo") }.should raise_error(Koala::Facebook::APIError)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "get_access_token" do
|
197
|
+
it "should use get_access_token_info to get and parse an access token token results" do
|
198
|
+
result = @oauth.get_access_token(@code)
|
199
|
+
result.should be_a(String)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should return the access token as a string" do
|
203
|
+
result = @oauth.get_access_token(@code)
|
204
|
+
original = @oauth.get_access_token_info(@code)
|
205
|
+
result.should == original["access_token"]
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should raise an error when get_access_token is called with a bad code" do
|
209
|
+
lambda { @oauth.get_access_token("foo") }.should raise_error(Koala::Facebook::APIError)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "get_app_access_token_info" do
|
214
|
+
it "should properly get and parse an app's access token as a hash" do
|
215
|
+
result = @oauth.get_app_access_token_info
|
216
|
+
result.should be_a(Hash)
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should include the access token" do
|
220
|
+
result = @oauth.get_app_access_token_info
|
221
|
+
result["access_token"].should
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe "get_app_acess_token" do
|
226
|
+
it "should use get_access_token_info to get and parse an access token token results" do
|
227
|
+
result = @oauth.get_app_access_token
|
228
|
+
result.should be_a(String)
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should return the access token as a string" do
|
232
|
+
result = @oauth.get_app_access_token
|
233
|
+
original = @oauth.get_app_access_token_info
|
234
|
+
result.should == original["access_token"]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe "protected methods" do
|
239
|
+
|
240
|
+
# protected methods
|
241
|
+
# since these are pretty fundamental and pretty testable, we want to test them
|
242
|
+
|
243
|
+
# parse_access_token
|
244
|
+
it "should properly parse access token results" do
|
245
|
+
result = @oauth.send(:parse_access_token, @raw_token_string)
|
246
|
+
has_both_parts = result["access_token"] && result["expires"]
|
247
|
+
has_both_parts.should
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should properly parse offline access token results" do
|
251
|
+
result = @oauth.send(:parse_access_token, @raw_offline_access_token_string)
|
252
|
+
has_both_parts = result["access_token"] && !result["expires"]
|
253
|
+
has_both_parts.should
|
254
|
+
end
|
255
|
+
|
256
|
+
# fetch_token_string
|
257
|
+
# somewhat duplicative with the tests for get_access_token and get_app_access_token
|
258
|
+
# but no harm in thoroughness
|
259
|
+
it "should fetch a proper token string from Facebook when given a code" do
|
260
|
+
result = @oauth.send(:fetch_token_string, :code => @code, :redirect_uri => @callback_url)
|
261
|
+
result.should =~ /^access_token/
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should fetch a proper token string from Facebook when asked for the app token" do
|
265
|
+
result = @oauth.send(:fetch_token_string, {:type => 'client_cred'}, true)
|
266
|
+
result.should =~ /^access_token/
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
describe "for exchanging session keys" do
|
272
|
+
describe "with get_token_info_from_session_keys" do
|
273
|
+
it "should get an array of session keys from Facebook when passed a single key" do
|
274
|
+
result = @oauth.get_tokens_from_session_keys([@oauth_data["session_key"]])
|
275
|
+
result.should be_an(Array)
|
276
|
+
result.length.should == 1
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should get an array of session keys from Facebook when passed multiple keys" do
|
280
|
+
result = @oauth.get_tokens_from_session_keys(@oauth_data["multiple_session_keys"])
|
281
|
+
result.should be_an(Array)
|
282
|
+
result.length.should == 2
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should return the original hashes" do
|
286
|
+
result = @oauth.get_token_info_from_session_keys(@oauth_data["multiple_session_keys"])
|
287
|
+
result[0].should be_a(Hash)
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should properly handle invalid session keys" do
|
291
|
+
result = @oauth.get_token_info_from_session_keys(["foo", "bar"])
|
292
|
+
#it should return nil for each of the invalid ones
|
293
|
+
result.each {|r| r.should be_nil}
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should properly handle a mix of valid and invalid session keys" do
|
297
|
+
result = @oauth.get_token_info_from_session_keys(["foo"].concat(@oauth_data["multiple_session_keys"]))
|
298
|
+
# it should return nil for each of the invalid ones
|
299
|
+
result.each_with_index {|r, index| index > 0 ? r.should(be_a(Hash)) : r.should(be_nil)}
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
describe "with get_tokens_from_session_keys" do
|
304
|
+
it "should call get_token_info_from_session_keys" do
|
305
|
+
args = @oauth_data["multiple_session_keys"]
|
306
|
+
@oauth.should_receive(:get_token_info_from_session_keys).with(args).and_return([])
|
307
|
+
@oauth.get_tokens_from_session_keys(args)
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should return an array of strings" do
|
311
|
+
args = @oauth_data["multiple_session_keys"]
|
312
|
+
result = @oauth.get_tokens_from_session_keys(args)
|
313
|
+
result.each {|r| r.should be_a(String) }
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should properly handle invalid session keys" do
|
317
|
+
result = @oauth.get_tokens_from_session_keys(["foo", "bar"])
|
318
|
+
# it should return nil for each of the invalid ones
|
319
|
+
result.each {|r| r.should be_nil}
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should properly handle a mix of valid and invalid session keys" do
|
323
|
+
result = @oauth.get_tokens_from_session_keys(["foo"].concat(@oauth_data["multiple_session_keys"]))
|
324
|
+
# it should return nil for each of the invalid ones
|
325
|
+
result.each_with_index {|r, index| index > 0 ? r.should(be_a(String)) : r.should(be_nil)}
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
describe "get_token_from_session_key" do
|
330
|
+
it "should call get_tokens_from_session_keys when the get_token_from_session_key is called" do
|
331
|
+
key = @oauth_data["session_key"]
|
332
|
+
@oauth.should_receive(:get_tokens_from_session_keys).with([key]).and_return([])
|
333
|
+
@oauth.get_token_from_session_key(key)
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should get back the access token string from get_token_from_session_key" do
|
337
|
+
result = @oauth.get_token_from_session_key(@oauth_data["session_key"])
|
338
|
+
result.should be_a(String)
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should be the first value in the array" do
|
342
|
+
result = @oauth.get_token_from_session_key(@oauth_data["session_key"])
|
343
|
+
array = @oauth.get_tokens_from_session_keys([@oauth_data["session_key"]])
|
344
|
+
result.should == array[0]
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should properly handle an invalid session key" do
|
348
|
+
result = @oauth.get_token_from_session_key("foo")
|
349
|
+
result.should be_nil
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
describe "for parsing signed requests" do
|
355
|
+
before :each do
|
356
|
+
# you can test against live data by updating the YML, or you can use the default data
|
357
|
+
# which tests against Facebook's example on http://developers.facebook.com/docs/authentication/canvas
|
358
|
+
@oauth = Koala::Facebook::OAuth.new(@app_id, @request_secret || @app_secret)
|
359
|
+
end
|
360
|
+
|
361
|
+
it "should break the request into the encoded signature and the payload" do
|
362
|
+
@signed_request.should_receive(:split).with(".").and_return(["", ""])
|
363
|
+
@oauth.parse_signed_request(@signed_request)
|
364
|
+
end
|
365
|
+
|
366
|
+
it "should base64 URL decode the signed request" do
|
367
|
+
sig = ""
|
368
|
+
@signed_request.should_receive(:split).with(".").and_return([sig, "1"])
|
369
|
+
@oauth.should_receive(:base64_url_decode).with(sig).and_return("4")
|
370
|
+
@oauth.parse_signed_request(@signed_request)
|
371
|
+
end
|
372
|
+
|
373
|
+
it "should base64 URL decode the signed request" do
|
374
|
+
sig = @signed_request.split(".")[0]
|
375
|
+
@oauth.should_receive(:base64_url_decode).with(sig).and_return(nil)
|
376
|
+
@oauth.parse_signed_request(@signed_request)
|
377
|
+
end
|
378
|
+
|
379
|
+
it "should get the sha64 encoded payload using proper arguments from OpenSSL::HMAC" do
|
380
|
+
payload = ""
|
381
|
+
@signed_request.should_receive(:split).with(".").and_return(["1", payload])
|
382
|
+
OpenSSL::HMAC.should_receive(:digest).with("sha256", @request_secret, payload)
|
383
|
+
@oauth.parse_signed_request(@signed_request)
|
384
|
+
end
|
385
|
+
|
386
|
+
it "should compare the encoded payload with the signature" do
|
387
|
+
sig = "2"
|
388
|
+
@oauth.should_receive(:base64_url_decode).and_return(sig)
|
389
|
+
encoded_payload = "1"
|
390
|
+
OpenSSL::HMAC.should_receive(:digest).with(anything, anything, anything).and_return(encoded_payload)
|
391
|
+
encoded_payload.should_receive(:==).with(sig)
|
392
|
+
@oauth.parse_signed_request(@signed_request)
|
393
|
+
end
|
394
|
+
|
395
|
+
describe "if the encoded payload matches the signature" do
|
396
|
+
before :each do
|
397
|
+
# set it up so the sig will match the encoded payload
|
398
|
+
raw_sig = ""
|
399
|
+
@sig = "2"
|
400
|
+
@payload = "1"
|
401
|
+
@signed_request.should_receive(:split).and_return([raw_sig, @payload])
|
402
|
+
@oauth.should_receive(:base64_url_decode).with(raw_sig).and_return(@sig)
|
403
|
+
OpenSSL::HMAC.should_receive(:digest).with(anything, anything, anything).and_return(@sig.dup)
|
404
|
+
end
|
405
|
+
|
406
|
+
it "should base64_url_decode the payload" do
|
407
|
+
@oauth.should_receive(:base64_url_decode).with(@payload).ordered.and_return("{}")
|
408
|
+
@oauth.parse_signed_request(@signed_request)
|
409
|
+
end
|
410
|
+
|
411
|
+
it "should JSON decode the payload" do
|
412
|
+
result = "{}"
|
413
|
+
@oauth.should_receive(:base64_url_decode).with(@payload).and_return(result)
|
414
|
+
JSON.should_receive(:parse).with(result)
|
415
|
+
@oauth.parse_signed_request(@signed_request)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
describe "if the encoded payload does not match the signature" do
|
420
|
+
before :each do
|
421
|
+
sig = ""
|
422
|
+
@signed_request.should_receive(:split).and_return([sig, ""])
|
423
|
+
OpenSSL::HMAC.should_receive(:digest).with(anything, anything, anything).and_return("hi")
|
424
|
+
end
|
425
|
+
|
426
|
+
it "should return nil" do
|
427
|
+
@oauth.parse_signed_request(@signed_request).should be_nil
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
describe "run against data" do
|
432
|
+
it "should work" do
|
433
|
+
@oauth.parse_signed_request(@signed_request).should == @signed_request_result
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
end # describe
|
439
|
+
|
440
|
+
end #class
|