koala 1.0.0 → 1.2.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/.autotest +12 -0
- data/.gitignore +3 -1
- data/.travis.yml +9 -0
- data/CHANGELOG +62 -2
- data/Gemfile +8 -0
- data/Rakefile +0 -1
- data/autotest/discover.rb +1 -0
- data/koala.gemspec +13 -14
- data/lib/koala/batch_operation.rb +74 -0
- data/lib/koala/graph_api.rb +145 -132
- data/lib/koala/graph_batch_api.rb +97 -0
- data/lib/koala/graph_collection.rb +59 -0
- data/lib/koala/http_service.rb +176 -0
- data/lib/koala/oauth.rb +191 -0
- data/lib/koala/realtime_updates.rb +23 -29
- data/lib/koala/rest_api.rb +13 -8
- data/lib/koala/test_users.rb +33 -17
- data/lib/koala/uploadable_io.rb +153 -87
- data/lib/koala/utils.rb +11 -0
- data/lib/koala/version.rb +3 -0
- data/lib/koala.rb +59 -217
- data/readme.md +92 -53
- data/spec/cases/{api_base_spec.rb → api_spec.rb} +31 -6
- data/spec/cases/error_spec.rb +32 -0
- data/spec/cases/graph_and_rest_api_spec.rb +12 -21
- data/spec/cases/graph_api_batch_spec.rb +582 -0
- data/spec/cases/graph_api_spec.rb +11 -14
- data/spec/cases/graph_collection_spec.rb +116 -0
- data/spec/cases/http_service_spec.rb +446 -0
- data/spec/cases/koala_spec.rb +54 -0
- data/spec/cases/oauth_spec.rb +319 -213
- data/spec/cases/realtime_updates_spec.rb +45 -31
- data/spec/cases/rest_api_spec.rb +23 -7
- data/spec/cases/test_users_spec.rb +123 -75
- data/spec/cases/uploadable_io_spec.rb +120 -37
- data/spec/cases/utils_spec.rb +10 -0
- data/spec/fixtures/cat.m4v +0 -0
- data/spec/fixtures/facebook_data.yml +26 -24
- data/spec/fixtures/mock_facebook_responses.yml +203 -78
- data/spec/spec_helper.rb +30 -5
- data/spec/support/graph_api_shared_examples.rb +149 -118
- data/spec/support/json_testing_fix.rb +42 -0
- data/spec/support/koala_test.rb +187 -0
- data/spec/support/mock_http_service.rb +62 -58
- data/spec/support/ordered_hash.rb +205 -0
- data/spec/support/rest_api_shared_examples.rb +139 -15
- data/spec/support/uploadable_io_shared_examples.rb +2 -8
- metadata +90 -114
- data/lib/koala/http_services.rb +0 -146
- data/spec/cases/http_services/http_service_spec.rb +0 -54
- data/spec/cases/http_services/net_http_service_spec.rb +0 -350
- data/spec/cases/http_services/typhoeus_service_spec.rb +0 -144
- data/spec/support/live_testing_data_helper.rb +0 -40
- data/spec/support/setup_mocks_or_live.rb +0 -52
|
@@ -16,17 +16,36 @@ shared_examples_for "Koala GraphAPI" do
|
|
|
16
16
|
# GRAPH CALL
|
|
17
17
|
describe "graph_call" do
|
|
18
18
|
it "should pass all arguments to the api method" do
|
|
19
|
-
args = [
|
|
20
|
-
|
|
19
|
+
args = [KoalaTest.user1, {}, "get", {:a => :b}]
|
|
21
20
|
@api.should_receive(:api).with(*args)
|
|
22
|
-
|
|
23
21
|
@api.graph_call(*args)
|
|
24
22
|
end
|
|
25
23
|
|
|
26
24
|
it "should throw an APIError if the result hash has an error key" do
|
|
27
25
|
Koala.stub(:make_request).and_return(Koala::Response.new(500, {"error" => "An error occurred!"}, {}))
|
|
28
|
-
lambda { @api.graph_call(
|
|
26
|
+
lambda { @api.graph_call(KoalaTest.user1, {}) }.should raise_exception(Koala::Facebook::APIError)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "passes the results through GraphCollection.evaluate" do
|
|
30
|
+
result = {}
|
|
31
|
+
@api.stub(:api).and_return(result)
|
|
32
|
+
Koala::Facebook::GraphCollection.should_receive(:evaluate).with(result, @api)
|
|
33
|
+
@api.graph_call("/me")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "returns the results of GraphCollection.evaluate" do
|
|
37
|
+
expected = {}
|
|
38
|
+
@api.stub(:api).and_return([])
|
|
39
|
+
Koala::Facebook::GraphCollection.should_receive(:evaluate).and_return(expected)
|
|
40
|
+
@api.graph_call("/me").should == expected
|
|
29
41
|
end
|
|
42
|
+
|
|
43
|
+
it "returns the post_processing block's results if one is supplied" do
|
|
44
|
+
other_result = [:a, 2, :three]
|
|
45
|
+
block = Proc.new {|r| other_result}
|
|
46
|
+
@api.stub(:api).and_return({})
|
|
47
|
+
@api.graph_call("/me", {}, "get", {}, &block).should == other_result
|
|
48
|
+
end
|
|
30
49
|
end
|
|
31
50
|
|
|
32
51
|
# SEARCH
|
|
@@ -40,20 +59,30 @@ shared_examples_for "Koala GraphAPI" do
|
|
|
40
59
|
|
|
41
60
|
# get_object
|
|
42
61
|
it "should get public data about a user" do
|
|
43
|
-
result = @api.get_object(
|
|
62
|
+
result = @api.get_object(KoalaTest.user1)
|
|
44
63
|
# the results should have an ID and a name, among other things
|
|
45
64
|
(result["id"] && result["name"]).should_not be_nil
|
|
46
65
|
end
|
|
47
66
|
|
|
48
67
|
it "should get public data about a Page" do
|
|
49
|
-
result = @api.get_object(
|
|
68
|
+
result = @api.get_object(KoalaTest.page)
|
|
50
69
|
# the results should have an ID and a name, among other things
|
|
51
70
|
(result["id"] && result["name"]).should
|
|
52
71
|
end
|
|
53
72
|
|
|
73
|
+
it "should return [] from get_objects if passed an empty array" do
|
|
74
|
+
results = @api.get_objects([])
|
|
75
|
+
results.should == []
|
|
76
|
+
end
|
|
77
|
+
|
|
54
78
|
it "should be able to get multiple objects" do
|
|
55
|
-
results = @api.get_objects([
|
|
56
|
-
results.
|
|
79
|
+
results = @api.get_objects([KoalaTest.page, KoalaTest.user1])
|
|
80
|
+
results.should have(2).items
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "should be able to get multiple objects if they're a string" do
|
|
84
|
+
results = @api.get_objects("contextoptional,#{KoalaTest.user1}")
|
|
85
|
+
results.should have(2).items
|
|
57
86
|
end
|
|
58
87
|
|
|
59
88
|
it "should be able to access a user's picture" do
|
|
@@ -61,14 +90,24 @@ shared_examples_for "Koala GraphAPI" do
|
|
|
61
90
|
end
|
|
62
91
|
|
|
63
92
|
it "should be able to access a user's picture, given a picture type" do
|
|
64
|
-
@api.get_picture(
|
|
93
|
+
@api.get_picture(KoalaTest.user2, {:type => 'large'}).should =~ /^http[s]*\:\/\//
|
|
65
94
|
end
|
|
66
95
|
|
|
67
96
|
it "should be able to access connections from public Pages" do
|
|
68
|
-
result = @api.get_connections(
|
|
97
|
+
result = @api.get_connections(KoalaTest.page, "photos")
|
|
69
98
|
result.should be_a(Array)
|
|
70
99
|
end
|
|
71
100
|
|
|
101
|
+
it "should be able to access comments for a URL" do
|
|
102
|
+
result = @api.get_comments_for_urls(["http://developers.facebook.com/blog/post/472"])
|
|
103
|
+
(result["http://developers.facebook.com/blog/post/472"]).should
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "should be able to access comments for 2 URLs" do
|
|
107
|
+
result = @api.get_comments_for_urls(["http://developers.facebook.com/blog/post/490", "http://developers.facebook.com/blog/post/472"])
|
|
108
|
+
(result["http://developers.facebook.com/blog/post/490"] && result["http://developers.facebook.com/blog/post/472"]).should
|
|
109
|
+
end
|
|
110
|
+
|
|
72
111
|
# SEARCH
|
|
73
112
|
it "should be able to search" do
|
|
74
113
|
result = @api.search("facebook")
|
|
@@ -86,9 +125,8 @@ end
|
|
|
86
125
|
|
|
87
126
|
|
|
88
127
|
shared_examples_for "Koala GraphAPI with an access token" do
|
|
89
|
-
|
|
90
128
|
it "should get private data about a user" do
|
|
91
|
-
result = @api.get_object(
|
|
129
|
+
result = @api.get_object(KoalaTest.user1)
|
|
92
130
|
# updated_time should be a pretty fixed test case
|
|
93
131
|
result["updated_time"].should_not be_nil
|
|
94
132
|
end
|
|
@@ -99,11 +137,11 @@ shared_examples_for "Koala GraphAPI with an access token" do
|
|
|
99
137
|
end
|
|
100
138
|
|
|
101
139
|
it "should be able to get multiple objects" do
|
|
102
|
-
result = @api.get_objects([
|
|
140
|
+
result = @api.get_objects([KoalaTest.page, KoalaTest.user1])
|
|
103
141
|
result.length.should == 2
|
|
104
142
|
end
|
|
105
143
|
it "should be able to access connections from users" do
|
|
106
|
-
result = @api.get_connections(
|
|
144
|
+
result = @api.get_connections(KoalaTest.user2, "friends")
|
|
107
145
|
result.length.should > 0
|
|
108
146
|
end
|
|
109
147
|
|
|
@@ -147,36 +185,91 @@ shared_examples_for "Koala GraphAPI with an access token" do
|
|
|
147
185
|
@temporary_object_id.should_not be_nil
|
|
148
186
|
end
|
|
149
187
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
188
|
+
describe ".put_picture" do
|
|
189
|
+
it "should be able to post photos to the user's wall with an open file object" do
|
|
190
|
+
content_type = "image/jpg"
|
|
191
|
+
file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
|
|
192
|
+
|
|
193
|
+
result = @api.put_picture(file, content_type)
|
|
194
|
+
@temporary_object_id = result["id"]
|
|
195
|
+
@temporary_object_id.should_not be_nil
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it "should be able to post photos to the user's wall without an open file object" do
|
|
199
|
+
content_type = "image/jpg",
|
|
200
|
+
file_path = File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg")
|
|
201
|
+
|
|
202
|
+
result = @api.put_picture(file_path, content_type)
|
|
203
|
+
@temporary_object_id = result["id"]
|
|
204
|
+
@temporary_object_id.should_not be_nil
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it "should be able to verify a photo posted to a user's wall" do
|
|
208
|
+
content_type = "image/jpg",
|
|
209
|
+
file_path = File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg")
|
|
210
|
+
|
|
211
|
+
expected_message = "This is the test message"
|
|
212
|
+
|
|
213
|
+
result = @api.put_picture(file_path, content_type, :message => expected_message)
|
|
214
|
+
@temporary_object_id = result["id"]
|
|
215
|
+
@temporary_object_id.should_not be_nil
|
|
216
|
+
|
|
217
|
+
get_result = @api.get_object(@temporary_object_id)
|
|
218
|
+
get_result["name"].should == expected_message
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
describe "using a URL instead of a file" do
|
|
223
|
+
before :each do
|
|
224
|
+
@url = "http://img.slate.com/images/redesign2008/slate_logo.gif"
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it "should be able to post photo to the user's wall using a URL" do
|
|
228
|
+
result = @api.put_picture(@url)
|
|
229
|
+
@temporary_object_id = result["id"]
|
|
230
|
+
@temporary_object_id.should_not be_nil
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
it "should be able to post photo to the user's wall using a URL and an additional param" do
|
|
234
|
+
result = @api.put_picture(@url, :message => "my message")
|
|
235
|
+
@temporary_object_id = result["id"]
|
|
236
|
+
@temporary_object_id.should_not be_nil
|
|
237
|
+
end
|
|
238
|
+
end
|
|
166
239
|
end
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
240
|
+
|
|
241
|
+
describe ".put_video" do
|
|
242
|
+
before :each do
|
|
243
|
+
@cat_movie = File.join(File.dirname(__FILE__), "..", "fixtures", "cat.m4v")
|
|
244
|
+
@content_type = "video/mpeg4"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "should set options[:video] to true" do
|
|
248
|
+
source = stub("UploadIO")
|
|
249
|
+
Koala::UploadableIO.stub(:new).and_return(source)
|
|
250
|
+
source.stub(:requires_base_http_service).and_return(false)
|
|
251
|
+
Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(:video => true)).and_return(Koala::Response.new(200, "[]", {}))
|
|
252
|
+
@api.put_video("foo")
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it "should be able to post videos to the user's wall with an open file object" do
|
|
256
|
+
file = File.open(@cat_movie)
|
|
257
|
+
|
|
258
|
+
result = @api.put_video(file, @content_type)
|
|
259
|
+
@temporary_object_id = result["id"]
|
|
260
|
+
@temporary_object_id.should_not be_nil
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
it "should be able to post videos to the user's wall without an open file object" do
|
|
265
|
+
result = @api.put_video(@cat_movie, @content_type)
|
|
266
|
+
@temporary_object_id = result["id"]
|
|
267
|
+
@temporary_object_id.should_not be_nil
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# note: Facebook doesn't post videos immediately to the wall, due to processing time
|
|
271
|
+
# during which get_object(video_id) will return false
|
|
272
|
+
# hence we can't do the same verify test we do for photos
|
|
180
273
|
end
|
|
181
274
|
|
|
182
275
|
it "should be able to verify a message with an attachment posted to a feed" do
|
|
@@ -220,6 +313,12 @@ shared_examples_for "Koala GraphAPI with an access token" do
|
|
|
220
313
|
like_result.should be_true
|
|
221
314
|
end
|
|
222
315
|
|
|
316
|
+
# Page Access Token Support
|
|
317
|
+
it "gets a page's access token" do
|
|
318
|
+
# we can't test this live since test users (or random real users) can't be guaranteed to have pages to manage
|
|
319
|
+
@api.should_receive(:api).with("my_page", {:fields => "access_token"}, "get", anything)
|
|
320
|
+
@api.get_page_access_token("my_page")
|
|
321
|
+
end
|
|
223
322
|
|
|
224
323
|
# test all methods to make sure they pass data through to the API
|
|
225
324
|
# we run the tests here (rather than in the common shared example group)
|
|
@@ -240,6 +339,7 @@ shared_examples_for "Koala GraphAPI with an access token" do
|
|
|
240
339
|
:search => 3,
|
|
241
340
|
# methods that have special arguments
|
|
242
341
|
:put_picture => ["x.jpg", "image/jpg", {}, "me"],
|
|
342
|
+
:put_video => ["x.mp4", "video/mpeg4", {}, "me"],
|
|
243
343
|
:get_objects => [["x"], {}]
|
|
244
344
|
}.each_pair do |method_name, params|
|
|
245
345
|
it "should pass http options through for #{method_name}" do
|
|
@@ -268,23 +368,17 @@ end
|
|
|
268
368
|
|
|
269
369
|
# GraphCollection
|
|
270
370
|
shared_examples_for "Koala GraphAPI with GraphCollection" do
|
|
271
|
-
|
|
272
|
-
it "should create an array-like object" do
|
|
273
|
-
call = @api.graph_call("contextoptional/photos")
|
|
274
|
-
Koala::Facebook::GraphCollection.new(call, @api).should be_an(Array)
|
|
275
|
-
end
|
|
276
|
-
|
|
277
371
|
describe "when getting a collection" do
|
|
278
372
|
# GraphCollection methods
|
|
279
373
|
it "should get a GraphCollection when getting connections" do
|
|
280
|
-
@result = @api.get_connections(
|
|
374
|
+
@result = @api.get_connections(KoalaTest.page, "photos")
|
|
281
375
|
@result.should be_a(Koala::Facebook::GraphCollection)
|
|
282
376
|
end
|
|
283
377
|
|
|
284
378
|
it "should return nil if the get_collections call fails with nil" do
|
|
285
379
|
# this happens sometimes
|
|
286
380
|
@api.should_receive(:graph_call).and_return(nil)
|
|
287
|
-
@api.get_connections(
|
|
381
|
+
@api.get_connections(KoalaTest.page, "photos").should be_nil
|
|
288
382
|
end
|
|
289
383
|
|
|
290
384
|
it "should get a GraphCollection when searching" do
|
|
@@ -299,74 +393,14 @@ shared_examples_for "Koala GraphAPI with GraphCollection" do
|
|
|
299
393
|
end
|
|
300
394
|
|
|
301
395
|
it "should get a GraphCollection when paging through results" do
|
|
302
|
-
@results = @api.get_page(["search", {"q"=>"facebook", "limit"=>"25", "until"=>
|
|
396
|
+
@results = @api.get_page(["search", {"q"=>"facebook", "limit"=>"25", "until"=> KoalaTest.search_time}])
|
|
303
397
|
@results.should be_a(Koala::Facebook::GraphCollection)
|
|
304
398
|
end
|
|
305
399
|
|
|
306
400
|
it "should return nil if the page call fails with nil" do
|
|
307
401
|
# this happens sometimes
|
|
308
402
|
@api.should_receive(:graph_call).and_return(nil)
|
|
309
|
-
@api.get_page(["search", {"q"=>"facebook", "limit"=>"25", "until"=>
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
# GraphCollection attributes
|
|
313
|
-
describe "the GraphCollection" do
|
|
314
|
-
before(:each) do
|
|
315
|
-
@result = @api.get_connections("contextoptional", "photos")
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
it "should have a read-only paging attribute" do
|
|
319
|
-
lambda { @result.paging }.should_not raise_error
|
|
320
|
-
lambda { @result.paging = "paging" }.should raise_error(NoMethodError)
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
describe "when getting a whole page" do
|
|
324
|
-
before(:each) do
|
|
325
|
-
@second_page = stub("page of Fb graph results")
|
|
326
|
-
@base = stub("base")
|
|
327
|
-
@args = stub("args")
|
|
328
|
-
@page_of_results = stub("page of results")
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
it "should return the previous page of results" do
|
|
332
|
-
@result.should_receive(:previous_page_params).and_return([@base, @args])
|
|
333
|
-
@api.should_receive(:graph_call).with(@base, @args).and_return(@second_page)
|
|
334
|
-
Koala::Facebook::GraphCollection.should_receive(:new).with(@second_page, @api).and_return(@page_of_results)
|
|
335
|
-
|
|
336
|
-
@result.previous_page.should == @page_of_results
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
it "should return the next page of results" do
|
|
340
|
-
@result.should_receive(:next_page_params).and_return([@base, @args])
|
|
341
|
-
@api.should_receive(:graph_call).with(@base, @args).and_return(@second_page)
|
|
342
|
-
Koala::Facebook::GraphCollection.should_receive(:new).with(@second_page, @api).and_return(@page_of_results)
|
|
343
|
-
|
|
344
|
-
@result.next_page.should == @page_of_results
|
|
345
|
-
end
|
|
346
|
-
|
|
347
|
-
it "should return nil it there are no other pages" do
|
|
348
|
-
%w{next previous}.each do |this|
|
|
349
|
-
@result.should_receive("#{this}_page_params".to_sym).and_return(nil)
|
|
350
|
-
@result.send("#{this}_page").should == nil
|
|
351
|
-
end
|
|
352
|
-
end
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
describe "when parsing page paramters" do
|
|
356
|
-
before(:each) do
|
|
357
|
-
@graph_collection = Koala::Facebook::GraphCollection.new({"data" => []}, Koala::Facebook::GraphAPI.new)
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
it "should return the base as the first array entry" do
|
|
361
|
-
base = "url_path"
|
|
362
|
-
@graph_collection.parse_page_url("anything.com/#{base}?anything").first.should == base
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
it "should return the arguments as a hash as the last array entry" do
|
|
366
|
-
args_hash = {"one" => "val_one", "two" => "val_two"}
|
|
367
|
-
@graph_collection.parse_page_url("anything.com/anything?#{args_hash.map {|k,v| "#{k}=#{v}" }.join("&")}").last.should == args_hash
|
|
368
|
-
end
|
|
369
|
-
end
|
|
403
|
+
@api.get_page(["search", {"q"=>"facebook", "limit"=>"25", "until"=> KoalaTest.search_time}]).should be_nil
|
|
370
404
|
end
|
|
371
405
|
end
|
|
372
406
|
end
|
|
@@ -385,12 +419,11 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
|
385
419
|
end
|
|
386
420
|
|
|
387
421
|
it "shouldn't be able to access connections from users" do
|
|
388
|
-
lambda { @api.get_connections("lukeshepard", "
|
|
422
|
+
lambda { @api.get_connections("lukeshepard", "friends") }.should raise_error(Koala::Facebook::APIError)
|
|
389
423
|
end
|
|
390
424
|
|
|
391
425
|
it "should not be able to put an object" do
|
|
392
426
|
lambda { @result = @api.put_object("lukeshepard", "feed", :message => "Hello, world") }.should raise_error(Koala::Facebook::APIError)
|
|
393
|
-
puts "Error! Object #{@result.inspect} somehow put onto Luke Shepard's wall!" if @result
|
|
394
427
|
end
|
|
395
428
|
|
|
396
429
|
# these are not strictly necessary as the other put methods resolve to put_object, but are here for completeness
|
|
@@ -399,13 +432,11 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
|
399
432
|
attachment = {:name => "OAuth Playground", :link => "http://oauth.twoalex.com/"}
|
|
400
433
|
@result = @api.put_wall_post("Hello, world", attachment, "contextoptional")
|
|
401
434
|
end).should raise_error(Koala::Facebook::APIError)
|
|
402
|
-
puts "Error! Object #{@result.inspect} somehow put onto Context Optional's wall!" if @result
|
|
403
435
|
end
|
|
404
436
|
|
|
405
437
|
it "should not be able to comment on an object" do
|
|
406
438
|
# random public post on the ContextOptional wall
|
|
407
439
|
lambda { @result = @api.put_comment("7204941866_119776748033392", "The hackathon was great!") }.should raise_error(Koala::Facebook::APIError)
|
|
408
|
-
puts "Error! Object #{@result.inspect} somehow commented on post 7204941866_119776748033392!" if @result
|
|
409
440
|
end
|
|
410
441
|
|
|
411
442
|
it "should not be able to like an object" do
|
|
@@ -421,4 +452,4 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
|
421
452
|
it "should not be able to delete a like" do
|
|
422
453
|
lambda { @api.delete_like("7204941866_119776748033392") }.should raise_error(Koala::Facebook::APIError)
|
|
423
454
|
end
|
|
424
|
-
end
|
|
455
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# when testing across Ruby versions, we found that JSON string creation inconsistently ordered keys
|
|
2
|
+
# which is a problem because our mock testing service ultimately matches strings to see if requests are mocked
|
|
3
|
+
# this fix solves that problem by ensuring all hashes are created with a consistent key order every time
|
|
4
|
+
module MultiJson
|
|
5
|
+
self.engine = :ok_json
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def encode_with_ordering(object)
|
|
9
|
+
# if it's a hash, recreate it with k/v pairs inserted in sorted-by-key order
|
|
10
|
+
# (for some reason, REE fails if we don't assign the ternary result as a local variable
|
|
11
|
+
# separately from calling encode_original)
|
|
12
|
+
encode_original(sort_object(object))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
alias_method :encode_original, :encode
|
|
16
|
+
alias_method :encode, :encode_with_ordering
|
|
17
|
+
|
|
18
|
+
def decode_with_ordering(string)
|
|
19
|
+
sort_object(decode_original(string))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
alias_method :decode_original, :decode
|
|
23
|
+
alias_method :decode, :decode_with_ordering
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def sort_object(object)
|
|
28
|
+
if object.is_a?(Hash)
|
|
29
|
+
sort_hash(object)
|
|
30
|
+
elsif object.is_a?(Array)
|
|
31
|
+
object.collect {|item| item.is_a?(Hash) ? sort_hash(item) : item}
|
|
32
|
+
else
|
|
33
|
+
object
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def sort_hash(unsorted_hash)
|
|
38
|
+
sorted_hash = KoalaTest::OrderedHash.new(sorted_hash)
|
|
39
|
+
unsorted_hash.keys.sort {|a, b| a.to_s <=> b.to_s}.inject(sorted_hash) {|hash, k| hash[k] = unsorted_hash[k]; hash}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# small helper method for live testing
|
|
2
|
+
module KoalaTest
|
|
3
|
+
|
|
4
|
+
class << self
|
|
5
|
+
attr_accessor :oauth_token, :app_id, :secret, :app_access_token, :code, :session_key
|
|
6
|
+
attr_accessor :oauth_test_data, :subscription_test_data, :search_time
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Test setup
|
|
10
|
+
|
|
11
|
+
def self.setup_test_environment!
|
|
12
|
+
setup_rspec
|
|
13
|
+
|
|
14
|
+
unless ENV['LIVE']
|
|
15
|
+
# By default the Koala specs are run using stubs for HTTP requests,
|
|
16
|
+
# so they won't fail due to Facebook-imposed rate limits or server timeouts.
|
|
17
|
+
#
|
|
18
|
+
# However as a result they are more brittle since
|
|
19
|
+
# we are not testing the latest responses from the Facebook servers.
|
|
20
|
+
# To be certain all specs pass with the current Facebook services,
|
|
21
|
+
# run LIVE=true bundle exec rake spec.
|
|
22
|
+
Koala.http_service = Koala::MockHTTPService
|
|
23
|
+
KoalaTest.setup_test_data(Koala::MockHTTPService::TEST_DATA)
|
|
24
|
+
else
|
|
25
|
+
# Runs Koala specs through the Facebook servers
|
|
26
|
+
# using data for a real app
|
|
27
|
+
live_data = YAML.load_file(File.join(File.dirname(__FILE__), '../fixtures/facebook_data.yml'))
|
|
28
|
+
KoalaTest.setup_test_data(live_data)
|
|
29
|
+
|
|
30
|
+
# allow live tests with different adapters
|
|
31
|
+
adapter = ENV['ADAPTER'] || "typhoeus"# use Typhoeus by default if available
|
|
32
|
+
begin
|
|
33
|
+
require adapter
|
|
34
|
+
Faraday.default_adapter = adapter.to_sym
|
|
35
|
+
rescue LoadError
|
|
36
|
+
puts "Unable to load adapter #{adapter}, using Net::HTTP."
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Koala.http_service.http_options[:beta] = true if ENV["beta"] || ENV["BETA"]
|
|
40
|
+
|
|
41
|
+
# use a test user unless the developer wants to test against a real profile
|
|
42
|
+
unless token = KoalaTest.oauth_token
|
|
43
|
+
KoalaTest.setup_test_users
|
|
44
|
+
else
|
|
45
|
+
KoalaTest.validate_user_info(token)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.setup_rspec
|
|
51
|
+
# set up a global before block to set the token for tests
|
|
52
|
+
# set the token up for
|
|
53
|
+
RSpec.configure do |config|
|
|
54
|
+
config.before :each do
|
|
55
|
+
@token = KoalaTest.oauth_token
|
|
56
|
+
Koala::Utils.stub(:deprecate) # never fire deprecation warnings
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
config.after :each do
|
|
60
|
+
# clean up any objects posted to Facebook
|
|
61
|
+
if @temporary_object_id && !KoalaTest.mock_interface?
|
|
62
|
+
api = @api || (@test_users ? @test_users.graph_api : nil)
|
|
63
|
+
raise "Unable to locate API when passed temporary object to delete!" unless api
|
|
64
|
+
|
|
65
|
+
# wait 10ms to allow Facebook to propagate data so we can delete it
|
|
66
|
+
sleep(0.01)
|
|
67
|
+
|
|
68
|
+
# clean up any objects we've posted
|
|
69
|
+
result = (api.delete_object(@temporary_object_id) rescue false)
|
|
70
|
+
# if we errored out or Facebook returned false, track that
|
|
71
|
+
puts "Unable to delete #{@temporary_object_id}: #{result} (probably a photo or video, which can't be deleted through the API)" unless result
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.setup_test_data(data)
|
|
78
|
+
# make data accessible to all our tests
|
|
79
|
+
self.oauth_test_data = data["oauth_test_data"]
|
|
80
|
+
self.subscription_test_data = data["subscription_test_data"]
|
|
81
|
+
self.oauth_token = data["oauth_token"]
|
|
82
|
+
self.app_id = data["oauth_test_data"]["app_id"]
|
|
83
|
+
self.app_access_token = data["oauth_test_data"]["app_access_token"]
|
|
84
|
+
self.secret = data["oauth_test_data"]["secret"]
|
|
85
|
+
self.code = data["oauth_test_data"]["code"]
|
|
86
|
+
self.session_key = data["oauth_test_data"]["session_key"]
|
|
87
|
+
|
|
88
|
+
# fix the search time so it can be used in the mock responses
|
|
89
|
+
self.search_time = data["search_time"] || (Time.now - 3600).to_s
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.testing_permissions
|
|
93
|
+
"read_stream, publish_stream, user_photos, user_videos, read_insights"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def self.setup_test_users
|
|
97
|
+
# note: we don't have to delete the two test users explicitly, since the test user specs do that for us
|
|
98
|
+
# technically, this is a point of brittleness and would break if the tests were run out of order
|
|
99
|
+
# however, for now we can live with it since it would slow tests way too much to constantly recreate our test users
|
|
100
|
+
print "Setting up test users..."
|
|
101
|
+
@test_user_api = Koala::Facebook::TestUsers.new(:app_id => self.app_id, :secret => self.secret)
|
|
102
|
+
|
|
103
|
+
RSpec.configure do |config|
|
|
104
|
+
config.before :all do
|
|
105
|
+
# before each test module, create two test users with specific names and befriend them
|
|
106
|
+
KoalaTest.create_test_users
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
config.after :all do
|
|
110
|
+
# after each test module, delete the test users to avoid cluttering up the application
|
|
111
|
+
KoalaTest.destroy_test_users
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
puts "done."
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.create_test_users
|
|
119
|
+
@live_testing_user = @test_user_api.create(true, KoalaTest.testing_permissions, :name => KoalaTest.user1_name)
|
|
120
|
+
@live_testing_friend = @test_user_api.create(true, KoalaTest.testing_permissions, :name => KoalaTest.user2_name)
|
|
121
|
+
@test_user_api.befriend(@live_testing_user, @live_testing_friend)
|
|
122
|
+
self.oauth_token = @live_testing_user["access_token"]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.destroy_test_users
|
|
126
|
+
[@live_testing_user, @live_testing_friend].each do |u|
|
|
127
|
+
puts "Unable to delete test user #{u.inspect}" if u && !(@test_user_api.delete(u) rescue false)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.validate_user_info(token)
|
|
132
|
+
print "Validating permissions for live testing..."
|
|
133
|
+
# make sure we have the necessary permissions
|
|
134
|
+
api = Koala::Facebook::API.new(token)
|
|
135
|
+
perms = api.fql_query("select #{testing_permissions} from permissions where uid = me()")[0]
|
|
136
|
+
perms.each_pair do |perm, value|
|
|
137
|
+
if value == (perm == "read_insights" ? 1 : 0) # live testing depends on insights calls failing
|
|
138
|
+
puts "failed!\n" # put a new line after the print above
|
|
139
|
+
raise ArgumentError, "Your access token must have the read_stream, publish_stream, and user_photos permissions, and lack read_insights. You have: #{perms.inspect}"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
puts "done!"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Info about the testing environment
|
|
146
|
+
def self.real_user?
|
|
147
|
+
!(mock_interface? || @test_user)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def self.test_user?
|
|
151
|
+
!!@test_user_api
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.mock_interface?
|
|
155
|
+
Koala.http_service == Koala::MockHTTPService
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Data for testing
|
|
159
|
+
def self.user1
|
|
160
|
+
test_user? ? @live_testing_user["id"] : "koppel"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def self.user1_id
|
|
164
|
+
test_user? ? @live_testing_user["id"] : 2905623
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def self.user1_name
|
|
168
|
+
"Alex"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def self.user2
|
|
172
|
+
test_user? ? @live_testing_friend["id"] : "lukeshepard"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self.user2_id
|
|
176
|
+
test_user? ? @live_testing_friend["id"] : 2901279
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def self.user2_name
|
|
180
|
+
"Luke"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def self.page
|
|
184
|
+
"contextoptional"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
end
|