tyler_koala 1.2.0beta

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 (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