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,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala::Facebook::API" do
4
+ before(:each) do
5
+ @service = Koala::Facebook::API.new
6
+ end
7
+
8
+ it "should not include an access token if none was given" do
9
+ Koala.should_receive(:make_request).with(
10
+ anything,
11
+ hash_not_including('access_token' => 1),
12
+ anything,
13
+ anything
14
+ ).and_return(Koala::Response.new(200, "", ""))
15
+
16
+ @service.api('anything')
17
+ end
18
+
19
+ it "should include an access token if given" do
20
+ token = 'adfadf'
21
+ service = Koala::Facebook::API.new token
22
+
23
+ Koala.should_receive(:make_request).with(
24
+ anything,
25
+ hash_including('access_token' => token),
26
+ anything,
27
+ anything
28
+ ).and_return(Koala::Response.new(200, "", ""))
29
+
30
+ service.api('anything')
31
+ end
32
+
33
+ it "should have an attr_reader for access token" do
34
+ token = 'adfadf'
35
+ service = Koala::Facebook::API.new token
36
+ service.access_token.should == token
37
+ end
38
+
39
+ it "should get the attribute of a Koala::Response given by the http_component parameter" do
40
+ http_component = :method_name
41
+
42
+ response = mock('Mock KoalaResponse', :body => '', :status => 200)
43
+ response.should_receive(http_component).and_return('')
44
+
45
+ Koala.stub(:make_request).and_return(response)
46
+
47
+ @service.api('anything', {}, 'get', :http_component => http_component)
48
+ end
49
+
50
+ it "should return the body of the request as JSON if no http_component is given" do
51
+ response = stub('response', :body => 'body', :status => 200)
52
+ Koala.stub(:make_request).and_return(response)
53
+
54
+ json_body = mock('JSON body')
55
+ MultiJson.stub(:decode).and_return([json_body])
56
+
57
+ @service.api('anything').should == json_body
58
+ end
59
+
60
+ it "should execute an error checking block if provided" do
61
+ body = '{}'
62
+ Koala.stub(:make_request).and_return(Koala::Response.new(200, body, {}))
63
+
64
+ yield_test = mock('Yield Tester')
65
+ yield_test.should_receive(:pass)
66
+
67
+ @service.api('anything', {}, "get") do |arg|
68
+ yield_test.pass
69
+ arg.should == MultiJson.decode(body)
70
+ end
71
+ end
72
+
73
+ it "should raise an API error if the HTTP response code is greater than or equal to 500" do
74
+ Koala.stub(:make_request).and_return(Koala::Response.new(500, 'response body', {}))
75
+
76
+ lambda { @service.api('anything') }.should raise_exception(Koala::Facebook::APIError)
77
+ end
78
+
79
+ it "should handle rogue true/false as responses" do
80
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'true', {}))
81
+ @service.api('anything').should be_true
82
+
83
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'false', {}))
84
+ @service.api('anything').should be_false
85
+ end
86
+
87
+ describe "with regard to leading slashes" do
88
+ it "should add a leading / to the path if not present" do
89
+ path = "anything"
90
+ Koala.should_receive(:make_request).with("/#{path}", anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
91
+ @service.api(path)
92
+ end
93
+
94
+ it "shouldn't change the path if a leading / is present" do
95
+ path = "/anything"
96
+ Koala.should_receive(:make_request).with(path, anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
97
+ @service.api(path)
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,30 @@
1
+ describe Koala::Facebook::APIError do
2
+ it "is a StandardError" do
3
+ Koala::Facebook::APIError.new.should be_a(StandardError)
4
+ end
5
+
6
+ it "has an accessor for fb_error_type" do
7
+ Koala::Facebook::APIError.instance_methods.map(&:to_sym).should include(:fb_error_type)
8
+ Koala::Facebook::APIError.instance_methods.map(&:to_sym).should include(:fb_error_type=)
9
+ end
10
+
11
+ it "sets fb_error_type to details['type']" do
12
+ type = "foo"
13
+ Koala::Facebook::APIError.new("type" => type).fb_error_type.should == type
14
+ end
15
+
16
+ it "sets the error message details['type']: details['message']" do
17
+ type = "foo"
18
+ message = "bar"
19
+ error = Koala::Facebook::APIError.new("type" => type, "message" => message)
20
+ error.message.should =~ /#{type}/
21
+ error.message.should =~ /#{message}/
22
+ end
23
+ end
24
+
25
+ describe Koala::KoalaError do
26
+ it "is a StandardError" do
27
+ Koala::KoalaError.new.should be_a(StandardError)
28
+ end
29
+ end
30
+
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala::Facebook::GraphAndRestAPI" do
4
+ describe "class consolidation" do
5
+ before :each do
6
+ Koala::Utils.stub(:deprecate) # avoid actual messages to stderr
7
+ end
8
+
9
+ it "still allows you to instantiate a GraphAndRestAPI object" do
10
+ api = Koala::Facebook::GraphAndRestAPI.new("token").should be_a(Koala::Facebook::GraphAndRestAPI)
11
+ end
12
+
13
+ it "ultimately creates an API object" do
14
+ api = Koala::Facebook::GraphAndRestAPI.new("token").should be_a(Koala::Facebook::API)
15
+ end
16
+
17
+ it "fires a depreciation warning" do
18
+ Koala::Utils.should_receive(:deprecate)
19
+ api = Koala::Facebook::GraphAndRestAPI.new("token")
20
+ end
21
+ end
22
+
23
+ describe "with an access token" do
24
+ before(:each) do
25
+ @api = Koala::Facebook::API.new(@token)
26
+ end
27
+
28
+ it_should_behave_like "Koala RestAPI"
29
+ it_should_behave_like "Koala RestAPI with an access token"
30
+
31
+ it_should_behave_like "Koala GraphAPI"
32
+ it_should_behave_like "Koala GraphAPI with an access token"
33
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
34
+ end
35
+
36
+ describe "without an access token" do
37
+ before(:each) do
38
+ @api = Koala::Facebook::API.new
39
+ end
40
+
41
+ it_should_behave_like "Koala RestAPI"
42
+ it_should_behave_like "Koala RestAPI without an access token"
43
+
44
+ it_should_behave_like "Koala GraphAPI"
45
+ it_should_behave_like "Koala GraphAPI without an access token"
46
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
47
+ end
48
+ end
@@ -0,0 +1,600 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala::Facebook::GraphAPI in batch mode" do
4
+
5
+ before :each do
6
+ @api = Koala::Facebook::API.new(@token)
7
+ # app API
8
+ @app_id = KoalaTest.app_id
9
+ @app_access_token = KoalaTest.app_access_token
10
+ @app_api = Koala::Facebook::API.new(@app_access_token)
11
+ end
12
+
13
+ describe "BatchOperations" do
14
+ before :each do
15
+ @args = {
16
+ :url => "my url",
17
+ :args => {:a => 2, :b => 3},
18
+ :method => "get",
19
+ :access_token => "12345",
20
+ :http_options => {},
21
+ :post_processing => lambda { }
22
+ }
23
+ end
24
+
25
+ describe "#new" do
26
+ it "makes http_options accessible" do
27
+ Koala::Facebook::BatchOperation.new(@args).http_options.should == @args[:http_options]
28
+ end
29
+
30
+ it "makes post_processing accessible" do
31
+ Koala::Facebook::BatchOperation.new(@args).post_processing.should == @args[:post_processing]
32
+ end
33
+
34
+ it "makes access_token accessible" do
35
+ Koala::Facebook::BatchOperation.new(@args).access_token.should == @args[:access_token]
36
+ end
37
+
38
+ it "doesn't change the original http_options" do
39
+ @args[:http_options][:name] = "baz2"
40
+ expected = @args[:http_options].dup
41
+ Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)
42
+ @args[:http_options].should == expected
43
+ end
44
+
45
+ it "leaves the file array nil by default" do
46
+ Koala::Facebook::BatchOperation.new(@args).files.should be_nil
47
+ end
48
+
49
+ it "raises a KoalaError if no access token supplied" do
50
+ expect { Koala::Facebook::BatchOperation.new(@args.merge(:access_token => nil)) }.to raise_exception(Koala::KoalaError)
51
+ end
52
+
53
+ describe "when supplied binary files" do
54
+ before :each do
55
+ @binary = stub("Binary file")
56
+ @uploadable_io = stub("UploadableIO 1")
57
+
58
+ @batch_queue = []
59
+ Koala::Facebook::GraphAPI.stub(:batch_calls).and_return(@batch_queue)
60
+
61
+ Koala::UploadableIO.stub(:new).with(@binary).and_return(@uploadable_io)
62
+ Koala::UploadableIO.stub(:binary_content?).and_return(false)
63
+ Koala::UploadableIO.stub(:binary_content?).with(@binary).and_return(true)
64
+ Koala::UploadableIO.stub(:binary_content?).with(@uploadable_io).and_return(true)
65
+ @uploadable_io.stub(:is_a?).with(Koala::UploadableIO).and_return(true)
66
+
67
+ @args[:method] = "post" # files are always post
68
+ end
69
+
70
+ it "adds binary files to the files attribute as UploadableIOs" do
71
+ @args[:args].merge!("source" => @binary)
72
+ batch_op = Koala::Facebook::BatchOperation.new(@args)
73
+ batch_op.files.should_not be_nil
74
+ batch_op.files.find {|k, v| v == @uploadable_io}.should_not be_nil
75
+ end
76
+
77
+ it "works if supplied an UploadableIO as an argument" do
78
+ # as happens with put_picture at the moment
79
+ @args[:args].merge!("source" => @uploadable_io)
80
+ batch_op = Koala::Facebook::BatchOperation.new(@args)
81
+ batch_op.files.should_not be_nil
82
+ batch_op.files.find {|k, v| v == @uploadable_io}.should_not be_nil
83
+ end
84
+
85
+ it "assigns each binary parameter unique name" do
86
+ @args[:args].merge!("source" => @binary, "source2" => @binary)
87
+ batch_op = Koala::Facebook::BatchOperation.new(@args)
88
+ # if the name wasn't unique, there'd just be one item
89
+ batch_op.files.should have(2).items
90
+ end
91
+
92
+ it "assigns each binary parameter unique name across batch requests" do
93
+ @args[:args].merge!("source" => @binary, "source2" => @binary)
94
+ batch_op = Koala::Facebook::BatchOperation.new(@args)
95
+ # simulate the batch operation, since it's used in determination
96
+ @batch_queue << batch_op
97
+ batch_op2 = Koala::Facebook::BatchOperation.new(@args)
98
+ @batch_queue << batch_op2
99
+ # if the name wasn't unique, we should have < 4 items since keys would be the same
100
+ batch_op.files.merge(batch_op2.files).should have(4).items
101
+ end
102
+
103
+ it "removes the value from the arguments" do
104
+ @args[:args].merge!("source" => @binary)
105
+ Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:body].should_not =~ /source=/
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ describe ".to_batch_params" do
112
+ describe "handling arguments and URLs" do
113
+ shared_examples_for "request with no body" do
114
+ it "adds the args to the URL string, with ? if no args previously present" do
115
+ test_args = "foo"
116
+ @args[:url] = url = "/"
117
+ Koala.http_service.stub(:encode_params).and_return(test_args)
118
+
119
+ Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == "#{url}?#{test_args}"
120
+ end
121
+
122
+ it "adds the args to the URL string, with & if args previously present" do
123
+ test_args = "foo"
124
+ @args[:url] = url = "/?a=2"
125
+ Koala.http_service.stub(:encode_params).and_return(test_args)
126
+
127
+ Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == "#{url}&#{test_args}"
128
+ end
129
+
130
+ it "adds nothing to the URL string if there are no args to be added" do
131
+ @args[:args] = {}
132
+ Koala::Facebook::BatchOperation.new(@args).to_batch_params(@args[:access_token])[:relative_url].should == @args[:url]
133
+ end
134
+
135
+ it "adds nothing to the body" do
136
+ Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:body].should be_nil
137
+ end
138
+ end
139
+
140
+ shared_examples_for "requests with a body param" do
141
+ it "sets the body to the encoded args string, if there are args" do
142
+ test_args = "foo"
143
+ Koala.http_service.stub(:encode_params).and_return(test_args)
144
+
145
+ Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:body].should == test_args
146
+ end
147
+
148
+ it "does not set the body if there are no args" do
149
+ test_args = ""
150
+ Koala.http_service.stub(:encode_params).and_return(test_args)
151
+ Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:body].should be_nil
152
+ end
153
+
154
+
155
+ it "doesn't change the url" do
156
+ test_args = "foo"
157
+ Koala.http_service.stub(:encode_params).and_return(test_args)
158
+
159
+ Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == @args[:url]
160
+ end
161
+ end
162
+
163
+ context "for get operations" do
164
+ before :each do
165
+ @args[:method] = :get
166
+ end
167
+
168
+ it_should_behave_like "request with no body"
169
+ end
170
+
171
+ context "for delete operations" do
172
+ before :each do
173
+ @args[:method] = :delete
174
+ end
175
+
176
+ it_should_behave_like "request with no body"
177
+ end
178
+
179
+ context "for get operations" do
180
+ before :each do
181
+ @args[:method] = :put
182
+ end
183
+
184
+ it_should_behave_like "requests with a body param"
185
+ end
186
+
187
+ context "for delete operations" do
188
+ before :each do
189
+ @args[:method] = :post
190
+ end
191
+
192
+ it_should_behave_like "requests with a body param"
193
+ end
194
+ end
195
+
196
+ it "includes the access token if the token is not the main one for the request" do
197
+ params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)
198
+ params[:relative_url].should =~ /access_token=#{@args[:access_token]}/
199
+ end
200
+
201
+ it "includes the other arguments if the token is not the main one for the request" do
202
+ @args[:args] = {:a => 2}
203
+ params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)
204
+ params[:relative_url].should =~ /a=2/
205
+ end
206
+
207
+ it "does not include the access token if the token is the main one for the request" do
208
+ params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(@args[:access_token])
209
+ params[:relative_url].should_not =~ /access_token=#{@args[:access_token]}/
210
+ end
211
+
212
+ it "includes the other arguments if the token is the main one for the request" do
213
+ @args[:args] = {:a => 2}
214
+ params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(@args[:access_token])
215
+ params[:relative_url].should =~ /a=2/
216
+ end
217
+
218
+ it "includes any arguments passed as http_options[:batch_args]" do
219
+ batch_args = {:name => "baz", :headers => {:some_param => true}}
220
+ @args[:http_options][:batch_args] = batch_args
221
+ params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(nil)
222
+ params.should include(batch_args)
223
+ end
224
+
225
+ it "includes the method" do
226
+ params = Koala::Facebook::BatchOperation.new(@args).to_batch_params(@args[:access_token])
227
+ params[:method].should == @args[:method].to_s
228
+ end
229
+
230
+ it "works with nil http_options" do
231
+ expect { Koala::Facebook::BatchOperation.new(@args.merge(:http_options => nil)).to_batch_params(nil) }.not_to raise_exception
232
+ end
233
+
234
+ it "works with nil args" do
235
+ expect { Koala::Facebook::BatchOperation.new(@args.merge(:args => nil)).to_batch_params(nil) }.not_to raise_exception
236
+ end
237
+
238
+ describe "with binary files" do
239
+ before :each do
240
+ @binary = stub("Binary file")
241
+ Koala::UploadableIO.stub(:binary_content?).and_return(false)
242
+ Koala::UploadableIO.stub(:binary_content?).with(@binary).and_return(true)
243
+ @uploadable_io = stub("UploadableIO")
244
+ Koala::UploadableIO.stub(:new).with(@binary).and_return(@uploadable_io)
245
+ @uploadable_io.stub(:is_a?).with(Koala::UploadableIO).and_return(true)
246
+
247
+ @batch_queue = []
248
+ Koala::Facebook::GraphAPI.stub(:batch_calls).and_return(@batch_queue)
249
+
250
+ @args[:method] = "post" # files are always post
251
+ end
252
+
253
+ it "adds file identifiers as attached_files in a comma-separated list" do
254
+ @args[:args].merge!("source" => @binary, "source2" => @binary)
255
+ batch_op = Koala::Facebook::BatchOperation.new(@args)
256
+ file_ids = batch_op.files.find_all {|k, v| v == @uploadable_io}.map {|k, v| k}
257
+ params = batch_op.to_batch_params(nil)
258
+ params[:attached_files].should == file_ids.join(",")
259
+ end
260
+ end
261
+ end
262
+
263
+ end
264
+
265
+ describe "GraphAPI batch interface" do
266
+
267
+ it "returns nothing for a batch operation" do
268
+ Koala.stub(:make_request).and_return(Koala::Response.new(200, "[]", {}))
269
+ @api.batch do |batch_api|
270
+ batch_api.get_object('me').should be_nil
271
+ end
272
+ end
273
+
274
+ describe "#batch" do
275
+ before :each do
276
+ @fake_response = Koala::Response.new(200, "[]", {})
277
+ Koala.stub(:make_request).and_return(@fake_response)
278
+ end
279
+
280
+ describe "making the request" do
281
+ context "with no calls" do
282
+ it "does not make any requests if batch_calls is empty" do
283
+ Koala.should_not_receive(:make_request)
284
+ @api.batch {|batch_api|}
285
+ end
286
+
287
+ it "returns []" do
288
+ @api.batch {|batch_api|}.should == []
289
+ end
290
+ end
291
+
292
+ it "includes the first operation's access token as the main one in the args" do
293
+ access_token = "foo"
294
+ Koala.should_receive(:make_request).with(anything, hash_including("access_token" => access_token), anything, anything).and_return(@fake_response)
295
+ Koala::Facebook::API.new(access_token).batch do |batch_api|
296
+ batch_api.get_object('me')
297
+ batch_api.get_object('me', {}, {'access_token' => 'bar'})
298
+ end
299
+ end
300
+
301
+ it "sets args['batch'] to a json'd map of all the batch params" do
302
+ access_token = "bar"
303
+ op = Koala::Facebook::BatchOperation.new(:access_token => access_token, :method => :get, :url => "/")
304
+ op.stub(:to_batch_params).and_return({:a => 2})
305
+ Koala::Facebook::BatchOperation.stub(:new).and_return(op)
306
+
307
+ # two requests should generate two batch operations
308
+ expected = MultiJson.encode([op.to_batch_params(access_token), op.to_batch_params(access_token)])
309
+ Koala.should_receive(:make_request).with(anything, hash_including("batch" => expected), anything, anything).and_return(@fake_response)
310
+ Koala::Facebook::API.new(access_token).batch do |batch_api|
311
+ batch_api.get_object('me')
312
+ batch_api.get_object('me')
313
+ end
314
+ end
315
+
316
+ it "adds any files from the batch operations to the arguments" do
317
+ # stub the batch operation
318
+ # we test above to ensure that files are properly assimilated into the BatchOperation instance
319
+ # right now, we want to make sure that batch_api handles them properly
320
+ @key = "file0_0"
321
+ @uploadable_io = stub("UploadableIO")
322
+ batch_op = stub("Koala Batch Operation", :files => {@key => @uploadable_io}, :to_batch_params => {}, :access_token => "foo")
323
+ Koala::Facebook::BatchOperation.stub(:new).and_return(batch_op)
324
+
325
+ Koala.should_receive(:make_request).with(anything, hash_including(@key => @uploadable_io), anything, anything).and_return(@fake_response)
326
+ Koala::Facebook::API.new("bar").batch do |batch_api|
327
+ batch_api.put_picture("path/to/file", "image/jpeg")
328
+ end
329
+ end
330
+
331
+ it "preserves operation order" do
332
+ access_token = "bar"
333
+ # two requests should generate two batch operations
334
+ Koala.should_receive(:make_request) do |url, args, method, options|
335
+ # test the batch operations to make sure they appear in the right order
336
+ (args ||= {})["batch"].should =~ /.*me\/farglebarg.*otheruser\/bababa/
337
+ @fake_response
338
+ end
339
+ Koala::Facebook::API.new(access_token).batch do |batch_api|
340
+ batch_api.get_connections('me', "farglebarg")
341
+ batch_api.get_connections('otheruser', "bababa")
342
+ end
343
+ end
344
+
345
+ it "makes a POST request" do
346
+ Koala.should_receive(:make_request).with(anything, anything, "post", anything).and_return(@fake_response)
347
+ Koala::Facebook::API.new("foo").batch do |batch_api|
348
+ batch_api.get_object('me')
349
+ end
350
+ end
351
+
352
+ it "makes a request to /" do
353
+ Koala.should_receive(:make_request).with("/", anything, anything, anything).and_return(@fake_response)
354
+ Koala::Facebook::API.new("foo").batch do |batch_api|
355
+ batch_api.get_object('me')
356
+ end
357
+ end
358
+
359
+ it "includes any http options specified at the top level" do
360
+ http_options = {"a" => "baz"}
361
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(http_options)).and_return(@fake_response)
362
+ Koala::Facebook::API.new("foo").batch(http_options) do |batch_api|
363
+ batch_api.get_object('me')
364
+ end
365
+ end
366
+ end
367
+
368
+ describe "processing the request" do
369
+ it "throws an error if the response is not 200" do
370
+ Koala.stub(:make_request).and_return(Koala::Response.new(500, "[]", {}))
371
+ expect { Koala::Facebook::API.new("foo").batch do |batch_api|
372
+ batch_api.get_object('me')
373
+ end }.to raise_exception(Koala::Facebook::APIError)
374
+ end
375
+
376
+ it "throws an error if the response is a Batch API-style error" do
377
+ Koala.stub(:make_request).and_return(Koala::Response.new(200, '{"error":190,"error_description":"Error validating access token."}', {}))
378
+ expect { Koala::Facebook::API.new("foo").batch do |batch_api|
379
+ batch_api.get_object('me')
380
+ end }.to raise_exception(Koala::Facebook::APIError)
381
+ end
382
+
383
+ it "returns the result status if http_component is status" do
384
+ Koala.stub(:make_request).and_return(Koala::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
385
+ result = @api.batch do |batch_api|
386
+ batch_api.get_object(KoalaTest.user1, {}, :http_component => :status)
387
+ end
388
+ result[0].should == 203
389
+ end
390
+
391
+ it "returns the result headers as a hash if http_component is headers" do
392
+ Koala.stub(:make_request).and_return(Koala::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
393
+ result = @api.batch do |batch_api|
394
+ batch_api.get_object(KoalaTest.user1, {}, :http_component => :headers)
395
+ end
396
+ result[0].should == {"Content-Type" => "text/javascript; charset=UTF-8"}
397
+ end
398
+ end
399
+
400
+ it "is thread safe" do
401
+ # ensure batch operations on one thread don't affect those on another
402
+ thread_one_count = 0
403
+ thread_two_count = 0
404
+ first_count = 20
405
+ second_count = 10
406
+
407
+ Koala.stub(:make_request).and_return(@fake_response)
408
+
409
+ thread1 = Thread.new do
410
+ @api.batch do |batch_api|
411
+ first_count.times {|i| batch_api.get_object("me"); sleep(0.01) }
412
+ thread_one_count = batch_api.batch_calls.count
413
+ end
414
+ end
415
+
416
+ thread2 = Thread.new do
417
+ @api.batch do |batch_api|
418
+ second_count.times {|i| batch_api.get_object("me"); sleep(0.01) }
419
+ thread_two_count = batch_api.batch_calls.count
420
+ end
421
+ end
422
+
423
+ thread1.join
424
+ thread2.join
425
+
426
+ thread_one_count.should == first_count
427
+ thread_two_count.should == second_count
428
+ end
429
+ end
430
+ end
431
+
432
+ describe "usage tests" do
433
+ it "can get two results at once" do
434
+ me, koppel = @api.batch do |batch_api|
435
+ batch_api.get_object('me')
436
+ batch_api.get_object(KoalaTest.user1)
437
+ end
438
+ me['id'].should_not be_nil
439
+ koppel['id'].should_not be_nil
440
+ end
441
+
442
+ it 'should be able to make mixed calls inside of a batch' do
443
+ me, friends = @api.batch do |batch_api|
444
+ batch_api.get_object('me')
445
+ batch_api.get_connections('me', 'friends')
446
+ end
447
+ me['id'].should_not be_nil
448
+ friends.should be_an(Array)
449
+ end
450
+
451
+ it 'should be able to make a get_picture call inside of a batch' do
452
+ pictures = @api.batch do |batch_api|
453
+ batch_api.get_picture('me')
454
+ end
455
+ pictures.first.should_not be_empty
456
+ end
457
+
458
+ it "should handle requests for two different tokens" do
459
+ me, insights = @api.batch do |batch_api|
460
+ batch_api.get_object('me')
461
+ batch_api.get_connections(@app_id, 'insights', {}, {"access_token" => @app_api.access_token})
462
+ end
463
+ me['id'].should_not be_nil
464
+ insights.should be_an(Array)
465
+ end
466
+
467
+ it "inserts errors in the appropriate place, without breaking other results" do
468
+ failed_insights, koppel = @api.batch do |batch_api|
469
+ batch_api.get_connections(@app_id, 'insights')
470
+ batch_api.get_object(KoalaTest.user1, {}, {"access_token" => @app_api.access_token})
471
+ end
472
+ failed_insights.should be_a(Koala::Facebook::APIError)
473
+ koppel["id"].should_not be_nil
474
+ end
475
+
476
+ it "handles different request methods" do
477
+ result = @api.put_wall_post("Hello, world, from the test suite batch API!")
478
+ wall_post = result["id"]
479
+
480
+ wall_post, koppel = @api.batch do |batch_api|
481
+ batch_api.put_like(wall_post)
482
+ batch_api.delete_object(wall_post)
483
+ end
484
+ end
485
+
486
+ it "allows FQL" do
487
+ result = @api.batch do |batch_api|
488
+ batch_api.graph_call("method/fql.query", {:query=>"select first_name from user where uid=#{KoalaTest.user1_id}"}, "post")
489
+ end
490
+
491
+ fql_result = result[0]
492
+ fql_result[0].should be_a(Hash)
493
+ fql_result[0]["first_name"].should == "Alex"
494
+ end
495
+
496
+ describe "binary files" do
497
+ it "posts binary files" do
498
+ file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
499
+
500
+ Koala::Facebook::BatchOperation.instance_variable_set(:@identifier, 0)
501
+ result = @api.batch do |batch_api|
502
+ batch_api.put_picture(file)
503
+ end
504
+
505
+ @temporary_object_id = result[0]["id"]
506
+ @temporary_object_id.should_not be_nil
507
+ end
508
+
509
+ it "posts binary files with multiple requests" do
510
+ file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
511
+ file2 = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
512
+
513
+ Koala::Facebook::BatchOperation.instance_variable_set(:@identifier, 0)
514
+ results = @api.batch do |batch_api|
515
+ batch_api.put_picture(file)
516
+ batch_api.put_picture(file2, {}, KoalaTest.user1)
517
+ end
518
+ results[0]["id"].should_not be_nil
519
+ results[1]["id"].should_not be_nil
520
+ end
521
+ end
522
+
523
+ describe "relating requests" do
524
+ it "allows you create relationships between requests without omit_response_on_success" do
525
+ results = @api.batch do |batch_api|
526
+ batch_api.get_connections("me", "friends", {:limit => 5}, :batch_args => {:name => "get-friends"})
527
+ batch_api.get_objects("{result=get-friends:$.data.*.id}")
528
+ end
529
+
530
+ results[0].should be_nil
531
+ results[1].should be_an(Hash)
532
+ end
533
+
534
+ it "allows you create relationships between requests with omit_response_on_success" do
535
+ results = @api.batch do |batch_api|
536
+ batch_api.get_connections("me", "friends", {:limit => 5}, :batch_args => {:name => "get-friends", :omit_response_on_success => false})
537
+ batch_api.get_objects("{result=get-friends:$.data.*.id}")
538
+ end
539
+
540
+ results[0].should be_an(Array)
541
+ results[1].should be_an(Hash)
542
+ end
543
+
544
+ it "allows you to create dependencies" do
545
+ me, koppel = @api.batch do |batch_api|
546
+ batch_api.get_object("me", {}, :batch_args => {:name => "getme"})
547
+ batch_api.get_object(KoalaTest.user1, {}, :batch_args => {:depends_on => "getme"})
548
+ end
549
+
550
+ me.should be_nil # gotcha! it's omitted because it's a successfully-executed dependency
551
+ koppel["id"].should_not be_nil
552
+ end
553
+
554
+ it "properly handles dependencies that fail" do
555
+ data, koppel = @api.batch do |batch_api|
556
+ batch_api.get_connections(@app_id, 'insights', {}, :batch_args => {:name => "getdata"})
557
+ batch_api.get_object(KoalaTest.user1, {}, :batch_args => {:depends_on => "getdata"})
558
+ end
559
+
560
+ data.should be_a(Koala::Facebook::APIError)
561
+ koppel.should be_nil
562
+ end
563
+
564
+ it "throws an error for badly-constructed request relationships" do
565
+ expect {
566
+ @api.batch do |batch_api|
567
+ batch_api.get_connections("me", "friends", {:limit => 5})
568
+ batch_api.get_objects("{result=i-dont-exist:$.data.*.id}")
569
+ end
570
+ }.to raise_exception(Koala::Facebook::APIError)
571
+ end
572
+ end
573
+ end
574
+
575
+ describe "new interface" do
576
+ it "includes a deprecation warning on GraphAPI" do
577
+ begin
578
+ Koala::Facebook::GraphAPI.batch do
579
+ end
580
+ rescue NoMethodError => @err
581
+ end
582
+
583
+ # verify the message points people to the wiki page
584
+ @err.should
585
+ @err.message.should =~ /https\:\/\/github.com\/arsduo\/koala\/wiki\/Batch-requests/
586
+ end
587
+
588
+ it "includes a deprecation warning on GraphAndRESTAPI" do
589
+ begin
590
+ Koala::Facebook::GraphAndRestAPI.batch do
591
+ end
592
+ rescue NoMethodError => @err
593
+ end
594
+
595
+ # verify the message points people to the wiki page
596
+ @err.should
597
+ @err.message.should =~ /https\:\/\/github.com\/arsduo\/koala\/wiki\/Batch-requests/
598
+ end
599
+ end
600
+ end