koala 0.4 → 3.7.0

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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +32 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +1 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +25 -0
  7. data/ISSUE_TEMPLATE +25 -0
  8. data/LICENSE +22 -0
  9. data/Manifest +32 -5
  10. data/PULL_REQUEST_TEMPLATE +11 -0
  11. data/Rakefile +12 -12
  12. data/changelog.md +781 -0
  13. data/code_of_conduct.md +74 -0
  14. data/koala.gemspec +28 -24
  15. data/lib/koala/api/batch_operation.rb +86 -0
  16. data/lib/koala/api/graph_api_methods.rb +504 -0
  17. data/lib/koala/api/graph_batch_api.rb +167 -0
  18. data/lib/koala/api/graph_collection.rb +129 -0
  19. data/lib/koala/api/graph_error_checker.rb +72 -0
  20. data/lib/koala/api.rb +159 -0
  21. data/lib/koala/configuration.rb +56 -0
  22. data/lib/koala/errors.rb +126 -0
  23. data/lib/koala/http_service/request.rb +133 -0
  24. data/lib/koala/http_service/response.rb +20 -0
  25. data/lib/koala/http_service/uploadable_io.rb +183 -0
  26. data/lib/koala/http_service.rb +108 -0
  27. data/lib/koala/oauth.rb +342 -0
  28. data/lib/koala/realtime_updates.rb +151 -0
  29. data/lib/koala/test_users.rb +189 -0
  30. data/lib/koala/utils.rb +41 -0
  31. data/lib/koala/version.rb +3 -0
  32. data/lib/koala.rb +51 -291
  33. data/readme.md +269 -21
  34. data/spec/cases/api_spec.rb +362 -0
  35. data/spec/cases/configuration_spec.rb +11 -0
  36. data/spec/cases/error_spec.rb +143 -0
  37. data/spec/cases/graph_api_batch_spec.rb +788 -0
  38. data/spec/cases/graph_api_spec.rb +76 -0
  39. data/spec/cases/graph_collection_spec.rb +192 -0
  40. data/spec/cases/graph_error_checker_spec.rb +147 -0
  41. data/spec/cases/http_service/request_spec.rb +250 -0
  42. data/spec/cases/http_service/response_spec.rb +24 -0
  43. data/spec/cases/http_service_spec.rb +280 -0
  44. data/spec/cases/koala_spec.rb +57 -0
  45. data/spec/cases/koala_test_spec.rb +5 -0
  46. data/spec/cases/oauth_spec.rb +647 -0
  47. data/spec/cases/realtime_updates_spec.rb +327 -0
  48. data/spec/cases/test_users_spec.rb +383 -0
  49. data/spec/cases/uploadable_io_spec.rb +266 -0
  50. data/spec/cases/utils_spec.rb +55 -0
  51. data/spec/fixtures/beach.jpg +0 -0
  52. data/spec/fixtures/cat.m4v +0 -0
  53. data/spec/fixtures/facebook_data.yml +63 -0
  54. data/spec/fixtures/mock_facebook_responses.yml +483 -0
  55. data/spec/fixtures/vcr_cassettes/app_test_accounts.yml +97 -0
  56. data/spec/fixtures/vcr_cassettes/friend_list_next_page.yml +121 -0
  57. data/spec/integration/graph_collection_spec.rb +24 -0
  58. data/spec/spec_helper.rb +25 -0
  59. data/spec/support/custom_matchers.rb +28 -0
  60. data/spec/support/graph_api_shared_examples.rb +534 -0
  61. data/spec/support/koala_test.rb +251 -0
  62. data/spec/support/mock_http_service.rb +140 -0
  63. data/spec/support/uploadable_io_shared_examples.rb +70 -0
  64. metadata +206 -62
  65. data/CHANGELOG +0 -24
  66. data/init.rb +0 -2
  67. data/lib/http_services.rb +0 -60
  68. data/test/facebook_data.yml +0 -5
  69. data/test/koala/facebook_no_access_token_tests.rb +0 -119
  70. data/test/koala/facebook_with_access_token_tests.rb +0 -106
  71. data/test/koala_tests.rb +0 -30
@@ -0,0 +1,383 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala::Facebook::TestUsers" do
4
+ before :all do
5
+ # get oauth data
6
+ @app_id = KoalaTest.app_id
7
+ @secret = KoalaTest.secret
8
+ @app_access_token = KoalaTest.app_access_token
9
+
10
+ @test_users = Koala::Facebook::TestUsers.new({:app_access_token => @app_access_token, :app_id => @app_id})
11
+
12
+ # check OAuth data
13
+ unless @app_id && @secret && @app_access_token
14
+ raise Exception, "Must supply OAuth app id, secret, app_access_token, and callback to run live subscription tests!"
15
+ end
16
+ end
17
+
18
+ after :each do
19
+ # clean up any test users
20
+ # Facebook only allows us 500 test users per app, so we have to clean up
21
+ # This would be a good place to clean up and accumulate all of them for
22
+ # later deletion.
23
+ unless KoalaTest.mock_interface? || @stubbed
24
+ ((@network || []) + [@user1, @user2]).each do |u|
25
+ puts "Unable to delete test user #{u.inspect}" if u && !(@test_users.delete(u) rescue false)
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "when initializing" do
31
+ # basic initialization
32
+ it "initializes properly with an app_id and an app_access_token" do
33
+ test_users = Koala::Facebook::TestUsers.new(:app_id => @app_id, :app_access_token => @app_access_token)
34
+ expect(test_users).to be_a(Koala::Facebook::TestUsers)
35
+ end
36
+
37
+ # init with secret / fetching the token
38
+ it "initializes properly with an app_id and a secret" do
39
+ test_users = Koala::Facebook::TestUsers.new(:app_id => @app_id, :secret => @secret)
40
+ expect(test_users).to be_a(Koala::Facebook::TestUsers)
41
+ end
42
+
43
+ context "with global defaults" do
44
+ let(:app_id) { :app_id }
45
+ let(:app_secret) { :app_secret }
46
+ let(:app_access_token) { :app_access_token }
47
+
48
+ before :each do
49
+ Koala.configure do |config|
50
+ config.app_id = app_id
51
+ config.app_secret = app_secret
52
+ config.app_access_token = app_access_token
53
+ end
54
+ end
55
+
56
+ it "defaults to the configured data if not otherwise provided" do
57
+ test_users = Koala::Facebook::TestUsers.new
58
+ expect(test_users.app_id).to eq(app_id)
59
+ expect(test_users.secret).to eq(app_secret)
60
+ expect(test_users.app_access_token).to eq(app_access_token)
61
+ end
62
+
63
+ it "lets you override app_id" do
64
+ other_value = :another_id
65
+ test_users = Koala::Facebook::TestUsers.new(app_id: other_value)
66
+ expect(test_users.app_id).to eq(other_value)
67
+ end
68
+
69
+ it "lets you override secret" do
70
+ other_value = :another_secret
71
+ test_users = Koala::Facebook::TestUsers.new(secret: other_value)
72
+ expect(test_users.secret).to eq(other_value)
73
+ end
74
+
75
+ it "lets you override app_id" do
76
+ other_value = :another_token
77
+ test_users = Koala::Facebook::TestUsers.new(app_access_token: other_value)
78
+ expect(test_users.app_access_token).to eq(other_value)
79
+ end
80
+ end
81
+
82
+
83
+ it "uses the OAuth class to fetch a token when provided an app_id and a secret" do
84
+ oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
85
+ token = oauth.get_app_access_token
86
+ expect(oauth).to receive(:get_app_access_token).and_return(token)
87
+ expect(Koala::Facebook::OAuth).to receive(:new).with(@app_id, @secret).and_return(oauth)
88
+ Koala::Facebook::TestUsers.new(:app_id => @app_id, :secret => @secret)
89
+ end
90
+
91
+ # attributes
92
+ it "allows read access to app_id, app_access_token, and secret" do
93
+ # in Ruby 1.9, .method returns symbols
94
+ expect(Koala::Facebook::TestUsers.instance_methods.map(&:to_sym)).to include(:app_id)
95
+ expect(Koala::Facebook::TestUsers.instance_methods.map(&:to_sym)).not_to include(:app_id=)
96
+ end
97
+
98
+ it "allows read access to app_access_token" do
99
+ # in Ruby 1.9, .method returns symbols
100
+ expect(Koala::Facebook::TestUsers.instance_methods.map(&:to_sym)).to include(:app_access_token)
101
+ expect(Koala::Facebook::TestUsers.instance_methods.map(&:to_sym)).not_to include(:app_access_token=)
102
+ end
103
+
104
+ it "allows read access to secret" do
105
+ # in Ruby 1.9, .method returns symbols
106
+ expect(Koala::Facebook::TestUsers.instance_methods.map(&:to_sym)).to include(:secret)
107
+ expect(Koala::Facebook::TestUsers.instance_methods.map(&:to_sym)).not_to include(:secret=)
108
+ end
109
+
110
+ it "allows read access to api" do
111
+ # in Ruby 1.9, .method returns symbols
112
+ expect(Koala::Facebook::TestUsers.instance_methods.map(&:to_sym)).to include(:api)
113
+ expect(Koala::Facebook::TestUsers.instance_methods.map(&:to_sym)).not_to include(:api=)
114
+ end
115
+ end
116
+
117
+ describe "when used without network" do
118
+ # TEST USER MANAGEMENT
119
+
120
+ describe "#create" do
121
+ it "creates a test user when not given installed" do
122
+ result = @test_users.create(false)
123
+ @user1 = result["id"]
124
+ expect(result).to be_a(Hash)
125
+ expect(result["id"] && result["access_token"] && result["login_url"]).to be_truthy
126
+ end
127
+
128
+ it "creates a test user when not given installed, ignoring permissions" do
129
+ result = @test_users.create(false, "read_stream")
130
+ @user1 = result["id"]
131
+ expect(result).to be_a(Hash)
132
+ expect(result["id"] && result["access_token"] && result["login_url"]).to be_truthy
133
+ end
134
+
135
+ it "accepts permissions as a string" do
136
+ expect(@test_users.api).to receive(:graph_call).with(anything, hash_including("permissions" => "read_stream,publish_stream"), anything, anything)
137
+ result = @test_users.create(true, "read_stream,publish_stream")
138
+ end
139
+
140
+ it "accepts permissions as an array" do
141
+ expect(@test_users.api).to receive(:graph_call).with(anything, hash_including("permissions" => "read_stream,publish_stream"), anything, anything)
142
+ result = @test_users.create(true, ["read_stream", "publish_stream"])
143
+ end
144
+
145
+ it "creates a test user when given installed and a permission" do
146
+ result = @test_users.create(true, "read_stream")
147
+ @user1 = result["id"]
148
+ expect(result).to be_a(Hash)
149
+ expect(result["id"] && result["access_token"] && result["login_url"]).to be_truthy
150
+ end
151
+
152
+ it "lets you specify other graph arguments, like uid and access token" do
153
+ args = {:uid => "some test user ID", :owner_access_token => "some owner access token"}
154
+ expect(@test_users.api).to receive(:graph_call).with(anything, hash_including(args), anything, anything)
155
+ @test_users.create(true, nil, args)
156
+ end
157
+
158
+ it "lets you specify http options that get passed through to the graph call" do
159
+ options = {:some_http_option => true}
160
+ expect(@test_users.api).to receive(:graph_call).with(anything, anything, anything, options)
161
+ @test_users.create(true, nil, {}, options)
162
+ end
163
+ end
164
+
165
+ describe "#list" do
166
+ before :each do
167
+ @user1 = @test_users.create(true, "read_stream")
168
+ @user2 = @test_users.create(true, "read_stream,user_interests")
169
+ end
170
+
171
+ it "lists test users" do
172
+ result = @test_users.list
173
+ expect(result).to be_an(Array)
174
+ first_user, second_user = result[0], result[1]
175
+ expect(first_user["id"] && first_user["access_token"] && first_user["login_url"]).to be_truthy
176
+ expect(second_user["id"] && second_user["access_token"] && second_user["login_url"]).to be_truthy
177
+ end
178
+
179
+ it "accepts http options" do
180
+ @stubbed = true
181
+ options = {:some_http_option => true}
182
+ expect(@test_users.api).to receive(:graph_call).with(anything, anything, anything, options)
183
+ @test_users.list(options)
184
+ end
185
+ end
186
+
187
+ describe "#delete" do
188
+ before :each do
189
+ @user1 = @test_users.create(true, "read_stream")
190
+ @user2 = @test_users.create(true, "read_stream,user_interests")
191
+ end
192
+
193
+ it "deletes a user by id" do
194
+ expect(@test_users.delete(@user1['id'])).to be_truthy
195
+ @user1 = nil
196
+ end
197
+
198
+ it "deletes a user by hash" do
199
+ expect(@test_users.delete(@user2)).to be_truthy
200
+ @user2 = nil
201
+ end
202
+
203
+ it "does not delete users when provided a false ID" do
204
+ expect { @test_users.delete("#{@user1['id']}1") }.to raise_exception(Koala::Facebook::APIError)
205
+ end
206
+
207
+ it "lets you specify http options that get passed through to the graph call" do
208
+ options = {:some_http_option => true}
209
+ # technically this goes through delete_object, but this makes it less brittle
210
+ @stubbed = true
211
+ expect(@test_users.api).to receive(:graph_call).with(anything, anything, anything, options)
212
+ @test_users.delete("user", options)
213
+ end
214
+ end
215
+
216
+ describe "#delete_all" do
217
+ it "deletes the batch API to deleten all users found by the list commnand" do
218
+ array = 200.times.collect { {"id" => rand}}
219
+ expect(@test_users).to receive(:list).and_return(array, [])
220
+ batch_api = double("batch API")
221
+ allow(@test_users.api).to receive(:batch).and_yield(batch_api)
222
+ array.each {|item| expect(batch_api).to receive(:delete_object).with(item["id"]) }
223
+ @test_users.delete_all
224
+ end
225
+
226
+ it "accepts http options that get passed to both list and the batch call" do
227
+ options = {:some_http_option => true}
228
+ expect(@test_users).to receive(:list).with(options).and_return([{"id" => rand}], [])
229
+ expect(@test_users.api).to receive(:batch).with(options)
230
+ @test_users.delete_all(options)
231
+ end
232
+
233
+ it "breaks if Facebook sends back the same list twice" do
234
+ list = [{"id" => rand}]
235
+ allow(@test_users).to receive(:list).and_return(list)
236
+ expect(@test_users.api).to receive(:batch).once
237
+ @test_users.delete_all
238
+ end
239
+
240
+ it "breaks if the same list comes back, even if the hashes differ" do
241
+ list1 = [{"id" => 123}]
242
+ list2 = [{"id" => 123, "name" => "foo"}]
243
+ allow(@test_users).to receive(:list).twice.and_return(list1, list2)
244
+ expect(@test_users.api).to receive(:batch).once
245
+ @test_users.delete_all
246
+ end
247
+ end
248
+
249
+ describe "#update" do
250
+ before :each do
251
+ @updates = {:name => "Foo Baz"}
252
+ # we stub out :graph_call, but still need to be able to delete the users
253
+ @test_users2 = Koala::Facebook::TestUsers.new(:app_id => @test_users.app_id, :app_access_token => @test_users.app_access_token)
254
+ end
255
+
256
+ it "makes a POST with the test user Graph API " do
257
+ @user1 = @test_users2.create(true)
258
+ expect(@test_users2.api).to receive(:graph_call).with(anything, anything, "post", anything)
259
+ @test_users2.update(@user1, @updates)
260
+ end
261
+
262
+ it "makes a request to the test user with the update params " do
263
+ @user1 = @test_users2.create(true)
264
+ expect(@test_users2.api).to receive(:graph_call).with(@user1["id"], @updates, anything, anything)
265
+ @test_users2.update(@user1, @updates)
266
+ end
267
+
268
+ it "accepts an options hash" do
269
+ options = {:some_http_option => true}
270
+ @stubbed = true
271
+ expect(@test_users2.api).to receive(:graph_call).with(anything, anything, anything, options)
272
+ @test_users2.update("foo", @updates, options)
273
+ end
274
+
275
+ it "works" do
276
+ @user1 = @test_users.create(true)
277
+ @test_users.update(@user1, @updates)
278
+ user_info = Koala::Facebook::API.new(@user1["access_token"]).get_object(@user1["id"])
279
+ expect(user_info["name"]).to eq(@updates[:name])
280
+ end
281
+ end
282
+
283
+ describe "#befriend" do
284
+ before :each do
285
+ @user1 = @test_users.create(true, "read_stream")
286
+ @user2 = @test_users.create(true, "read_stream,user_interests")
287
+ end
288
+
289
+ it "makes two users into friends with string hashes" do
290
+ result = @test_users.befriend(@user1, @user2)
291
+ expect(result).to be_truthy
292
+ end
293
+
294
+ it "makes two users into friends with symbol hashes" do
295
+ new_user_1 = {}
296
+ @user1.each_pair {|k, v| new_user_1[k.to_sym] = v}
297
+ new_user_2 = {}
298
+ @user2.each_pair {|k, v| new_user_2[k.to_sym] = v}
299
+
300
+ result = @test_users.befriend(new_user_1, new_user_2)
301
+ expect(result).to be_truthy
302
+ end
303
+
304
+ it "accepts http options passed to both calls" do
305
+ options = {:some_http_option => true}
306
+ # should come twice, once for each user
307
+ @stubbed = true
308
+ expect(Koala.http_service).to receive(:make_request).twice do |request|
309
+ expect(request.raw_options).to eq(options)
310
+ Koala::HTTPService::Response.new(200, "{}", {})
311
+ end
312
+ @test_users.befriend(@user1, @user2, options)
313
+ end
314
+
315
+ it "includes the secret, generating the appsecret_proof in both calls if provided" do
316
+ # should come twice, once for each user
317
+ @stubbed = true
318
+ test_users = Koala::Facebook::TestUsers.new({:secret => @secret, :app_id => @app_id})
319
+ expect(Koala.http_service).to receive(:make_request).twice do |request|
320
+ expect(request.post_args).to include("appsecret_proof")
321
+ Koala::HTTPService::Response.new(200, "{}", {})
322
+ end
323
+ test_users.befriend(@user1, @user2)
324
+ end
325
+ end
326
+ end # when used without network
327
+
328
+ describe "#test_user_accounts_path" do
329
+ it "returns the app_id/accounts/test-users" do
330
+ expect(@test_users.test_user_accounts_path).to eq("/#{@app_id}/accounts/test-users")
331
+ end
332
+ end
333
+
334
+ describe "when creating a network of friends" do
335
+ before :each do
336
+ @network = []
337
+
338
+ if KoalaTest.mock_interface?
339
+ id_counter = 999999900
340
+ allow(@test_users).to receive(:create) do
341
+ id_counter += 1
342
+ {"id" => id_counter, "access_token" => @token, "login_url" => "https://www.facebook.com/platform/test_account.."}
343
+ end
344
+ allow(@test_users).to receive(:befriend).and_return(true)
345
+ allow(@test_users).to receive(:delete).and_return(true)
346
+ end
347
+ end
348
+
349
+ describe "tests that create users" do
350
+ it "creates a 5 person network" do
351
+ size = 5
352
+ @network = @test_users.create_network(size)
353
+ expect(@network).to be_a(Array)
354
+ expect(@network.size).to eq(size)
355
+ end
356
+ end
357
+
358
+ it "has no built-in network size limit" do
359
+ times = 100
360
+ expect(@test_users).to receive(:create).exactly(times).times
361
+ allow(@test_users).to receive(:befriend)
362
+ @test_users.create_network(times)
363
+ end
364
+
365
+ it "passes on the installed and permissions parameters to create" do
366
+ perms = ["read_stream", "offline_access"]
367
+ installed = false
368
+ count = 25
369
+ expect(@test_users).to receive(:create).exactly(count).times.with(installed, perms, anything, anything)
370
+ allow(@test_users).to receive(:befriend)
371
+ @test_users.create_network(count, installed, perms)
372
+ end
373
+
374
+ it "accepts http options that are passed to both the create and befriend calls" do
375
+ count = 25
376
+ options = {:some_http_option => true}
377
+ expect(@test_users).to receive(:create).exactly(count).times.with(anything, anything, anything, options).and_return({})
378
+ # there are more befriends than creates, but we don't need to do the extra work to calculate out the exact #
379
+ expect(@test_users).to receive(:befriend).at_least(count).times.with(anything, anything, options)
380
+ @test_users.create_network(count, true, "", options)
381
+ end
382
+ end # when creating network
383
+ end # describe Koala TestUsers
@@ -0,0 +1,266 @@
1
+ # fake MIME::Types
2
+ module Koala::MIME
3
+ module Types
4
+ def self.type_for(type)
5
+ # this should be faked out in tests
6
+ nil
7
+ end
8
+ end
9
+ end
10
+
11
+ describe "Koala::HTTPService::UploadableIO" do
12
+ def rails_3_mocks
13
+ tempfile = double('Tempfile', :path => "foo")
14
+ uploaded_file = double('ActionDispatch::Http::UploadedFile',
15
+ :content_type => true,
16
+ :tempfile => tempfile,
17
+ :original_filename => "bar"
18
+ )
19
+ allow(tempfile).to receive(:respond_to?).with(:path).and_return(true)
20
+
21
+ [tempfile, uploaded_file]
22
+ end
23
+
24
+ def sinatra_mocks
25
+ {:type => "type", :tempfile => "Tempfile", :filename => "foo.bar"}
26
+ end
27
+
28
+ describe "the constructor" do
29
+ describe "when given a file path" do
30
+ before(:each) do
31
+ @path = BEACH_BALL_PATH
32
+ @koala_io_params = [@path]
33
+ end
34
+
35
+ describe "and a content type" do
36
+ before :each do
37
+ @stub_type = double("image/jpg")
38
+ @koala_io_params.concat([@stub_type])
39
+ end
40
+
41
+ it "returns an UploadIO with the same file path" do
42
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).io_or_path).to eq(@path)
43
+ end
44
+
45
+ it "returns an UploadIO with the same content type" do
46
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).content_type).to eq(@stub_type)
47
+ end
48
+
49
+ it "returns an UploadIO with the file's name" do
50
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).filename).to eq(File.basename(@path))
51
+ end
52
+ end
53
+
54
+ describe "and no content type" do
55
+ it_should_behave_like "determining a mime type"
56
+ end
57
+ end
58
+
59
+ describe "when given a File object" do
60
+ before(:each) do
61
+ @file = File.open(BEACH_BALL_PATH)
62
+ @koala_io_params = [@file]
63
+ end
64
+
65
+ describe "and a content type" do
66
+ before :each do
67
+ @koala_io_params.concat(["image/jpg"])
68
+ end
69
+
70
+ it "returns an UploadIO with the same io" do
71
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).io_or_path).to eq(@koala_io_params[0])
72
+ end
73
+
74
+ it "returns an UploadableIO with the same content_type" do
75
+ content_stub = @koala_io_params[1] = double('Content Type')
76
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).content_type).to eq(content_stub)
77
+ end
78
+
79
+ it "returns an UploadableIO with the right filename" do
80
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).filename).to eq(File.basename(@file.path))
81
+ end
82
+ end
83
+
84
+ describe "and no content type" do
85
+ it_should_behave_like "determining a mime type"
86
+ end
87
+ end
88
+
89
+
90
+ describe "when given a Tempfile object" do
91
+ before(:each) do
92
+ @file = Tempfile.new('coucou')
93
+ @koala_io_params = [@file]
94
+ end
95
+
96
+ describe "and a content type" do
97
+ before :each do
98
+ @koala_io_params.concat(["image/jpg"])
99
+ end
100
+
101
+ it "returns an UploadIO with the same io" do
102
+ #Problem comparing Tempfile in Ruby 1.8, REE and Rubinius mode 1.8
103
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).io_or_path.path).to eq(@koala_io_params[0].path)
104
+ end
105
+
106
+ it "returns an UploadableIO with the same content_type" do
107
+ content_stub = @koala_io_params[1] = double('Content Type')
108
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).content_type).to eq(content_stub)
109
+ end
110
+
111
+ it "returns an UploadableIO with the right filename" do
112
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).filename).to eq(File.basename(@file.path))
113
+ end
114
+ end
115
+
116
+ describe "and no content type" do
117
+ it_should_behave_like "determining a mime type"
118
+ end
119
+ end
120
+
121
+ describe "when given an IO object" do
122
+ before(:each) do
123
+ @io = StringIO.open("abcdefgh")
124
+ @koala_io_params = [@io]
125
+ end
126
+
127
+ describe "and a content type" do
128
+ before :each do
129
+ @koala_io_params.concat(["image/jpg"])
130
+ end
131
+
132
+ it "returns an UploadableIO with the same io" do
133
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).io_or_path).to eq(@koala_io_params[0])
134
+ end
135
+
136
+ it "returns an UploadableIO with the same content_type" do
137
+ content_stub = @koala_io_params[1] = double('Content Type')
138
+ expect(Koala::HTTPService::UploadableIO.new(*@koala_io_params).content_type).to eq(content_stub)
139
+ end
140
+ end
141
+
142
+ describe "and no content type" do
143
+ it "raises an exception" do
144
+ expect { Koala::HTTPService::UploadableIO.new(*@koala_io_params) }.to raise_exception(Koala::KoalaError)
145
+ end
146
+ end
147
+ end
148
+
149
+ describe "when given a Rails 3 ActionDispatch::Http::UploadedFile" do
150
+ before(:each) do
151
+ @tempfile, @uploaded_file = rails_3_mocks
152
+ end
153
+
154
+ it "gets the path from the tempfile associated with the UploadedFile" do
155
+ expected_path = double('Tempfile')
156
+ expect(@tempfile).to receive(:path).and_return(expected_path)
157
+ expect(Koala::HTTPService::UploadableIO.new(@uploaded_file).io_or_path).to eq(expected_path)
158
+ end
159
+
160
+ it "gets the content type via the content_type method" do
161
+ expected_content_type = double('Content Type')
162
+ expect(@uploaded_file).to receive(:content_type).and_return(expected_content_type)
163
+ expect(Koala::HTTPService::UploadableIO.new(@uploaded_file).content_type).to eq(expected_content_type)
164
+ end
165
+
166
+ it "gets the filename from the UploadedFile" do
167
+ expect(Koala::HTTPService::UploadableIO.new(@uploaded_file).filename).to eq(@uploaded_file.original_filename)
168
+ end
169
+ end
170
+
171
+ describe "when given a Sinatra file parameter hash" do
172
+ before(:each) do
173
+ @file_hash = sinatra_mocks
174
+ end
175
+
176
+ it "gets the io_or_path from the :tempfile key" do
177
+ expected_file = double('File')
178
+ @file_hash[:tempfile] = expected_file
179
+
180
+ uploadable = Koala::HTTPService::UploadableIO.new(@file_hash)
181
+ expect(uploadable.io_or_path).to eq(expected_file)
182
+ end
183
+
184
+ it "gets the content type from the :type key" do
185
+ expected_content_type = double('Content Type')
186
+ @file_hash[:type] = expected_content_type
187
+
188
+ uploadable = Koala::HTTPService::UploadableIO.new(@file_hash)
189
+ expect(uploadable.content_type).to eq(expected_content_type)
190
+ end
191
+
192
+ it "gets the content type from the :type key" do
193
+ uploadable = Koala::HTTPService::UploadableIO.new(@file_hash)
194
+ expect(uploadable.filename).to eq(@file_hash[:filename])
195
+ end
196
+ end
197
+
198
+ describe "for files with with recognizable MIME types" do
199
+ # what that means is tested below
200
+ it "should accept a file object alone" do
201
+ params = [BEACH_BALL_PATH]
202
+ expect { Koala::HTTPService::UploadableIO.new(*params) }.not_to raise_exception
203
+ end
204
+
205
+ it "should accept a file path alone" do
206
+ params = [BEACH_BALL_PATH]
207
+ expect { Koala::HTTPService::UploadableIO.new(*params) }.not_to raise_exception
208
+ end
209
+ end
210
+ end
211
+
212
+ describe "getting an UploadableIO" do
213
+ before(:each) do
214
+ @upload_io = double("UploadIO")
215
+ allow(UploadIO).to receive(:new).with(anything, anything, anything).and_return(@upload_io)
216
+ end
217
+
218
+ context "if no filename was provided" do
219
+ it "should call the constructor with the content type, file name, and a dummy file name" do
220
+ expect(UploadIO).to receive(:new).with(BEACH_BALL_PATH, "content/type", anything).and_return(@upload_io)
221
+ expect(Koala::HTTPService::UploadableIO.new(BEACH_BALL_PATH, "content/type").to_upload_io).to eq(@upload_io)
222
+ end
223
+ end
224
+
225
+ context "if a filename was provided" do
226
+ it "should call the constructor with the content type, file name, and the filename" do
227
+ filename = "file"
228
+ expect(UploadIO).to receive(:new).with(BEACH_BALL_PATH, "content/type", filename).and_return(@upload_io)
229
+ Koala::HTTPService::UploadableIO.new(BEACH_BALL_PATH, "content/type", filename).to_upload_io
230
+ end
231
+ end
232
+ end
233
+
234
+ describe "getting a file" do
235
+ it "returns the File if initialized with a file" do
236
+ f = File.new(BEACH_BALL_PATH)
237
+ expect(Koala::HTTPService::UploadableIO.new(f).to_file).to eq(f)
238
+ end
239
+
240
+ it "should open up and return a file corresponding to the path if io_or_path is a path" do
241
+ result = double("File")
242
+ expect(File).to receive(:open).with(BEACH_BALL_PATH).and_return(result)
243
+ expect(Koala::HTTPService::UploadableIO.new(BEACH_BALL_PATH).to_file).to eq(result)
244
+ end
245
+ end
246
+
247
+ describe ".binary_content?" do
248
+ it "returns true for Rails 3 file uploads" do
249
+ expect(Koala::HTTPService::UploadableIO.binary_content?(rails_3_mocks.last)).to be_truthy
250
+ end
251
+
252
+ it "returns true for Sinatra file uploads" do
253
+ expect(Koala::HTTPService::UploadableIO.binary_content?(rails_3_mocks.last)).to be_truthy
254
+ end
255
+
256
+ it "returns true for File objects" do
257
+ expect(Koala::HTTPService::UploadableIO.binary_content?(File.open(BEACH_BALL_PATH))).to be_truthy
258
+ end
259
+
260
+ it "returns false for everything else" do
261
+ expect(Koala::HTTPService::UploadableIO.binary_content?(StringIO.new)).to be_falsey
262
+ expect(Koala::HTTPService::UploadableIO.binary_content?(BEACH_BALL_PATH)).to be_falsey
263
+ expect(Koala::HTTPService::UploadableIO.binary_content?(nil)).to be_falsey
264
+ end
265
+ end
266
+ end # describe UploadableIO