koala 1.0.0 → 1.1.0rc
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.
- data/.autotest +12 -0
- data/.gitignore +2 -1
- data/CHANGELOG +18 -0
- data/autotest/discover.rb +1 -0
- data/koala.gemspec +5 -5
- data/lib/koala.rb +29 -194
- data/lib/koala/graph_api.rb +71 -31
- data/lib/koala/graph_api_batch.rb +151 -0
- data/lib/koala/http_services.rb +10 -112
- data/lib/koala/http_services/net_http_service.rb +87 -0
- data/lib/koala/http_services/typhoeus_service.rb +37 -0
- data/lib/koala/oauth.rb +181 -0
- data/lib/koala/realtime_updates.rb +5 -14
- data/lib/koala/rest_api.rb +13 -8
- data/lib/koala/uploadable_io.rb +35 -7
- data/readme.md +19 -7
- data/spec/cases/api_base_spec.rb +2 -2
- data/spec/cases/graph_api_batch_spec.rb +600 -0
- data/spec/cases/http_services/http_service_spec.rb +76 -1
- data/spec/cases/http_services/net_http_service_spec.rb +164 -48
- data/spec/cases/http_services/typhoeus_service_spec.rb +27 -19
- data/spec/cases/koala_spec.rb +55 -0
- data/spec/cases/test_users_spec.rb +1 -1
- data/spec/cases/uploadable_io_spec.rb +56 -14
- data/spec/fixtures/mock_facebook_responses.yml +89 -5
- data/spec/support/graph_api_shared_examples.rb +34 -7
- data/spec/support/mock_http_service.rb +54 -56
- data/spec/support/rest_api_shared_examples.rb +131 -7
- data/spec/support/setup_mocks_or_live.rb +3 -3
- metadata +36 -24
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Koala" do
|
4
|
+
it "has an http_service accessor" do
|
5
|
+
Koala.respond_to?(:http_service)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should let an http service be set" do
|
9
|
+
current_service = Koala.http_service
|
10
|
+
Koala.http_service = Koala::MockHTTPService
|
11
|
+
Koala.http_service.should == Koala::MockHTTPService
|
12
|
+
# reset the service back to the original one (important for live tests)
|
13
|
+
Koala.http_service = current_service
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets Net::HTTP as the base service" do
|
17
|
+
Koala.base_http_service.should == Koala::NetHTTPService
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".always_use_ssl" do
|
21
|
+
it "should be added" do
|
22
|
+
# in Ruby 1.8, .methods returns strings
|
23
|
+
# in Ruby 1.9, .method returns symbols
|
24
|
+
Koala.methods.collect {|m| m.to_sym}.should include(:always_use_ssl)
|
25
|
+
Koala.methods.collect {|m| m.to_sym}.should include(:always_use_ssl=)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe ".make_request" do
|
30
|
+
|
31
|
+
before :each do
|
32
|
+
@old_service = Koala.http_service
|
33
|
+
Koala.http_service = Koala::MockHTTPService
|
34
|
+
end
|
35
|
+
|
36
|
+
after :each do
|
37
|
+
Koala.http_service = @old_service
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should use SSL if Koala.always_use_ssl is set to true, even if there's no token" do
|
41
|
+
Koala.http_service.should_receive(:make_request).with(anything, anything, anything, hash_including(:use_ssl => true))
|
42
|
+
|
43
|
+
Koala.always_use_ssl = true
|
44
|
+
Koala.make_request('anything', {}, 'anything')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should allow the caller to override the http_service" do
|
48
|
+
http_service = stub
|
49
|
+
http_service.should_receive(:make_request)
|
50
|
+
|
51
|
+
Koala.make_request(anything, anything, anything, :http_service => http_service)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -16,7 +16,7 @@ describe "Koala::Facebook::TestUsers" do
|
|
16
16
|
raise Exception, "Must supply OAuth app id, secret, app_access_token, and callback to run live subscription tests!"
|
17
17
|
end
|
18
18
|
|
19
|
-
@is_mock =
|
19
|
+
@is_mock = Koala.http_service.respond_to?(:mock?) && Koala.http_service.mock?
|
20
20
|
end
|
21
21
|
|
22
22
|
describe "when initializing" do
|
@@ -9,6 +9,21 @@ module Koala::MIME
|
|
9
9
|
end
|
10
10
|
|
11
11
|
describe "Koala::UploadableIO" do
|
12
|
+
def rails_3_mocks
|
13
|
+
tempfile = stub('Tempfile', :path => "foo")
|
14
|
+
uploaded_file = stub('ActionDispatch::Http::UploadedFile',
|
15
|
+
:content_type => true,
|
16
|
+
:tempfile => tempfile
|
17
|
+
)
|
18
|
+
tempfile.stub!(:respond_to?).with(:path).and_return(true)
|
19
|
+
|
20
|
+
[tempfile, uploaded_file]
|
21
|
+
end
|
22
|
+
|
23
|
+
def sinatra_mocks
|
24
|
+
{:type => "type", :tempfile => "Tempfile"}
|
25
|
+
end
|
26
|
+
|
12
27
|
describe "the constructor" do
|
13
28
|
describe "when given a file path" do
|
14
29
|
before(:each) do
|
@@ -29,6 +44,12 @@ describe "Koala::UploadableIO" do
|
|
29
44
|
stub_type = @koala_io_params[1] = stub('Content Type')
|
30
45
|
Koala::UploadableIO.new(*@koala_io_params).content_type.should == stub_type
|
31
46
|
end
|
47
|
+
|
48
|
+
it "should detect that NetHTTPService must be used" do
|
49
|
+
@koala_io_params[0] = mock
|
50
|
+
@koala_io_params[0].stub!(:read)
|
51
|
+
Koala::UploadableIO.new(*@koala_io_params).requires_base_http_service.should be_true
|
52
|
+
end
|
32
53
|
end
|
33
54
|
|
34
55
|
describe "and no content type" do
|
@@ -63,13 +84,7 @@ describe "Koala::UploadableIO" do
|
|
63
84
|
|
64
85
|
describe "when given a Rails 3 ActionDispatch::Http::UploadedFile" do
|
65
86
|
before(:each) do
|
66
|
-
@tempfile
|
67
|
-
@uploaded_file = stub('ActionDispatch::Http::UploadedFile',
|
68
|
-
:content_type => true,
|
69
|
-
:tempfile => @tempfile
|
70
|
-
)
|
71
|
-
|
72
|
-
@tempfile.stub!(:respond_to?).with(:path).and_return(true)
|
87
|
+
@tempfile, @uploaded_file = rails_3_mocks
|
73
88
|
end
|
74
89
|
|
75
90
|
it "should get the content type via the content_type method" do
|
@@ -87,10 +102,7 @@ describe "Koala::UploadableIO" do
|
|
87
102
|
|
88
103
|
describe "when given a Sinatra file parameter hash" do
|
89
104
|
before(:each) do
|
90
|
-
@file_hash =
|
91
|
-
:type => "type",
|
92
|
-
:tempfile => "Tempfile"
|
93
|
-
}
|
105
|
+
@file_hash = sinatra_mocks
|
94
106
|
end
|
95
107
|
|
96
108
|
it "should get the content type from the :type key" do
|
@@ -130,9 +142,19 @@ describe "Koala::UploadableIO" do
|
|
130
142
|
UploadIO.stub!(:new).with(anything, anything, anything).and_return(@upload_io)
|
131
143
|
end
|
132
144
|
|
133
|
-
|
134
|
-
|
135
|
-
|
145
|
+
context "if no filename was provided" do
|
146
|
+
it "should call the constructor with the content type, file name, and a dummy file name" do
|
147
|
+
UploadIO.should_receive(:new).with(BEACH_BALL_PATH, "content/type", anything).and_return(@upload_io)
|
148
|
+
Koala::UploadableIO.new(BEACH_BALL_PATH, "content/type").to_upload_io.should == @upload_io
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "if a filename was provided" do
|
153
|
+
it "should call the constructor with the content type, file name, and the filename" do
|
154
|
+
filename = "file"
|
155
|
+
UploadIO.should_receive(:new).with(BEACH_BALL_PATH, "content/type", filename).and_return(@upload_io)
|
156
|
+
Koala::UploadableIO.new(BEACH_BALL_PATH, "content/type", filename).to_upload_io
|
157
|
+
end
|
136
158
|
end
|
137
159
|
end
|
138
160
|
|
@@ -148,4 +170,24 @@ describe "Koala::UploadableIO" do
|
|
148
170
|
Koala::UploadableIO.new(BEACH_BALL_PATH).to_file.should == result
|
149
171
|
end
|
150
172
|
end
|
173
|
+
|
174
|
+
describe "#binary_content?" do
|
175
|
+
it "returns true for Rails 3 file uploads" do
|
176
|
+
Koala::UploadableIO.binary_content?(rails_3_mocks.last).should be_true
|
177
|
+
end
|
178
|
+
|
179
|
+
it "returns true for Sinatra file uploads" do
|
180
|
+
Koala::UploadableIO.binary_content?(rails_3_mocks.last).should be_true
|
181
|
+
end
|
182
|
+
|
183
|
+
it "returns true for File objects" do
|
184
|
+
Koala::UploadableIO.binary_content?(File.open(BEACH_BALL_PATH)).should be_true
|
185
|
+
end
|
186
|
+
|
187
|
+
it "returns false for everything else" do
|
188
|
+
Koala::UploadableIO.binary_content?(StringIO.new).should be_false
|
189
|
+
Koala::UploadableIO.binary_content?(BEACH_BALL_PATH).should be_false
|
190
|
+
Koala::UploadableIO.binary_content?(nil).should be_false
|
191
|
+
end
|
192
|
+
end
|
151
193
|
end # describe UploadableIO
|
@@ -24,6 +24,18 @@ rest_api:
|
|
24
24
|
with_token: '[{"read_stream":1}]'
|
25
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 = 216743"}]}'
|
26
26
|
|
27
|
+
/method/fql.multiquery:
|
28
|
+
'queries={"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 = 216743"}]}'
|
32
|
+
'queries={"query1":"select first_name from user where uid = 216743","query2":"select first_name from user where uid = 2905623"}':
|
33
|
+
get:
|
34
|
+
with_token: '[{"name":"query1", "fql_result_set":[{"first_name":"Chris"}]},{"name":"query2", "fql_result_set":[{"first_name":"Alex"}]}]'
|
35
|
+
no_token: '[{"name":"query1", "fql_result_set":[{"first_name":"Chris"}]},{"name":"query2", "fql_result_set":[{"first_name":"Alex"}]}]'
|
36
|
+
|
37
|
+
|
38
|
+
|
27
39
|
|
28
40
|
# ====== GRAPH API =====
|
29
41
|
graph_api:
|
@@ -49,7 +61,7 @@ graph_api:
|
|
49
61
|
|
50
62
|
test_user_no_perms: &test_user_no_perms
|
51
63
|
post:
|
52
|
-
with_token: '{"id": "777777777", "access_token":"<%=
|
64
|
+
with_token: '{"id": "777777777", "access_token":"<%= APP_ACCESS_TOKEN %>", "login_url":"https://www.facebook.com/platform/test_account.."}'
|
53
65
|
|
54
66
|
test_user_befriended: &test_user_befriended
|
55
67
|
post:
|
@@ -59,8 +71,59 @@ graph_api:
|
|
59
71
|
root:
|
60
72
|
ids=contextoptional,naitik:
|
61
73
|
get:
|
62
|
-
with_token: '
|
63
|
-
no_token: '
|
74
|
+
with_token: '{"contextoptional":"{}","naitik":"{}"}'
|
75
|
+
no_token: '{"contextoptional":"{}","naitik":"{}"}'
|
76
|
+
# Ruby 1.8.7 and 1.9.2 generate JSON with different key ordering, hence we have to dynamically generate it here
|
77
|
+
batch=<%= [{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "koppel"}].to_json %>:
|
78
|
+
post:
|
79
|
+
with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"id\":\"456\"}"}]'
|
80
|
+
batch=<%= [{"method" => "get", "relative_url" => "me/picture"}].to_json %>:
|
81
|
+
post:
|
82
|
+
with_token: '[{"headers":[{"name":"Location","value":"http://google.com"}]}]'
|
83
|
+
batch=<%= [{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "me/friends"}].to_json %>:
|
84
|
+
post:
|
85
|
+
with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[]}"}]'
|
86
|
+
batch=<%= [{"method"=>"get", "relative_url"=>"me"}, {"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}].to_json %>:
|
87
|
+
post:
|
88
|
+
with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[]}"}]'
|
89
|
+
batch=<%= [{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights"}, {"method"=>"get", "relative_url"=>"koppel?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}].to_json %>:
|
90
|
+
post:
|
91
|
+
with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},{"body":"{\"id\":\"123\"}"}]'
|
92
|
+
batch=<%= [{"method"=>"post", "relative_url"=>"FEED_ITEM_BATCH/likes"}, {"method"=>"delete", "relative_url"=> "FEED_ITEM_BATCH"}].to_json %>:
|
93
|
+
post:
|
94
|
+
with_token: '[{"body": "{\"id\": \"MOCK_LIKE\"}"},{"body":true}]'
|
95
|
+
batch=<%= [{"method" => "get", "relative_url" => "me/friends?limit=5", "name" => "get-friends"}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=get-friends:$.data.*.id}"}"}].to_json %>:
|
96
|
+
post:
|
97
|
+
with_token: '[null,{"body":"{}"}]'
|
98
|
+
batch=<%= [{"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}"}"}].to_json %>:
|
99
|
+
post:
|
100
|
+
with_token: '[{"body":"{\"data\":[]}"},{"body":"{}"}]'
|
101
|
+
batch=<%= [{"method" => "get", "relative_url" => "me/friends?limit=5"}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=i-dont-exist:$.data.*.id}"}"}].to_json %>:
|
102
|
+
post:
|
103
|
+
with_token: '{"error":190,"error_description":"Error validating access token."}'
|
104
|
+
batch=<%= [{"method" => "post", "relative_url" => "method/fql.query", "body" => "query=select+name+from+user+where+uid%3D4"}].to_json %>:
|
105
|
+
post:
|
106
|
+
with_token: '[{"body":"[{\"name\":\"Mark Zuckerberg\"}]"}]'
|
107
|
+
batch=<%= [{"method"=>"get", "relative_url"=>"me", "name" => "getme"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getme"}].to_json %>:
|
108
|
+
post:
|
109
|
+
with_token: '[null,{"body":"{\"id\":\"123\"}"}]'
|
110
|
+
batch=<%= [{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights", "name" => "getdata"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getdata"}].to_json %>:
|
111
|
+
post:
|
112
|
+
with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},null]'
|
113
|
+
batch=<%= [{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights", "name" => "getdata"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getdata"}].to_json %>:
|
114
|
+
post:
|
115
|
+
with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},null]'
|
116
|
+
batch=<%= [{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "file0_0"}].to_json %>&file0_0=[FILE]:
|
117
|
+
post:
|
118
|
+
with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},null]'
|
119
|
+
batch=<%= [{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "file0_0"}].to_json %>&file0_0=[FILE]:
|
120
|
+
post:
|
121
|
+
with_token: '[{"body":"{\"id\": \"MOCK_PHOTO\"}"}]'
|
122
|
+
batch=<%= [{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "file0_0"}, {"method"=>"post", "relative_url"=>"koppel/photos", "attached_files" => "file1_0"}].to_json %>&file0_0=[FILE]&file1_0=[FILE]:
|
123
|
+
post:
|
124
|
+
with_token: '[{"body":"{\"id\": \"MOCK_PHOTO\"}"}, {"body":"{\"id\": \"MOCK_PHOTO\"}"}]'
|
125
|
+
|
126
|
+
|
64
127
|
/me:
|
65
128
|
no_args:
|
66
129
|
get:
|
@@ -83,6 +146,9 @@ graph_api:
|
|
83
146
|
message=Hello, world, from the test suite delete method!:
|
84
147
|
post:
|
85
148
|
with_token: '{"id": "FEED_ITEM_DELETE"}'
|
149
|
+
message=Hello, world, from the test suite batch API!:
|
150
|
+
post:
|
151
|
+
with_token: '{"id": "FEED_ITEM_BATCH"}'
|
86
152
|
link=http://oauth.twoalex.com/&message=Hello, world, from the test suite again!&name=OAuth Playground:
|
87
153
|
post:
|
88
154
|
with_token: '{"id": "FEED_ITEM_CONTEXT"}'
|
@@ -144,6 +210,24 @@ graph_api:
|
|
144
210
|
headers:
|
145
211
|
Location: http://facebook.com/
|
146
212
|
|
213
|
+
/comments:
|
214
|
+
ids=http://developers.facebook.com/blog/post/472:
|
215
|
+
get:
|
216
|
+
with_token: '{"http://developers.facebook.com/blog/post/472": []}'
|
217
|
+
no_token: '{"http://developers.facebook.com/blog/post/472": []}'
|
218
|
+
ids=http://developers.facebook.com/blog/post/490,http://developers.facebook.com/blog/post/472:
|
219
|
+
get:
|
220
|
+
with_token: '{"http://developers.facebook.com/blog/post/490": [], "http://developers.facebook.com/blog/post/472": []}'
|
221
|
+
no_token: '{"http://developers.facebook.com/blog/post/490": [], "http://developers.facebook.com/blog/post/472": []}'
|
222
|
+
ids=:
|
223
|
+
get:
|
224
|
+
body: '{"error": {"type": "OAuthException","message": "Cannot specify an empty identifier"}}'
|
225
|
+
code: 400
|
226
|
+
no_args:
|
227
|
+
get:
|
228
|
+
body: '{"error": {"type": "Exception","message": "No node specified"}}'
|
229
|
+
code: 500
|
230
|
+
|
147
231
|
/search:
|
148
232
|
q=facebook:
|
149
233
|
get:
|
@@ -169,7 +253,7 @@ graph_api:
|
|
169
253
|
<<: *oauth_error
|
170
254
|
client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&type=client_cred:
|
171
255
|
post:
|
172
|
-
no_token: access_token=<%=
|
256
|
+
no_token: access_token=<%= APP_ACCESS_TOKEN %>
|
173
257
|
/oauth/exchange_sessions:
|
174
258
|
client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&sessions=<%= OAUTH_DATA["session_key"] %>&type=client_cred:
|
175
259
|
post:
|
@@ -311,4 +395,4 @@ graph_api:
|
|
311
395
|
|
312
396
|
/777777777:
|
313
397
|
no_args:
|
314
|
-
<<: *item_deleted
|
398
|
+
<<: *item_deleted
|
@@ -51,9 +51,19 @@ shared_examples_for "Koala GraphAPI" do
|
|
51
51
|
(result["id"] && result["name"]).should
|
52
52
|
end
|
53
53
|
|
54
|
+
it "should return [] from get_objects if passed an empty array" do
|
55
|
+
results = @api.get_objects([])
|
56
|
+
results.should == []
|
57
|
+
end
|
58
|
+
|
54
59
|
it "should be able to get multiple objects" do
|
55
60
|
results = @api.get_objects(["contextoptional", "naitik"])
|
56
|
-
results.
|
61
|
+
results.should have(2).items
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should be able to get multiple objects if they're a string" do
|
65
|
+
results = @api.get_objects("contextoptional,naitik")
|
66
|
+
results.should have(2).items
|
57
67
|
end
|
58
68
|
|
59
69
|
it "should be able to access a user's picture" do
|
@@ -69,6 +79,16 @@ shared_examples_for "Koala GraphAPI" do
|
|
69
79
|
result.should be_a(Array)
|
70
80
|
end
|
71
81
|
|
82
|
+
it "should be able to access comments for a URL" do
|
83
|
+
result = @api.get_comments_for_urls(["http://developers.facebook.com/blog/post/472"])
|
84
|
+
(result["http://developers.facebook.com/blog/post/472"]).should
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should be able to access comments for 2 URLs" do
|
88
|
+
result = @api.get_comments_for_urls(["http://developers.facebook.com/blog/post/490", "http://developers.facebook.com/blog/post/472"])
|
89
|
+
(result["http://developers.facebook.com/blog/post/490"] && result["http://developers.facebook.com/blog/post/472"]).should
|
90
|
+
end
|
91
|
+
|
72
92
|
# SEARCH
|
73
93
|
it "should be able to search" do
|
74
94
|
result = @api.search("facebook")
|
@@ -86,7 +106,6 @@ end
|
|
86
106
|
|
87
107
|
|
88
108
|
shared_examples_for "Koala GraphAPI with an access token" do
|
89
|
-
|
90
109
|
it "should get private data about a user" do
|
91
110
|
result = @api.get_object("koppel")
|
92
111
|
# updated_time should be a pretty fixed test case
|
@@ -156,6 +175,14 @@ shared_examples_for "Koala GraphAPI with an access token" do
|
|
156
175
|
@temporary_object_id.should_not be_nil
|
157
176
|
end
|
158
177
|
|
178
|
+
it "uses the base HTTP service if the upload is a StringIO or similar" do
|
179
|
+
source = stub("UploadIO")
|
180
|
+
Koala::UploadableIO.stub(:new).and_return(source)
|
181
|
+
source.stub(:requires_base_http_service).and_return(true)
|
182
|
+
Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(:http_service => Koala.base_http_service)).and_return(Koala::Response.new(200, "[]", {}))
|
183
|
+
@api.put_picture(StringIO.new)
|
184
|
+
end
|
185
|
+
|
159
186
|
it "should be able to post photos to the user's wall without an open file object" do
|
160
187
|
content_type = "image/jpg",
|
161
188
|
file_path = File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg")
|
@@ -330,18 +357,18 @@ shared_examples_for "Koala GraphAPI with GraphCollection" do
|
|
330
357
|
|
331
358
|
it "should return the previous page of results" do
|
332
359
|
@result.should_receive(:previous_page_params).and_return([@base, @args])
|
333
|
-
@api.should_receive(:graph_call).with(@base, @args).
|
360
|
+
@api.should_receive(:graph_call).with(@base, @args).and_yield(@second_page)
|
334
361
|
Koala::Facebook::GraphCollection.should_receive(:new).with(@second_page, @api).and_return(@page_of_results)
|
335
362
|
|
336
|
-
@result.previous_page
|
363
|
+
@result.previous_page#.should == @page_of_results
|
337
364
|
end
|
338
365
|
|
339
366
|
it "should return the next page of results" do
|
340
367
|
@result.should_receive(:next_page_params).and_return([@base, @args])
|
341
|
-
@api.should_receive(:graph_call).with(@base, @args).
|
368
|
+
@api.should_receive(:graph_call).with(@base, @args).and_yield(@second_page)
|
342
369
|
Koala::Facebook::GraphCollection.should_receive(:new).with(@second_page, @api).and_return(@page_of_results)
|
343
370
|
|
344
|
-
@result.next_page
|
371
|
+
@result.next_page#.should == @page_of_results
|
345
372
|
end
|
346
373
|
|
347
374
|
it "should return nil it there are no other pages" do
|
@@ -421,4 +448,4 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
421
448
|
it "should not be able to delete a like" do
|
422
449
|
lambda { @api.delete_like("7204941866_119776748033392") }.should raise_error(Koala::Facebook::APIError)
|
423
450
|
end
|
424
|
-
end
|
451
|
+
end
|
@@ -3,11 +3,12 @@ require 'yaml'
|
|
3
3
|
|
4
4
|
module Koala
|
5
5
|
module MockHTTPService
|
6
|
-
|
7
|
-
IS_MOCK = true # this lets our tests figure out if we want to stub methods
|
6
|
+
include Koala::HTTPService
|
8
7
|
|
8
|
+
# Mocks all HTTP requests for with koala_spec_with_mocks.rb
|
9
9
|
# Mocked values to be included in TEST_DATA used in specs
|
10
10
|
ACCESS_TOKEN = '*'
|
11
|
+
APP_ACCESS_TOKEN = "**"
|
11
12
|
OAUTH_CODE = 'OAUTHCODE'
|
12
13
|
|
13
14
|
# Loads testing data
|
@@ -18,7 +19,7 @@ module Koala
|
|
18
19
|
# Useful in mock_facebook_responses.yml
|
19
20
|
OAUTH_DATA = TEST_DATA['oauth_test_data']
|
20
21
|
OAUTH_DATA.merge!({
|
21
|
-
'app_access_token' =>
|
22
|
+
'app_access_token' => APP_ACCESS_TOKEN,
|
22
23
|
'session_key' => "session_key",
|
23
24
|
'multiple_session_keys' => ["session_key", "session_key_2"]
|
24
25
|
})
|
@@ -30,66 +31,63 @@ module Koala
|
|
30
31
|
mock_response_file_path = File.join(File.dirname(__FILE__), '..', 'fixtures', 'mock_facebook_responses.yml')
|
31
32
|
RESPONSES = YAML.load(ERB.new(IO.read(mock_response_file_path)).result(binding))
|
32
33
|
|
33
|
-
def self.
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
path = 'root' if path == '' || path == '/'
|
40
|
-
verb ||= 'get'
|
41
|
-
server = options[:rest_api] ? 'rest_api' : 'graph_api'
|
42
|
-
with_token = args.delete('access_token') == ACCESS_TOKEN ? 'with_token' : 'no_token'
|
43
|
-
|
44
|
-
# Assume format is always JSON
|
45
|
-
args.delete('format')
|
34
|
+
def self.make_request(path, args, verb, options = {})
|
35
|
+
path = 'root' if path == '' || path == '/'
|
36
|
+
verb ||= 'get'
|
37
|
+
server = options[:rest_api] ? 'rest_api' : 'graph_api'
|
38
|
+
token = args.delete('access_token')
|
39
|
+
with_token = (token == ACCESS_TOKEN || token == APP_ACCESS_TOKEN) ? 'with_token' : 'no_token'
|
46
40
|
|
47
|
-
|
48
|
-
|
41
|
+
# Assume format is always JSON
|
42
|
+
args.delete('format')
|
49
43
|
|
50
|
-
|
51
|
-
|
44
|
+
# Create a hash key for the arguments
|
45
|
+
args = create_params_key(args)
|
52
46
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
# create response class object
|
57
|
-
response_object = if response.is_a? String
|
58
|
-
Koala::Response.new(200, response, {})
|
59
|
-
else
|
60
|
-
Koala::Response.new(response["code"] || 200, response["body"] || "", response["headers"] || {})
|
61
|
-
end
|
62
|
-
|
63
|
-
rescue NoMethodError
|
64
|
-
# Raises an error message with the place in the data YML
|
65
|
-
# to place a mock as well as a URL to request from
|
66
|
-
# Facebook's servers for the actual data
|
67
|
-
# (Don't forget to replace ACCESS_TOKEN with a real access token)
|
68
|
-
data_trace = [server, path, args, verb, with_token] * ': '
|
69
|
-
|
70
|
-
args = args == 'no_args' ? '' : "#{args}&"
|
71
|
-
args += 'format=json'
|
72
|
-
args += "&access_token=#{ACCESS_TOKEN}" if with_token
|
73
|
-
|
74
|
-
raise "Missing a mock response for #{data_trace}\nAPI PATH: #{[path, args].join('?')}"
|
75
|
-
end
|
47
|
+
begin
|
48
|
+
response = RESPONSES[server][path][args][verb][with_token]
|
76
49
|
|
77
|
-
|
78
|
-
|
50
|
+
# Raises an error of with_token/no_token key is missing
|
51
|
+
raise NoMethodError unless response
|
79
52
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
'no_args'
|
53
|
+
# create response class object
|
54
|
+
response_object = if response.is_a? String
|
55
|
+
Koala::Response.new(200, response, {})
|
84
56
|
else
|
85
|
-
|
86
|
-
arr[1] = '[FILE]' if arr[1].kind_of?(Koala::UploadableIO)
|
87
|
-
arr.join('=')
|
88
|
-
end.join('&')
|
57
|
+
Koala::Response.new(response["code"] || 200, response["body"] || "", response["headers"] || {})
|
89
58
|
end
|
90
|
-
end
|
91
59
|
|
92
|
-
|
93
|
-
|
60
|
+
rescue NoMethodError
|
61
|
+
# Raises an error message with the place in the data YML
|
62
|
+
# to place a mock as well as a URL to request from
|
63
|
+
# Facebook's servers for the actual data
|
64
|
+
# (Don't forget to replace ACCESS_TOKEN with a real access token)
|
65
|
+
data_trace = [server, path, args, verb, with_token] * ': '
|
66
|
+
|
67
|
+
args = args == 'no_args' ? '' : "#{args}&"
|
68
|
+
args += 'format=json'
|
69
|
+
args += "&access_token=#{ACCESS_TOKEN}" if with_token
|
70
|
+
|
71
|
+
raise "Missing a mock response for #{data_trace}\nAPI PATH: #{[path, args].join('?')}"
|
72
|
+
end
|
73
|
+
|
74
|
+
response_object
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.mock?
|
78
|
+
true
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
def self.create_params_key(params_hash)
|
83
|
+
if params_hash.empty?
|
84
|
+
'no_args'
|
85
|
+
else
|
86
|
+
params_hash.sort{ |a,b| a[0].to_s <=> b[0].to_s}.map do |arr|
|
87
|
+
arr[1] = '[FILE]' if arr[1].kind_of?(Koala::UploadableIO)
|
88
|
+
arr.join('=')
|
89
|
+
end.join('&')
|
90
|
+
end
|
91
|
+
end
|
94
92
|
end
|
95
93
|
end
|