koala 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. data/.gitignore +3 -1
  2. data/.rspec +1 -0
  3. data/.travis.yml +4 -0
  4. data/.yardopts +3 -0
  5. data/CHANGELOG +28 -0
  6. data/Gemfile +14 -0
  7. data/Guardfile +6 -0
  8. data/koala.gemspec +3 -3
  9. data/lib/koala/api/batch_operation.rb +83 -0
  10. data/lib/koala/api/graph_api.rb +476 -0
  11. data/lib/koala/{graph_batch_api.rb → api/graph_batch_api.rb} +22 -17
  12. data/lib/koala/api/graph_collection.rb +107 -0
  13. data/lib/koala/api/legacy.rb +26 -0
  14. data/lib/koala/{rest_api.rb → api/rest_api.rb} +34 -13
  15. data/lib/koala/api.rb +93 -0
  16. data/lib/koala/http_service/multipart_request.rb +41 -0
  17. data/lib/koala/http_service/response.rb +18 -0
  18. data/lib/koala/http_service/uploadable_io.rb +187 -0
  19. data/lib/koala/http_service.rb +69 -20
  20. data/lib/koala/oauth.rb +170 -36
  21. data/lib/koala/realtime_updates.rb +89 -51
  22. data/lib/koala/test_users.rb +122 -32
  23. data/lib/koala/utils.rb +11 -4
  24. data/lib/koala/version.rb +1 -1
  25. data/lib/koala.rb +16 -96
  26. data/readme.md +9 -9
  27. data/spec/cases/api_spec.rb +19 -12
  28. data/spec/cases/error_spec.rb +10 -0
  29. data/spec/cases/graph_api_batch_spec.rb +100 -58
  30. data/spec/cases/graph_collection_spec.rb +23 -7
  31. data/spec/cases/http_service_spec.rb +5 -26
  32. data/spec/cases/koala_spec.rb +22 -4
  33. data/spec/cases/legacy_spec.rb +115 -0
  34. data/spec/cases/multipart_request_spec.rb +7 -7
  35. data/spec/cases/oauth_spec.rb +134 -48
  36. data/spec/cases/realtime_updates_spec.rb +154 -47
  37. data/spec/cases/test_users_spec.rb +276 -219
  38. data/spec/cases/uploadable_io_spec.rb +1 -1
  39. data/spec/cases/utils_spec.rb +29 -5
  40. data/spec/fixtures/mock_facebook_responses.yml +41 -30
  41. data/spec/spec_helper.rb +3 -0
  42. data/spec/support/custom_matchers.rb +28 -0
  43. data/spec/support/graph_api_shared_examples.rb +192 -14
  44. data/spec/support/koala_test.rb +10 -1
  45. data/spec/support/mock_http_service.rb +2 -2
  46. data/spec/support/rest_api_shared_examples.rb +5 -165
  47. metadata +75 -99
  48. data/lib/koala/batch_operation.rb +0 -74
  49. data/lib/koala/graph_api.rb +0 -270
  50. data/lib/koala/graph_collection.rb +0 -59
  51. data/lib/koala/multipart_request.rb +0 -35
  52. data/lib/koala/uploadable_io.rb +0 -181
  53. data/spec/cases/graph_and_rest_api_spec.rb +0 -22
  54. data/spec/cases/graph_api_spec.rb +0 -22
  55. data/spec/cases/rest_api_spec.rb +0 -22
@@ -14,30 +14,15 @@
14
14
  rest_api:
15
15
 
16
16
  # -- Stubbed Responses --
17
- /method/fql.query:
18
- query=select first_name from user where uid = 2901279:
19
- get:
20
- no_token: '[{"first_name":"Luke"}]'
21
- with_token: '[{"first_name":"Luke"}]'
22
- query=select read_stream from permissions where uid = 2901279:
23
- get:
24
- with_token: '[{"read_stream":1}]'
25
- no_token: '{"error_code":104,"error_msg":"Requires valid signature","request_args":[{"key":"method","value":"fql.query"},{"key":"format","value":"json"},{"key":"query","value":"select read_stream from permissions where uid = 2901279"}]}'
26
-
27
- /method/fql.multiquery:
28
- 'queries=<%= MultiJson.encode({"query1" => "select post_id from stream where source_id = me()", "query2" => "select fromid from comment where post_id in (select post_id from #query1)", "query3" => "select uid, name from user where uid in (select fromid from #query2)"}) %>':
29
- get:
30
- with_token: '[{"name":"query1", "fql_result_set":[]},{"name":"query2", "fql_result_set":[]},{"name":"query3", "fql_result_set":[]}]'
31
- no_token: '{"error_code":104,"error_msg":"Requires valid signature","request_args":[{"key":"method","value":"fql.query"},{"key":"format","value":"json"},{"key":"query","value":"select read_stream from permissions where uid = 2901279"}]}'
32
- 'queries=<%= MultiJson.encode({"query1" => "select first_name from user where uid = 2901279", "query2" => "select first_name from user where uid = 2905623"}) %>':
33
- get:
34
- with_token: '[{"name":"query1", "fql_result_set":[{"first_name":"Luke"}]},{"name":"query2", "fql_result_set":[{"first_name":"Alex"}]}]'
35
- no_token: '[{"name":"query1", "fql_result_set":[{"first_name":"Luke"}]},{"name":"query2", "fql_result_set":[{"first_name":"Alex"}]}]'
36
17
  /method/admin.setAppProperties:
37
18
  'properties={"desktop":0}':
38
19
  post:
39
20
  with_token: 'true'
40
-
21
+ /method/fql.query: # for testing the beta tier
22
+ query=select first_name from user where uid = 2901279:
23
+ get:
24
+ no_token: '[{"first_name":"Luke"}]'
25
+ with_token: '[{"first_name":"Luke"}]'
41
26
 
42
27
 
43
28
 
@@ -227,29 +212,26 @@ graph_api:
227
212
  with_token: '{"data": [{}], "paging": {}}'
228
213
 
229
214
  /lukeshepard/picture:
230
- type=large:
215
+ no_args:
231
216
  get:
232
217
  no_token:
233
218
  code: 302
234
219
  headers:
235
- Location: https://facebook.com/large
220
+ Location: http://facebook.com/
236
221
  with_token:
237
222
  code: 302
238
223
  headers:
239
- Location: https://facebook.com/large
240
-
241
-
242
- /chris.baclig/picture:
243
- no_args:
224
+ Location: http://facebook.com/
225
+ type=large:
244
226
  get:
245
227
  no_token:
246
228
  code: 302
247
229
  headers:
248
- Location: http://facebook.com/
230
+ Location: https://facebook.com/large
249
231
  with_token:
250
232
  code: 302
251
233
  headers:
252
- Location: http://facebook.com/
234
+ Location: https://facebook.com/large
253
235
 
254
236
  /comments:
255
237
  ids=http://developers.facebook.com/blog/post/472:
@@ -279,6 +261,25 @@ graph_api:
279
261
  with_token: '{"data": [{"id": "507731521_100412693339488"}], "paging": {"previous": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000", "next": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000"}}'
280
262
  no_token: '{"data": [{"id": "507731521_100412693339488"}], "paging": {"previous": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000", "next": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000"}}'
281
263
 
264
+ /fql:
265
+ q=select uid, first_name from user where uid = 2901279:
266
+ get:
267
+ no_token: '[{"uid":2901279,"first_name":"Luke"}]'
268
+ with_token: '[{"uid":2901279,"first_name":"Luke"}]'
269
+ q=select read_stream from permissions where uid = 2901279:
270
+ get:
271
+ <<: *token_required
272
+ with_token: '[{"read_stream":1}]'
273
+ 'q=<%= MultiJson.encode({"query1" => "select post_id from stream where source_id = me()", "query2" => "select fromid from comment where post_id in (select post_id from #query1)", "query3" => "select uid, name from user where uid in (select fromid from #query2)"}) %>':
274
+ get:
275
+ <<: *token_required
276
+ with_token: '[{"name":"query1", "fql_result_set":[]},{"name":"query2", "fql_result_set":[]},{"name":"query3", "fql_result_set":[]}]'
277
+ 'q=<%= MultiJson.encode({"query1" => "select uid, first_name from user where uid = 2901279", "query2" => "select uid, first_name from user where uid = 2905623"}) %>':
278
+ get:
279
+ with_token: '[{"name":"query1", "fql_result_set":[{"uid":2901279,"first_name":"Luke"}]},{"name":"query2", "fql_result_set":[{"uid":"2905623","first_name":"Alex"}]}]'
280
+ no_token: '[{"name":"query1", "fql_result_set":[{"uid":2901279,"first_name":"Luke"}]},{"name":"query2", "fql_result_set":[{"uid":"2905623","first_name":"Alex"}]}]'
281
+
282
+
282
283
  '/115349521819193_113815981982767':
283
284
  no_args:
284
285
  delete:
@@ -290,6 +291,12 @@ graph_api:
290
291
  <<: *token_required
291
292
  with_token: '{"access_token": "<%= APP_ACCESS_TOKEN %>"}'
292
293
 
294
+
295
+ '/<%= APP_ID %>':
296
+ restrictions=<%= MultiJson.encode({"age_distr" => "13+"}) %>:
297
+ post:
298
+ with_token: "true"
299
+
293
300
  # -- OAuth responses --
294
301
  /oauth/access_token:
295
302
  client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&code=<%= OAUTH_CODE %>&redirect_uri=<%= OAUTH_DATA["callback_url"] %>:
@@ -326,6 +333,10 @@ graph_api:
326
333
  post:
327
334
  with_token:
328
335
  code: 200
336
+ callback_url=<%= SUBSCRIPTION_DATA["subscription_path"] %>&fields=name&object=user:
337
+ post:
338
+ with_token:
339
+ code: 200
329
340
  callback_url=<%= SUBSCRIPTION_DATA["subscription_path"] %>foo&fields=name&object=user&verify_token=<%= SUBSCRIPTION_DATA["verify_token"] %>:
330
341
  post:
331
342
  with_token: '{"error":{"type":"Exception","message":"(#2200) subscription validation failed"}}'
@@ -415,7 +426,7 @@ graph_api:
415
426
  with_token: '{"id": "888888888", "access_token":"<%= ACCESS_TOKEN %>", "login_url":"https://www.facebook.com/platform/test_account.."}'
416
427
  no_args:
417
428
  get:
418
- with_token: '{"data":[{"id": "999999999", "access_token":"<%= ACCESS_TOKEN %>", "login_url":"https://www.facebook.com/platform/test_account.."}, {"id": "888888888", "access_token":"119908831367602|o3wswWQ88LYjEC9-ukR_gjRIOMw.", "login_url":"https://www.facebook.com/platform/test_account.."}]}'
429
+ with_token: '{"data":[{"id": "999999999", "access_token":"<%= ACCESS_TOKEN %>", "login_url":"https://www.facebook.com/platform/test_account.."}, {"id": "888888888", "access_token":"119908831367602|o3wswWQ88LYjEC9-ukR_gjRIOMw.", "login_url":"https://www.facebook.com/platform/test_account.."}], "paging":{"next":"https://graph.facebook.com/119908831367602/accounts/test-users?access_token=119908831367602|o3wswWQ88LYjEC9-ukR_gjRIOMw&limit=50&offset=50&__after_id=100003241848740"}}'
419
430
 
420
431
  /999999999:
421
432
  no_args:
data/spec/spec_helper.rb CHANGED
@@ -21,6 +21,9 @@ end
21
21
  # See https://github.com/tenderlove/psych/issues/8 for more details
22
22
  YAML::ENGINE.yamler = 'syck' if RUBY_VERSION == '1.9.2' && RUBY_PATCHLEVEL < 290
23
23
 
24
+ # load custom RSpec matchers
25
+ require 'support/custom_matchers'
26
+
24
27
  # load the library
25
28
  require 'koala'
26
29
 
@@ -0,0 +1,28 @@
1
+ # Verifies that two URLs are equal, ignoring the order of the query string parameters
2
+ RSpec::Matchers.define :match_url do |url|
3
+ match do |original_url|
4
+ base, query_string = url.split("?")
5
+ original_base, original_query_string = original_url.split("?")
6
+ query_hash = query_to_params(query_string)
7
+ original_query_hash = query_to_params(original_query_string)
8
+
9
+ # the base URLs need to match
10
+ base.should == original_base
11
+
12
+ # the number of parameters should match (avoid one being a subset of the other)
13
+ query_hash.values.length.should == original_query_hash.values.length
14
+
15
+ # and ensure all the keys and values match
16
+ query_hash.each_pair do |key, value|
17
+ original_query_hash[key].should == value
18
+ end
19
+ end
20
+
21
+ def query_to_params(query_string)
22
+ query_string.split("&").inject({}) do |hash, segment|
23
+ k, v = segment.split("=")
24
+ hash[k] = v
25
+ hash
26
+ end
27
+ end
28
+ end
@@ -8,7 +8,7 @@ shared_examples_for "Koala GraphAPI" do
8
8
  anything,
9
9
  anything,
10
10
  hash_not_including(:rest_api => true)
11
- ).and_return(Koala::Response.new(200, "", {}))
11
+ ).and_return(Koala::HTTPService::Response.new(200, "", {}))
12
12
 
13
13
  @api.api("anything")
14
14
  end
@@ -22,7 +22,7 @@ shared_examples_for "Koala GraphAPI" do
22
22
  end
23
23
 
24
24
  it "throws an APIError if the result hash has an error key" do
25
- Koala.stub(:make_request).and_return(Koala::Response.new(500, {"error" => "An error occurred!"}, {}))
25
+ Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(500, {"error" => "An error occurred!"}, {}))
26
26
  lambda { @api.graph_call(KoalaTest.user1, {}) }.should raise_exception(Koala::Facebook::APIError)
27
27
  end
28
28
 
@@ -86,7 +86,7 @@ shared_examples_for "Koala GraphAPI" do
86
86
  end
87
87
 
88
88
  it "can access a user's picture" do
89
- @api.get_picture("chris.baclig").should =~ /http[s]*\:\/\//
89
+ @api.get_picture(KoalaTest.user2).should =~ /http[s]*\:\/\//
90
90
  end
91
91
 
92
92
  it "can access a user's picture, given a picture type" do
@@ -128,6 +128,63 @@ shared_examples_for "Koala GraphAPI" do
128
128
  # the results should have an ID and a name, among other things
129
129
  (result["id"] && result["name"]).should_not be_nil
130
130
  end
131
+
132
+ # FQL
133
+ describe "#fql_query" do
134
+ it "makes a request to /fql" do
135
+ @api.should_receive(:get_object).with("fql", anything, anything)
136
+ @api.fql_query stub('query string')
137
+ end
138
+
139
+ it "passes a query argument" do
140
+ query = stub('query string')
141
+ @api.should_receive(:get_object).with(anything, hash_including(:q => query), anything)
142
+ @api.fql_query(query)
143
+ end
144
+
145
+ it "passes on any other arguments provided" do
146
+ args = {:a => 2}
147
+ @api.should_receive(:get_object).with(anything, hash_including(args), anything)
148
+ @api.fql_query("a query", args)
149
+ end
150
+ end
151
+
152
+ describe "#fql_multiquery" do
153
+ it "makes a request to /fql" do
154
+ @api.should_receive(:get_object).with("fql", anything, anything)
155
+ @api.fql_multiquery 'query string'
156
+ end
157
+
158
+ it "passes a queries argument" do
159
+ queries = stub('query string')
160
+ queries_json = "some JSON"
161
+ MultiJson.stub(:encode).with(queries).and_return(queries_json)
162
+
163
+ @api.should_receive(:get_object).with(anything, hash_including(:q => queries_json), anything)
164
+ @api.fql_multiquery(queries)
165
+ end
166
+
167
+ it "simplifies the response format" do
168
+ raw_results = [
169
+ {"name" => "query1", "fql_result_set" => [1, 2, 3]},
170
+ {"name" => "query2", "fql_result_set" => [:a, :b, :c]}
171
+ ]
172
+ expected_results = {
173
+ "query1" => [1, 2, 3],
174
+ "query2" => [:a, :b, :c]
175
+ }
176
+
177
+ @api.stub(:get_object).and_return(raw_results)
178
+ results = @api.fql_multiquery({:query => true})
179
+ results.should == expected_results
180
+ end
181
+
182
+ it "passes on any other arguments provided" do
183
+ args = {:a => 2}
184
+ @api.should_receive(:get_object).with(anything, hash_including(args), anything)
185
+ @api.fql_multiquery("a query", args)
186
+ end
187
+ end
131
188
  end
132
189
 
133
190
 
@@ -211,7 +268,7 @@ shared_examples_for "Koala GraphAPI with an access token" do
211
268
  end
212
269
 
213
270
 
214
- describe ".put_picture" do
271
+ describe "#put_picture" do
215
272
  it "can post photos to the user's wall with an open file object" do
216
273
  content_type = "image/jpg"
217
274
  file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
@@ -264,7 +321,7 @@ shared_examples_for "Koala GraphAPI with an access token" do
264
321
  end
265
322
  end
266
323
 
267
- describe ".put_video" do
324
+ describe "#put_video" do
268
325
  before :each do
269
326
  @cat_movie = File.join(File.dirname(__FILE__), "..", "fixtures", "cat.m4v")
270
327
  @content_type = "video/mpeg4"
@@ -274,7 +331,7 @@ shared_examples_for "Koala GraphAPI with an access token" do
274
331
  source = stub("UploadIO")
275
332
  Koala::UploadableIO.stub(:new).and_return(source)
276
333
  source.stub(:requires_base_http_service).and_return(false)
277
- Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(:video => true)).and_return(Koala::Response.new(200, "[]", {}))
334
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(:video => true)).and_return(Koala::HTTPService::Response.new(200, "[]", {}))
278
335
  @api.put_video("foo")
279
336
  end
280
337
 
@@ -340,10 +397,95 @@ shared_examples_for "Koala GraphAPI with an access token" do
340
397
  end
341
398
 
342
399
  # Page Access Token Support
343
- it "gets a page's access token" do
344
- # we can't test this live since test users (or random real users) can't be guaranteed to have pages to manage
345
- @api.should_receive(:api).with("my_page", {:fields => "access_token"}, "get", anything)
346
- @api.get_page_access_token("my_page")
400
+ describe "#get_page_access_token" do
401
+ it "gets the page object with the access_token field" do
402
+ # we can't test this live since test users (or random real users) can't be guaranteed to have pages to manage
403
+ @api.should_receive(:api).with("my_page", hash_including({:fields => "access_token"}), "get", anything)
404
+ @api.get_page_access_token("my_page")
405
+ end
406
+
407
+ it "merges in any other arguments" do
408
+ # we can't test this live since test users (or random real users) can't be guaranteed to have pages to manage
409
+ args = {:a => 3}
410
+ @api.should_receive(:api).with("my_page", hash_including(args), "get", anything)
411
+ @api.get_page_access_token("my_page", args)
412
+ end
413
+ end
414
+
415
+ describe "#set_app_restrictions" do
416
+ before :all do
417
+ oauth = Koala::Facebook::OAuth.new(KoalaTest.app_id, KoalaTest.secret)
418
+ app_token = oauth.get_app_access_token
419
+ @app_api = Koala::Facebook::API.new(app_token)
420
+ @restrictions = {"age_distr" => "13+"}
421
+ end
422
+
423
+ it "makes a POST to /app_id" do
424
+ @app_api.should_receive(:graph_call).with(KoalaTest.app_id, anything, "post", anything)
425
+ @app_api.set_app_restrictions(KoalaTest.app_id, @restrictions)
426
+ end
427
+
428
+ it "JSON-encodes the restrictions" do
429
+ @app_api.should_receive(:graph_call).with(anything, hash_including(:restrictions => MultiJson.encode(@restrictions)), anything, anything)
430
+ @app_api.set_app_restrictions(KoalaTest.app_id, @restrictions)
431
+ end
432
+
433
+ it "includes the other arguments" do
434
+ args = {:a => 2}
435
+ @app_api.should_receive(:graph_call).with(anything, hash_including(args), anything, anything)
436
+ @app_api.set_app_restrictions(KoalaTest.app_id, @restrictions, args)
437
+ end
438
+
439
+ it "works" do
440
+ @app_api.set_app_restrictions(KoalaTest.app_id, @restrictions).should be_true
441
+ end
442
+ end
443
+
444
+ it "changes tests to first_name when FB repairs its bug"
445
+
446
+ it "can access public information via FQL" do
447
+ result = @api.fql_query("select uid, first_name from user where uid = #{KoalaTest.user2_id}")
448
+ result.size.should == 1
449
+ # result.first['first_name'].should == KoalaTest.user2_name
450
+ result.first['uid'].should == KoalaTest.user2_id.to_i
451
+ end
452
+
453
+ it "can access public information via FQL.multiquery" do
454
+ result = @api.fql_multiquery(
455
+ :query1 => "select uid, first_name from user where uid = #{KoalaTest.user2_id}",
456
+ :query2 => "select uid, first_name from user where uid = #{KoalaTest.user1_id}"
457
+ )
458
+ result.size.should == 2
459
+ # this should check for first_name, but there's an FB bug currently
460
+ result["query1"].first['uid'].should == KoalaTest.user2_id.to_i
461
+ # result["query1"].first['first_name'].should == KoalaTest.user2_name
462
+ result["query2"].first['first_name'].should == KoalaTest.user1_name
463
+ end
464
+
465
+ it "can access protected information via FQL" do
466
+ # Tests agains the permissions fql table
467
+
468
+ # get the current user's ID
469
+ # we're sneakily using the Graph API, which should be okay since it has its own tests
470
+ g = Koala::Facebook::API.new(@token)
471
+ id = g.get_object("me", :fields => "id")["id"]
472
+
473
+ # now send a query about your permissions
474
+ result = @api.fql_query("select read_stream from permissions where uid = #{id}")
475
+
476
+ result.size.should == 1
477
+ # we've verified that you have read_stream permissions, so we can test against that
478
+ result.first["read_stream"].should == 1
479
+ end
480
+
481
+ it "can access protected information via FQL.multiquery" do
482
+ result = @api.fql_multiquery(
483
+ :query1 => "select post_id from stream where source_id = me()",
484
+ :query2 => "select fromid from comment where post_id in (select post_id from #query1)",
485
+ :query3 => "select uid, name from user where uid in (select fromid from #query2)"
486
+ )
487
+ result.size.should == 3
488
+ result.keys.should include("query1", "query2", "query3")
347
489
  end
348
490
 
349
491
  # test all methods to make sure they pass data through to the API
@@ -363,7 +505,11 @@ shared_examples_for "Koala GraphAPI with an access token" do
363
505
  :put_comment => 3,
364
506
  :put_like => 2, :delete_like => 2,
365
507
  :search => 3,
508
+ :set_app_restrictions => 4,
509
+ :get_page_access_token => 3,
510
+ :fql_query => 3, :fql_multiquery => 3,
366
511
  # methods that have special arguments
512
+ :get_comments_for_urls => [["url1", "url2"], {}],
367
513
  :put_picture => ["x.jpg", "image/jpg", {}, "me"],
368
514
  :put_video => ["x.mp4", "video/mpeg4", {}, "me"],
369
515
  :get_objects => [["x"], {}]
@@ -433,9 +579,8 @@ end
433
579
 
434
580
 
435
581
  shared_examples_for "Koala GraphAPI without an access token" do
436
-
437
582
  it "can't get private data about a user" do
438
- result = @api.get_object("koppel")
583
+ result = @api.get_object(KoalaTest.user1)
439
584
  # updated_time should be a pretty fixed test case
440
585
  result["updated_time"].should be_nil
441
586
  end
@@ -445,11 +590,11 @@ shared_examples_for "Koala GraphAPI without an access token" do
445
590
  end
446
591
 
447
592
  it "can't access connections from users" do
448
- lambda { @api.get_connections("lukeshepard", "friends") }.should raise_error(Koala::Facebook::APIError)
593
+ lambda { @api.get_connections(KoalaTest.user2, "friends") }.should raise_error(Koala::Facebook::APIError)
449
594
  end
450
595
 
451
596
  it "can't put an object" do
452
- lambda { @result = @api.put_object("lukeshepard", "feed", :message => "Hello, world") }.should raise_error(Koala::Facebook::APIError)
597
+ lambda { @result = @api.put_object(KoalaTest.user2, "feed", :message => "Hello, world") }.should raise_error(Koala::Facebook::APIError)
453
598
  end
454
599
 
455
600
  # these are not strictly necessary as the other put methods resolve to put_object, but are here for completeness
@@ -478,4 +623,37 @@ shared_examples_for "Koala GraphAPI without an access token" do
478
623
  it "can't delete a like" do
479
624
  lambda { @api.delete_like("7204941866_119776748033392") }.should raise_error(Koala::Facebook::APIError)
480
625
  end
626
+
627
+ # FQL_QUERY
628
+ describe "when making a FQL request" do
629
+ it "can access public information via FQL" do
630
+ result = @api.fql_query("select uid, first_name from user where uid = #{KoalaTest.user2_id}")
631
+ result.size.should == 1
632
+ result.first['first_name'].should == KoalaTest.user2_name
633
+ end
634
+
635
+ it "can access public information via FQL.multiquery" do
636
+ result = @api.fql_multiquery(
637
+ :query1 => "select uid, first_name from user where uid = #{KoalaTest.user2_id}",
638
+ :query2 => "select uid, first_name from user where uid = #{KoalaTest.user1_id}"
639
+ )
640
+ result.size.should == 2
641
+ result["query1"].first['first_name'].should == KoalaTest.user2_name
642
+ result["query2"].first['first_name'].should == KoalaTest.user1_name
643
+ end
644
+
645
+ it "can't access protected information via FQL" do
646
+ lambda { @api.fql_query("select read_stream from permissions where uid = #{KoalaTest.user2_id}") }.should raise_error(Koala::Facebook::APIError)
647
+ end
648
+
649
+ it "can't access protected information via FQL.multiquery" do
650
+ lambda {
651
+ @api.fql_multiquery(
652
+ :query1 => "select post_id from stream where source_id = me()",
653
+ :query2 => "select fromid from comment where post_id in (select post_id from #query1)",
654
+ :query3 => "select uid, name from user where uid in (select fromid from #query2)"
655
+ )
656
+ }.should raise_error(Koala::Facebook::APIError)
657
+ end
658
+ end
481
659
  end
@@ -4,6 +4,7 @@ module KoalaTest
4
4
  class << self
5
5
  attr_accessor :oauth_token, :app_id, :secret, :app_access_token, :code, :session_key
6
6
  attr_accessor :oauth_test_data, :subscription_test_data, :search_time
7
+ attr_accessor :test_user_api
7
8
  end
8
9
 
9
10
  # Test setup
@@ -28,7 +29,7 @@ module KoalaTest
28
29
  KoalaTest.setup_test_data(live_data)
29
30
 
30
31
  # allow live tests with different adapters
31
- adapter = ENV['ADAPTER'] || "typhoeus"# use Typhoeus by default if available
32
+ adapter = ENV['ADAPTER'] || "typhoeus" # use Typhoeus by default if available
32
33
  begin
33
34
  require adapter
34
35
  Faraday.default_adapter = adapter.to_sym
@@ -159,10 +160,13 @@ module KoalaTest
159
160
 
160
161
  # Data for testing
161
162
  def self.user1
163
+ # user ID, either numeric or username
162
164
  test_user? ? @live_testing_user["id"] : "koppel"
163
165
  end
164
166
 
165
167
  def self.user1_id
168
+ # numerical ID, used for FQL
169
+ # (otherwise the two IDs are interchangeable)
166
170
  test_user? ? @live_testing_user["id"] : 2905623
167
171
  end
168
172
 
@@ -171,10 +175,12 @@ module KoalaTest
171
175
  end
172
176
 
173
177
  def self.user2
178
+ # see notes for user1
174
179
  test_user? ? @live_testing_friend["id"] : "lukeshepard"
175
180
  end
176
181
 
177
182
  def self.user2_id
183
+ # see notes for user1
178
184
  test_user? ? @live_testing_friend["id"] : 2901279
179
185
  end
180
186
 
@@ -186,4 +192,7 @@ module KoalaTest
186
192
  "contextoptional"
187
193
  end
188
194
 
195
+ def self.app_properties
196
+ mock_interface? ? {"desktop" => 0} : {"description" => "A test framework for Koala and its users. (#{rand(10000).to_i})"}
197
+ end
189
198
  end
@@ -56,9 +56,9 @@ module Koala
56
56
 
57
57
  # create response class object
58
58
  response_object = if response.is_a? String
59
- Koala::Response.new(200, response, {})
59
+ Koala::HTTPService::Response.new(200, response, {})
60
60
  else
61
- Koala::Response.new(response["code"] || 200, response["body"] || "", response["headers"] || {})
61
+ Koala::HTTPService::Response.new(response["code"] || 200, response["body"] || "", response["headers"] || {})
62
62
  end
63
63
 
64
64
  rescue NoMethodError