tyler_koala 1.2.0beta
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +12 -0
- data/.gitignore +5 -0
- data/.travis.yml +9 -0
- data/CHANGELOG +185 -0
- data/Gemfile +11 -0
- data/LICENSE +22 -0
- data/Manifest +39 -0
- data/Rakefile +16 -0
- data/autotest/discover.rb +1 -0
- data/koala.gemspec +50 -0
- data/lib/koala.rb +119 -0
- data/lib/koala/batch_operation.rb +74 -0
- data/lib/koala/graph_api.rb +281 -0
- data/lib/koala/graph_batch_api.rb +87 -0
- data/lib/koala/graph_collection.rb +54 -0
- data/lib/koala/http_service.rb +161 -0
- data/lib/koala/oauth.rb +181 -0
- data/lib/koala/realtime_updates.rb +89 -0
- data/lib/koala/rest_api.rb +95 -0
- data/lib/koala/test_users.rb +102 -0
- data/lib/koala/uploadable_io.rb +180 -0
- data/lib/koala/utils.rb +7 -0
- data/readme.md +160 -0
- data/spec/cases/api_base_spec.rb +101 -0
- data/spec/cases/error_spec.rb +30 -0
- data/spec/cases/graph_and_rest_api_spec.rb +48 -0
- data/spec/cases/graph_api_batch_spec.rb +600 -0
- data/spec/cases/graph_api_spec.rb +42 -0
- data/spec/cases/http_service_spec.rb +420 -0
- data/spec/cases/koala_spec.rb +21 -0
- data/spec/cases/oauth_spec.rb +428 -0
- data/spec/cases/realtime_updates_spec.rb +198 -0
- data/spec/cases/rest_api_spec.rb +41 -0
- data/spec/cases/test_users_spec.rb +281 -0
- data/spec/cases/uploadable_io_spec.rb +206 -0
- data/spec/cases/utils_spec.rb +8 -0
- data/spec/fixtures/beach.jpg +0 -0
- data/spec/fixtures/cat.m4v +0 -0
- data/spec/fixtures/facebook_data.yml +61 -0
- data/spec/fixtures/mock_facebook_responses.yml +439 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/graph_api_shared_examples.rb +502 -0
- data/spec/support/json_testing_fix.rb +42 -0
- data/spec/support/koala_test.rb +163 -0
- data/spec/support/mock_http_service.rb +98 -0
- data/spec/support/ordered_hash.rb +205 -0
- data/spec/support/rest_api_shared_examples.rb +285 -0
- data/spec/support/uploadable_io_shared_examples.rb +70 -0
- 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
|