tyler_koala 1.2.0beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.autotest +12 -0
  2. data/.gitignore +5 -0
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG +185 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE +22 -0
  7. data/Manifest +39 -0
  8. data/Rakefile +16 -0
  9. data/autotest/discover.rb +1 -0
  10. data/koala.gemspec +50 -0
  11. data/lib/koala.rb +119 -0
  12. data/lib/koala/batch_operation.rb +74 -0
  13. data/lib/koala/graph_api.rb +281 -0
  14. data/lib/koala/graph_batch_api.rb +87 -0
  15. data/lib/koala/graph_collection.rb +54 -0
  16. data/lib/koala/http_service.rb +161 -0
  17. data/lib/koala/oauth.rb +181 -0
  18. data/lib/koala/realtime_updates.rb +89 -0
  19. data/lib/koala/rest_api.rb +95 -0
  20. data/lib/koala/test_users.rb +102 -0
  21. data/lib/koala/uploadable_io.rb +180 -0
  22. data/lib/koala/utils.rb +7 -0
  23. data/readme.md +160 -0
  24. data/spec/cases/api_base_spec.rb +101 -0
  25. data/spec/cases/error_spec.rb +30 -0
  26. data/spec/cases/graph_and_rest_api_spec.rb +48 -0
  27. data/spec/cases/graph_api_batch_spec.rb +600 -0
  28. data/spec/cases/graph_api_spec.rb +42 -0
  29. data/spec/cases/http_service_spec.rb +420 -0
  30. data/spec/cases/koala_spec.rb +21 -0
  31. data/spec/cases/oauth_spec.rb +428 -0
  32. data/spec/cases/realtime_updates_spec.rb +198 -0
  33. data/spec/cases/rest_api_spec.rb +41 -0
  34. data/spec/cases/test_users_spec.rb +281 -0
  35. data/spec/cases/uploadable_io_spec.rb +206 -0
  36. data/spec/cases/utils_spec.rb +8 -0
  37. data/spec/fixtures/beach.jpg +0 -0
  38. data/spec/fixtures/cat.m4v +0 -0
  39. data/spec/fixtures/facebook_data.yml +61 -0
  40. data/spec/fixtures/mock_facebook_responses.yml +439 -0
  41. data/spec/spec_helper.rb +43 -0
  42. data/spec/support/graph_api_shared_examples.rb +502 -0
  43. data/spec/support/json_testing_fix.rb +42 -0
  44. data/spec/support/koala_test.rb +163 -0
  45. data/spec/support/mock_http_service.rb +98 -0
  46. data/spec/support/ordered_hash.rb +205 -0
  47. data/spec/support/rest_api_shared_examples.rb +285 -0
  48. data/spec/support/uploadable_io_shared_examples.rb +70 -0
  49. metadata +221 -0
@@ -0,0 +1,8 @@
1
+ describe Koala::Utils do
2
+ it "has a deprecate method" do
3
+ Koala::Utils.should respond_to(:deprecate)
4
+ end
5
+
6
+ # AFAIK there's no way to test that (Kernel.)warn receives the text
7
+ # Kernel.should_receive(:warn) doesn't seem to work, even though the text gets printed
8
+ end
Binary file
Binary file
@@ -0,0 +1,61 @@
1
+ # Testing data
2
+
3
+ # IMPORTANT NOTE: live tests will run against a test users automatically
4
+ # If you want to run them against a real user or test users on a different account, you can
5
+ # by enter an OAuth token, code, and session_key (for real users) or changing the app_id and secret (for test users)
6
+ # (note for real users: this will leave some photos and videos posted to your wall, since they can't be deleted through the API)
7
+
8
+ # These values are configured to work with the OAuth Playground app by default
9
+ # Of course, you can change this to work with your own app.
10
+ # Check out http://oauth.twoalex.com/ to easily generate tokens, cookies, etc.
11
+
12
+ # Your OAuth token should have the read_stream, publish_stream, user_photos, user_videos, and read_insights permissions.
13
+ oauth_token:
14
+
15
+ # for testing the OAuth class
16
+ # baseline app
17
+ oauth_test_data:
18
+ # the following two values are not needed for most of the test suite, but the relevant tests will not run if they're not present
19
+ code:
20
+ session_key:
21
+
22
+ # These values will work out of the box
23
+ app_id: 119908831367602
24
+ secret: e45e55a333eec232d4206d2703de1307
25
+ callback_url: http://oauth.twoalex.com/
26
+ app_access_token: 119908831367602|o3wswWQ88LYjEC9-ukR_gjRIOMw.
27
+ raw_token_string: "access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.&expires=6621"
28
+ raw_offline_access_token_string: access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.
29
+ valid_cookies:
30
+ # note: the tests stub the time class so these default cookies are always valid (if you're using the default app)
31
+ # if not you may want to remove the stubbing to test expiration
32
+ fbs_119908831367602: '"access_token=119908831367602|2.LKE7ksSPOx0V_8mHPr2NHQ__.3600.1273363200-2905623|CMpi0AYbn03Oukzv94AUha2qbO4.&expires=1273363200&secret=lT_9zm5r5IbJ6Aa5O54nFw__&session_key=2.LKE7ksSPOx0V_8mHPr2NHQ__.3600.1273363200-2905623&sig=9515e93113921f9476a4efbdd4a3c746&uid=2905623"'
33
+ expired_cookies:
34
+ fbs_119908831367602: '"access_token=119908831367602|2.xv9mi6QSOpr474s4n2X_pw__.3600.1273287600-2905623|yVt5WH_S6J5p3gFa5_5lBzckhws.&expires=1273287600&secret=V_E79ovQnXqxGctFuC_n5A__&session_key=2.xv9mi6QSOpr474s4n2X_pw__.3600.1273287600-2905623&sig=eeef60838c0c800258d89b7e6ddddddb&uid=2905623"'
35
+ offline_access_cookies:
36
+ # note: I've revoked the offline access for security reasons, so you can't make calls against this :)
37
+ fbs_119908831367602: '"access_token=119908831367602|08170230801eb225068e7a70-2905623|Q3LDCYYF8CX9cstxnZLsxiR0nwg.&expires=0&secret=78abaee300b392e275072a9f2727d436&session_key=08170230801eb225068e7a70-2905623&sig=423b8aa4b6fa1f9a571955f8e929d567&uid=2905623"'
38
+
39
+ # These values from the OAuth Playground (see above) will work out of the box
40
+ # You can update this to live data if desired
41
+ # request_secret is optional and will fall back to the secret above if absent
42
+ signed_params: "zWRm0gd5oHW_jzXP_WA9CirO7c5CLHotn-SKRqH2NmU.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzMDE5MjIwMDAsImlzc3VlZF9hdCI6MTMwMTkxNzI5OSwib2F1dGhfdG9rZW4iOiIxMTk5MDg4MzEzNjc2MDJ8Mi56VkZfNk5yTUVMSHVKYTRnSVU5dEt3X18uMzYwMC4xMzAxOTIyMDAwLTI5MDU2MjN8emdxUHNyMkJHOUxvT0s5a2VrR2dSVVJaeDBrIiwidXNlciI6eyJjb3VudHJ5IjoiZGUiLCJsb2NhbGUiOiJkZV9ERSIsImFnZSI6eyJtaW4iOjIxfX0sInVzZXJfaWQiOiIyOTA1NjIzIn0"
43
+ signed_params_result:
44
+ expires: 1301922000
45
+ algorithm: HMAC-SHA256
46
+ user_id: "2905623"
47
+ oauth_token: 119908831367602|2.zVF_6NrMELHuJa4gIU9tKw__.3600.1301922000-2905623|zgqPsr2BG9LoOK9kekGgRURZx0k
48
+ user:
49
+ country: de
50
+ locale: de_DE
51
+ age:
52
+ min: 21
53
+ issued_at: 1301917299
54
+
55
+ subscription_test_data:
56
+ subscription_path: http://oauth.twoalex.com/subscriptions
57
+ verify_token: "myverificationtoken|1f54545d5f722733e17faae15377928f"
58
+ challenge_data:
59
+ "hub.challenge": "1290024882"
60
+ "hub.verify_token": "myverificationtoken|1f54545d5f722733e17faae15377928f"
61
+ "hub.mode": "subscribe"
@@ -0,0 +1,439 @@
1
+ # Responses by MockHTTPService are taken directly from
2
+ # this file.
3
+ #
4
+ # Structure
5
+ # ----------
6
+ #
7
+ # path:
8
+ # arguments: # sorted by key
9
+ # method: # HTTP method (GET, POST, DELETE, etc.)
10
+ # with_token:
11
+ # no_token:
12
+
13
+ # ====== REST API =====
14
+ rest_api:
15
+
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
+
37
+
38
+
39
+
40
+ # ====== GRAPH API =====
41
+ graph_api:
42
+
43
+ # -- Common Responses --
44
+
45
+ # Error responses for when a token is required, but not given
46
+ token_required_responses: &token_required
47
+ no_token: '{"error":{"type":"OAuthAccessTokenException", "message":"An access token is required to request this resource."}}'
48
+
49
+ # Common mock item responses
50
+ item_deleted: &item_deleted
51
+ delete:
52
+ with_token: 'true'
53
+
54
+ # OAuth error response
55
+ oauth_error: &oauth_error
56
+ no_token: '{"error": {"type": "OAuthException", "message": "Error validating verification code."}}'
57
+
58
+ # Subscription error response
59
+ verification_error: &verification_error
60
+ with_token: '{"error": {"type": "OAuthException", "message": "Error validating verification code."}}'
61
+
62
+ test_user_uninstalled: &test_user_uninstalled
63
+ post:
64
+ with_token: '{"id": "777777777", "access_token":"<%= APP_ACCESS_TOKEN %>", "login_url":"https://www.facebook.com/platform/test_account.."}'
65
+
66
+ test_user_installed: &test_user_installed
67
+ post:
68
+ with_token: '{"id": "999999999", "access_token":"<%= ACCESS_TOKEN %>", "login_url":"https://www.facebook.com/platform/test_account.."}'
69
+
70
+ test_user_befriended: &test_user_befriended
71
+ post:
72
+ with_token: 'true'
73
+
74
+ # -- Stubbed Responses --
75
+ root:
76
+ ids=contextoptional,koppel:
77
+ get:
78
+ with_token: '{"contextoptional":"{}","koppel":"{}"}'
79
+ no_token: '{"contextoptional":"{}","koppel":"{}"}'
80
+
81
+ # Ruby 1.8.7 and 1.9.2 generate JSON with different key ordering, hence we have to dynamically generate it here
82
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "koppel"}]) %>:
83
+ post:
84
+ with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"id\":\"456\"}"}]'
85
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/picture"}]) %>:
86
+ post:
87
+ with_token: '[{"headers":[{"name":"Location","value":"http://google.com"}]}]'
88
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "me/friends"}]) %>:
89
+ post:
90
+ with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[]}"}]'
91
+ batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"me"}, {"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}]) %>:
92
+ post:
93
+ with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[]}"}]'
94
+ batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights"}, {"method"=>"get", "relative_url"=>"koppel?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}]) %>:
95
+ post:
96
+ with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},{"body":"{\"id\":\"123\"}"}]'
97
+ batch=<%= MultiJson.encode([{"method"=>"post", "relative_url"=>"FEED_ITEM_BATCH/likes"}, {"method"=>"delete", "relative_url"=> "FEED_ITEM_BATCH"}]) %>:
98
+ post:
99
+ with_token: '[{"body": "{\"id\": \"MOCK_LIKE\"}"},{"body":true}]'
100
+ batch=<%= MultiJson.encode([{"method" => "post", "relative_url" => "method/fql.query", "body" => "query=select+first_name+from+user+where+uid%3D2905623"}]) %>:
101
+ post:
102
+ with_token: '[{"body":"[{\"first_name\":\"Alex\"}]"}]'
103
+
104
+ # dependencies
105
+ batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"me", "name" => "getme"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getme"}]) %>:
106
+ post:
107
+ with_token: '[null,{"body":"{\"id\":\"123\"}"}]'
108
+ batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights", "name" => "getdata"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getdata"}]) %>:
109
+ post:
110
+ with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},null]'
111
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/friends?limit=5", "name" => "get-friends"}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=get-friends:$.data.*.id}"}"}]) %>:
112
+ post:
113
+ with_token: '[null,{"body":"{}"}]'
114
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/friends?limit=5", "name" => "get-friends", "omit_response_on_success" => false}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=get-friends:$.data.*.id}"}"}]) %>:
115
+ post:
116
+ with_token: '[{"body":"{\"data\":[]}"},{"body":"{}"}]'
117
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/friends?limit=5"}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=i-dont-exist:$.data.*.id}"}"}]) %>:
118
+ post:
119
+ with_token: '{"error":190,"error_description":"Error validating access token."}'
120
+
121
+ # attached files tests
122
+ batch=<%= MultiJson.encode([{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "op1_file0"}]) %>&op1_file0=[FILE]:
123
+ post:
124
+ with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},null]'
125
+ batch=<%= MultiJson.encode([{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "op1_file0"}]) %>&op1_file0=[FILE]:
126
+ post:
127
+ with_token: '[{"body":"{\"id\": \"MOCK_PHOTO\"}"}]'
128
+ batch=<%= MultiJson.encode([{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "op1_file0"}, {"method"=>"post", "relative_url"=>"koppel/photos", "attached_files" => "op2_file0"}]) %>&op1_file0=[FILE]&op2_file0=[FILE]:
129
+ post:
130
+ with_token: '[{"body":"{\"id\": \"MOCK_PHOTO\"}"}, {"body":"{\"id\": \"MOCK_PHOTO\"}"}]'
131
+
132
+
133
+
134
+
135
+ /me:
136
+ no_args:
137
+ get:
138
+ <<: *token_required
139
+ with_token: '{"updated_time": 1}'
140
+ fields=id:
141
+ get:
142
+ with_token: '{"id": "2901279"}'
143
+
144
+ /me/feed:
145
+ message=Hello, world, from the test suite!:
146
+ post:
147
+ with_token: '{"id": "MOCK_FEED_ITEM"}'
148
+ message=Hello, world, from the test suite, testing comments!:
149
+ post:
150
+ with_token: '{"id": "MOCK_FEED_ITEM"}'
151
+ message=the cats are asleep:
152
+ post:
153
+ with_token: '{"id": "FEED_ITEM_CATS"}'
154
+ message=Hello, world, from the test suite delete method!:
155
+ post:
156
+ with_token: '{"id": "FEED_ITEM_DELETE"}'
157
+ message=Hello, world, from the test suite batch API!:
158
+ post:
159
+ with_token: '{"id": "FEED_ITEM_BATCH"}'
160
+ link=http://oauth.twoalex.com/&message=Hello, world, from the test suite again!&name=OAuth Playground:
161
+ post:
162
+ with_token: '{"id": "FEED_ITEM_CONTEXT"}'
163
+
164
+ /me/photos:
165
+ source=[FILE]:
166
+ post:
167
+ <<: *token_required
168
+ with_token: '{"id": "MOCK_PHOTO"}'
169
+ message=This is the test message&source=[FILE]:
170
+ post:
171
+ <<: *token_required
172
+ with_token: '{"id": "MOCK_PHOTO"}'
173
+ url=http://img.slate.com/images/redesign2008/slate_logo.gif:
174
+ post:
175
+ <<: *token_required
176
+ with_token: '{"id": "MOCK_PHOTO_FROM_URL"}'
177
+ message=my message&url=http://img.slate.com/images/redesign2008/slate_logo.gif:
178
+ post:
179
+ <<: *token_required
180
+ with_token: '{"id": "MOCK_PHOTO_FROM_URL"}'
181
+
182
+ /me/videos:
183
+ source=[FILE]:
184
+ post:
185
+ <<: *token_required
186
+ with_token: '{"id": "MOCK_PHOTO"}'
187
+ message=This is the test message&source=[FILE]:
188
+ post:
189
+ <<: *token_required
190
+ with_token: '{"id": "MOCK_PHOTO"}'
191
+
192
+ /koppel:
193
+ no_args:
194
+ get:
195
+ with_token: '{"id": 1, "name": 1, "updated_time": 1}'
196
+ no_token: '{"id": 1, "name": 1}'
197
+
198
+ /contextoptional:
199
+ no_args:
200
+ get:
201
+ with_token: '{"id": 1, "name": 1}'
202
+ no_token: '{"id": 1, "name": 1}'
203
+
204
+ /contextoptional/photos:
205
+ no_args:
206
+ get:
207
+ with_token: '{"data": [{}], "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"}}'
208
+ no_token: '{"data": [{}], "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"}}'
209
+
210
+ /lukeshepard/friends:
211
+ no_args:
212
+ get:
213
+ <<: *token_required
214
+ with_token: '{"data": [{}]}'
215
+
216
+ /lukeshepard/picture:
217
+ type=large:
218
+ get:
219
+ no_token:
220
+ code: 302
221
+ headers:
222
+ Location: https://facebook.com/large
223
+ with_token:
224
+ code: 302
225
+ headers:
226
+ Location: https://facebook.com/large
227
+
228
+
229
+ /chris.baclig/picture:
230
+ no_args:
231
+ get:
232
+ no_token:
233
+ code: 302
234
+ headers:
235
+ Location: http://facebook.com/
236
+ with_token:
237
+ code: 302
238
+ headers:
239
+ Location: http://facebook.com/
240
+
241
+ /comments:
242
+ ids=http://developers.facebook.com/blog/post/472:
243
+ get:
244
+ with_token: '{"http://developers.facebook.com/blog/post/472": []}'
245
+ no_token: '{"http://developers.facebook.com/blog/post/472": []}'
246
+ ids=http://developers.facebook.com/blog/post/490,http://developers.facebook.com/blog/post/472:
247
+ get:
248
+ with_token: '{"http://developers.facebook.com/blog/post/490": [], "http://developers.facebook.com/blog/post/472": []}'
249
+ no_token: '{"http://developers.facebook.com/blog/post/490": [], "http://developers.facebook.com/blog/post/472": []}'
250
+ ids=:
251
+ get:
252
+ body: '{"error": {"type": "OAuthException","message": "Cannot specify an empty identifier"}}'
253
+ code: 400
254
+ no_args:
255
+ get:
256
+ body: '{"error": {"type": "Exception","message": "No node specified"}}'
257
+ code: 500
258
+
259
+ /search:
260
+ q=facebook:
261
+ get:
262
+ 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"}}'
263
+ 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"}}'
264
+ "limit=25&q=facebook&until=2010-09-23T21:17:33+0000":
265
+ get:
266
+ 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"}}'
267
+ 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"}}'
268
+
269
+ '/115349521819193_113815981982767':
270
+ no_args:
271
+ delete:
272
+ <<: *token_required
273
+
274
+ /my_page:
275
+ fields=access_token:
276
+ get:
277
+ <<: *token_required
278
+ with_token: '{"access_token": "<%= APP_ACCESS_TOKEN %>"}'
279
+
280
+ # -- OAuth responses --
281
+ /oauth/access_token:
282
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&code=<%= OAUTH_CODE %>&redirect_uri=<%= OAUTH_DATA["callback_url"] %>:
283
+ get:
284
+ no_token: access_token=<%= ACCESS_TOKEN %>
285
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&code=foo&redirect_uri=<%= OAUTH_DATA["callback_url"] %>:
286
+ get:
287
+ <<: *oauth_error
288
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&type=client_cred:
289
+ post:
290
+ no_token: access_token=<%= APP_ACCESS_TOKEN %>
291
+ /oauth/exchange_sessions:
292
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&sessions=<%= OAUTH_DATA["session_key"] %>&type=client_cred:
293
+ post:
294
+ no_token: '[{"access_token":"<%= ACCESS_TOKEN %>","expires":4315}]'
295
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&sessions=<%= [OAUTH_DATA["session_key"], OAUTH_DATA["session_key"]].join(",") %>&type=client_cred:
296
+ post:
297
+ no_token: '[{"access_token":"<%= ACCESS_TOKEN %>","expires":4315}, {"access_token":"<%= ACCESS_TOKEN %>","expires":4315}]'
298
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&sessions=<%= ["foo"].concat([OAUTH_DATA["session_key"], OAUTH_DATA["session_key"]]).join(",") %>&type=client_cred:
299
+ post:
300
+ no_token: '[null, {"access_token":"<%= ACCESS_TOKEN %>","expires":4315}, {"access_token":"<%= ACCESS_TOKEN %>","expires":4315}]'
301
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&sessions=foo,bar&type=client_cred:
302
+ post:
303
+ no_token: '[null, null]'
304
+ client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&sessions=foo&type=client_cred:
305
+ post:
306
+ no_token: '[null]'
307
+
308
+
309
+
310
+ # -- Subscription Responses --
311
+ /<%= APP_ID %>/subscriptions:
312
+ callback_url=<%= SUBSCRIPTION_DATA["subscription_path"] %>&fields=name&object=user&verify_token=<%= SUBSCRIPTION_DATA["verify_token"] %>:
313
+ post:
314
+ with_token:
315
+ code: 200
316
+ callback_url=<%= SUBSCRIPTION_DATA["subscription_path"] %>foo&fields=name&object=user&verify_token=<%= SUBSCRIPTION_DATA["verify_token"] %>:
317
+ post:
318
+ with_token: '{"error":{"type":"Exception","message":"(#2200) subscription validation failed"}}'
319
+ callback_url=foo&fields=name&object=user&verify_token=<%= SUBSCRIPTION_DATA["verify_token"] %>:
320
+ post:
321
+ with_token: '{"error":{"type":"Exception","message":"(#100) callback_url URL is not properly formatted"}}'
322
+ object=user:
323
+ delete:
324
+ with_token:
325
+ code: 200
326
+ object=kittens:
327
+ delete:
328
+ with_token: '{"error":{"type":"Exception","message":"(#100) Invalid parameter"}}'
329
+ no_args:
330
+ delete:
331
+ with_token:
332
+ code: 200
333
+ get:
334
+ with_token: '{"data":[{"callback_url":"http://oauth.twoalex.com/subscriptions", "fields":["name"], "object":"user", "active":true}]}'
335
+
336
+
337
+ callback_url=<%= SUBSCRIPTION_DATA["subscription_path"] %>:
338
+ get:
339
+ with_token: '{"data":[{"callback_url":"http://oauth.twoalex.com/subscriptions", "fields":["name"], "object":"user", "active":true}]}'
340
+
341
+ # -- Mock Item Responses --
342
+
343
+ /MOCK_FEED_ITEM/likes:
344
+ no_args:
345
+ post:
346
+ with_token: '{"id": "MOCK_LIKE"}'
347
+
348
+ /MOCK_FEED_ITEM/comments:
349
+ message=it's my comment!:
350
+ post:
351
+ with_token: '{"id": "MOCK_COMMENT"}'
352
+
353
+ /MOCK_FEED_ITEM:
354
+ no_args:
355
+ <<: *item_deleted
356
+
357
+ /FEED_ITEM_CONTEXT:
358
+ no_args:
359
+ <<: *item_deleted
360
+ get:
361
+ with_token: '{"link":"http://oauth.twoalex.com/", "name": "OAuth Playground"}'
362
+
363
+ /FEED_ITEM_CATS:
364
+ no_args:
365
+ <<: *item_deleted
366
+ get:
367
+ with_token: '{"message": "the cats are asleep"}'
368
+
369
+ /FEED_ITEM_DELETE:
370
+ no_args:
371
+ <<: *item_deleted
372
+
373
+ /FEED_ITEM_DELETE/likes:
374
+ no_args:
375
+ <<: *item_deleted
376
+ post:
377
+ with_token: 'true'
378
+
379
+ /MOCK_COMMENT:
380
+ no_args:
381
+ <<: *item_deleted
382
+ get:
383
+ with_token: "{\"message\": \"it's my comment!\"}"
384
+ /MOCK_PHOTO:
385
+ no_args:
386
+ <<: *item_deleted
387
+ get:
388
+ with_token: "{\"name\": \"This is the test message\"}"
389
+
390
+ # -- Mock Test User Responses --
391
+ /<%= APP_ID %>/accounts/test-users:
392
+ installed=false:
393
+ <<: *test_user_uninstalled
394
+ installed=false&permissions=read_stream:
395
+ <<: *test_user_uninstalled
396
+ installed=true&permissions=:
397
+ <<: *test_user_installed
398
+ installed=true&permissions=read_stream:
399
+ <<: *test_user_installed
400
+ installed=true&permissions=read_stream,user_interests:
401
+ post:
402
+ with_token: '{"id": "888888888", "access_token":"<%= ACCESS_TOKEN %>", "login_url":"https://www.facebook.com/platform/test_account.."}'
403
+ no_args:
404
+ get:
405
+ 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.."}]}'
406
+
407
+ /999999999:
408
+ no_args:
409
+ <<: *item_deleted
410
+ get:
411
+ with_token: '{"name":"Foo Baz"}'
412
+ name=Foo Baz:
413
+ post:
414
+ with_token: 'true'
415
+
416
+
417
+ /999999999/friends/888888888:
418
+ no_args:
419
+ post:
420
+ with_token: 'true'
421
+
422
+
423
+ /9999999991:
424
+ no_args:
425
+ delete:
426
+ with_token: '{"error": {"type": "OAuthException", "message": "Error validating verification code."}}'
427
+
428
+ /888888888:
429
+ no_args:
430
+ <<: *item_deleted
431
+
432
+ /888888888/friends/999999999:
433
+ no_args:
434
+ <<: *test_user_befriended
435
+
436
+
437
+ /777777777:
438
+ no_args:
439
+ <<: *item_deleted