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,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Koala::Facebook::GraphAPIMethods' do
4
+ before do
5
+ @api = Koala::Facebook::API.new(@token)
6
+ # app API
7
+ @app_id = KoalaTest.app_id
8
+ @app_access_token = KoalaTest.app_access_token
9
+ @app_api = Koala::Facebook::API.new(@app_access_token)
10
+ end
11
+
12
+ let(:dummy_response) { double("fake response", data: {}, status: 200, body: "", headers: {}) }
13
+
14
+ describe 'post-processing for' do
15
+ let(:result) { double("result") }
16
+ let(:post_processing) { lambda {|arg| {"result" => result, "args" => arg} } }
17
+
18
+ # Most API methods have the same signature, we test get_object representatively
19
+ # and the other methods which do some post-processing locally
20
+ context '#get_object' do
21
+ it 'returns result of block' do
22
+ allow(@api).to receive(:api).and_return(dummy_response)
23
+ expect(@api.get_object('barackobama', &post_processing)["result"]).to eq(result)
24
+ end
25
+
26
+ it "doesn't add token to received arguments" do
27
+ args = {}.freeze
28
+ expect(Koala).to receive(:make_request).and_return(dummy_response)
29
+ expect(@api.get_object('barackobama', args, &post_processing)["result"]).to eq(result)
30
+ end
31
+ end
32
+
33
+ context '#get_picture' do
34
+ it 'returns result of block' do
35
+ result_url = "a url"
36
+ allow(@api).to receive(:api).and_return(Koala::HTTPService::Response.new(200, {"data" => {"is_silhouette" => false, "url" => result_url}}.to_json, {}))
37
+ expect(@api.get_picture('koppel', &post_processing)["result"]).to eq(result)
38
+ end
39
+ end
40
+
41
+ context '#get_page_access_token' do
42
+ it 'returns result of block' do
43
+ token = Koala::MockHTTPService::APP_ACCESS_TOKEN
44
+ allow(@api).to receive(:api).and_return(
45
+ Koala::HTTPService::Response.new(200, {"access_token" => token}.to_json, {})
46
+ )
47
+ response = @api.get_page_access_token('facebook', &post_processing)
48
+ expect(response["args"]).to eq(token)
49
+ expect(response["result"]).to eq(result)
50
+ end
51
+ end
52
+ end
53
+
54
+ context '#graph_call' do
55
+ describe "the appsecret_proof option" do
56
+ let(:path) { '/path' }
57
+
58
+ it "is enabled by default if an app secret is present" do
59
+ api = Koala::Facebook::API.new(@token, "mysecret")
60
+ expect(api).to receive(:api).with(path, {}, 'get', :appsecret_proof => true).and_return(dummy_response)
61
+ api.graph_call(path)
62
+ end
63
+
64
+ it "can be disabled manually" do
65
+ api = Koala::Facebook::API.new(@token, "mysecret")
66
+ expect(api).to receive(:api).with(path, {}, 'get', hash_not_including(appsecret_proof: true)).and_return(dummy_response)
67
+ api.graph_call(path, {}, "get", appsecret_proof: false)
68
+ end
69
+
70
+ it "isn't included if no app secret is present" do
71
+ expect(@api).to receive(:api).with(path, {}, 'get', {}).and_return(dummy_response)
72
+ @api.graph_call(path)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,192 @@
1
+ require 'spec_helper'
2
+
3
+ describe Koala::Facebook::API::GraphCollection do
4
+ let(:paging){ {"paging" => true} }
5
+
6
+ before(:each) do
7
+ @headers = {'Content-Type' => 'application/json'}
8
+ @data = {
9
+ "data" => [1, 2, 'three'],
10
+ "paging" => paging,
11
+ "summary" => [3]
12
+ }
13
+ @result = Koala::HTTPService::Response.new(200, @data.to_json, @headers)
14
+ @api = Koala::Facebook::API.new("123")
15
+ @collection = Koala::Facebook::API::GraphCollection.new(@result, @api)
16
+ end
17
+
18
+ it "subclasses Array" do
19
+ expect(Koala::Facebook::API::GraphCollection.ancestors).to include(Array)
20
+ end
21
+
22
+ it "creates an array-like object" do
23
+ expect(Koala::Facebook::API::GraphCollection.new(@result, @api)).to be_an(Array)
24
+ end
25
+
26
+ it "contains the result data" do
27
+ @data["data"].each_with_index {|r, i| expect(@collection[i]).to eq(r)}
28
+ end
29
+
30
+ it "has a read-only paging attribute" do
31
+ expect(@collection.methods.map(&:to_sym)).to include(:paging)
32
+ expect(@collection.methods.map(&:to_sym)).not_to include(:paging=)
33
+ end
34
+
35
+ it "sets paging to results['paging']" do
36
+ expect(@collection.paging).to eq(@data["paging"])
37
+ end
38
+
39
+ it "sets summary to results['summary']" do
40
+ expect(@collection.summary).to eq(@data["summary"])
41
+ end
42
+
43
+ it "sets raw_response to the original results" do
44
+ expect(@collection.raw_response).to eq(@result.data)
45
+ end
46
+
47
+ it "sets the API to the provided API" do
48
+ expect(@collection.api).to eq(@api)
49
+ end
50
+
51
+ it "sets the headers correctly" do
52
+ expect(@collection.headers).to eq(@headers)
53
+ end
54
+
55
+ describe "when getting a whole page" do
56
+ before(:each) do
57
+ @second_page = {
58
+ "data" => ["second", "page", "data"],
59
+ "paging" => {}
60
+ }
61
+ @base = double("base")
62
+ @args = {"a" => 1}
63
+ @page_of_results = double("page of results")
64
+ @result = Koala::HTTPService::Response.new(200, @second_page.to_json, {})
65
+ @result.data
66
+ end
67
+
68
+ it "should return the previous page of results" do
69
+ expect(@collection).to receive(:previous_page_params).and_return([@base, @args])
70
+ expect(@api).to receive(:api).with(@base, @args, anything, anything).and_return(@result)
71
+ expect(Koala::Facebook::API::GraphCollection).to receive(:new).with(@result, @api).and_return(@page_of_results)
72
+ expect(@collection.previous_page).to eq(@page_of_results)
73
+ end
74
+
75
+ it "should return the next page of results" do
76
+ expect(@collection).to receive(:next_page_params).and_return([@base, @args])
77
+ expect(@api).to receive(:api).with(@base, @args, anything, anything).and_return(@result)
78
+ expect(Koala::Facebook::API::GraphCollection).to receive(:new).with(@result, @api).and_return(@page_of_results)
79
+
80
+ expect(@collection.next_page).to eq(@page_of_results)
81
+ end
82
+
83
+ it "should return nil it there are no other pages" do
84
+ %w{next previous}.each do |this|
85
+ expect(@collection).to receive("#{this}_page_params".to_sym).and_return(nil)
86
+ expect(@collection.send("#{this}_page")).to eq(nil)
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "when parsing page paramters" do
92
+ describe "#parse_page_url" do
93
+ it "should pass the url to the class method" do
94
+ url = double("url")
95
+ expect(Koala::Facebook::API::GraphCollection).to receive(:parse_page_url).with(url)
96
+ @collection.parse_page_url(url)
97
+ end
98
+
99
+ it "should return the result of the class method" do
100
+ parsed_content = double("parsed_content")
101
+ allow(Koala::Facebook::API::GraphCollection).to receive(:parse_page_url).and_return(parsed_content)
102
+ expect(@collection.parse_page_url(double("url"))).to eq(parsed_content)
103
+ end
104
+ end
105
+
106
+ describe ".parse_page_url" do
107
+ it "should return the base as the first array entry" do
108
+ base = "url_path"
109
+ expect(Koala::Facebook::API::GraphCollection.parse_page_url("http://facebook.com/#{base}?anything").first).to eq(base)
110
+ end
111
+
112
+ it "should return the arguments as a hash as the last array entry" do
113
+ args_hash = {"one" => "val_one", "two" => "val_two"}
114
+ expect(Koala::Facebook::API::GraphCollection.parse_page_url("http://facebook.com/anything?#{args_hash.map {|k,v| "#{k}=#{v}" }.join("&")}").last).to eq(args_hash)
115
+ end
116
+
117
+ it "works with non-.com addresses" do
118
+ base = "url_path"
119
+ args_hash = {"one" => "val_one", "two" => "val_two"}
120
+ expect(Koala::Facebook::API::GraphCollection.parse_page_url("http://facebook.com/#{base}?#{args_hash.map {|k,v| "#{k}=#{v}" }.join("&")}")).to eq([base, args_hash])
121
+ end
122
+
123
+ it "works with addresses with irregular characters" do
124
+ access_token = "appid123a|fdcba"
125
+ base, args_hash = Koala::Facebook::API::GraphCollection.parse_page_url("http://facebook.com/foo?token=#{access_token}")
126
+ expect(args_hash["token"]).to eq(access_token)
127
+ end
128
+ end
129
+ end
130
+
131
+ describe ".evaluate" do
132
+ it "returns the body of the original response if it's provided a Response with a non-hash data key" do
133
+ result = double('fake response')
134
+ allow(result).to receive(:is_a?).with(Hash).and_return(false)
135
+ allow(result).to receive(:data).and_return([])
136
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq([])
137
+ end
138
+
139
+ it "returns the original result if it's provided a nil result" do
140
+ result = nil
141
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq(result)
142
+ end
143
+
144
+ it "returns the original result body if the result doesn't have a data key" do
145
+ paging = {"paging" => {}}
146
+ result = Koala::HTTPService::Response.new(200, paging.to_json, {})
147
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq(paging)
148
+ end
149
+
150
+ it "returns the original result if the result's data key isn't an array" do
151
+ body = {"data" => {}, "paging" => {}}
152
+ result = Koala::HTTPService::Response.new(200, body.to_json, {})
153
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq(body)
154
+ end
155
+
156
+ it "returns a new GraphCollection of the result if it has an array data key and a paging key" do
157
+ body = {"data" => [], "paging" => {}}
158
+ result = Koala::HTTPService::Response.new(200, body.to_json, {})
159
+ expected = :foo
160
+ expect(Koala::Facebook::API::GraphCollection).to receive(:new).with(result, @api).and_return(expected)
161
+ expect(Koala::Facebook::API::GraphCollection.evaluate(result, @api)).to eq(expected)
162
+ end
163
+ end
164
+
165
+ describe "#next_page" do
166
+ let(:paging){ {"next" => "http://example.com/abc?a=2&b=3"} }
167
+
168
+ it "should get next page" do
169
+ expect(@api).to receive(:get_page).with(["abc", {"a" => "2", "b" => "3"}])
170
+ @collection.next_page
171
+ end
172
+
173
+ it "should get next page with extra parameters" do
174
+ expect(@api).to receive(:get_page).with(["abc", {"a" => "2", "b" => "3", "c" => "4"}])
175
+ @collection.next_page("c" => "4")
176
+ end
177
+ end
178
+
179
+ describe "#previous_page" do
180
+ let(:paging){ {"previous" => "http://example.com/?a=2&b=3"} }
181
+
182
+ it "should get previous page" do
183
+ expect(@api).to receive(:get_page).with(["", {"a" => "2", "b" => "3"}])
184
+ @collection.previous_page
185
+ end
186
+
187
+ it "should get previous page with extra parameters" do
188
+ expect(@api).to receive(:get_page).with(["", {"a" => "2", "b" => "3", "c" => "4"}])
189
+ @collection.previous_page("c" => "4")
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,147 @@
1
+ require 'spec_helper'
2
+
3
+ module Koala
4
+ module Facebook
5
+ RSpec.describe GraphErrorChecker do
6
+ it "defines a set of AUTHENTICATION_ERROR_CODES" do
7
+ expect(GraphErrorChecker::AUTHENTICATION_ERROR_CODES).to match_array([102, 190, 450, 452, 2500])
8
+ end
9
+
10
+ it "defines a set of DEBUG_HEADERS" do
11
+ expect(GraphErrorChecker::DEBUG_HEADERS).to match_array([
12
+ "x-fb-rev",
13
+ "x-fb-debug",
14
+ "x-fb-trace-id",
15
+ "x-business-use-case-usage",
16
+ "x-ad-account-usage",
17
+ "x-app-usage"
18
+ ])
19
+ end
20
+
21
+ describe "#error_if_appropriate" do
22
+ shared_examples_for :returning_no_error do |status|
23
+ it "returns no error" do
24
+ expect(GraphErrorChecker.new(status, "{}", {}).error_if_appropriate).to be_nil
25
+ end
26
+
27
+ it "ignores error data even if present" do
28
+ checker = GraphErrorChecker.new(
29
+ status,
30
+ {"error" => {"some" => "error"}},
31
+ {"x-fb-rev" => "data"}
32
+ )
33
+ expect(checker.error_if_appropriate).to be_nil
34
+ end
35
+ end
36
+
37
+ context "if the status is 2xx" do
38
+ it_should_behave_like :returning_no_error, 202
39
+ end
40
+
41
+ context "if the status is 3xx" do
42
+ it_should_behave_like :returning_no_error, 302
43
+ end
44
+
45
+ shared_examples_for :returns_an_error do |status|
46
+ let(:body) { "{}" }
47
+ let(:headers) { {} }
48
+ let(:error) { GraphErrorChecker.new(status, body, headers).error_if_appropriate }
49
+ it "returns a ClientError for a generic error" do
50
+ expect(error).to be_a(ClientError)
51
+ expect(error.response_body).to eq(body)
52
+ end
53
+
54
+ it "returns a ClientError even if the body can't be parsed as JSON" do
55
+ body.replace("hello from Chicago")
56
+ expect(error).to be_a(ClientError)
57
+ expect(error.response_body).to eq(body)
58
+ end
59
+
60
+ it "returns a ClientError if the body is empty" do
61
+ body.replace("")
62
+ expect(error).to be_a(ClientError)
63
+ expect(error.response_body).to eq(body)
64
+ end
65
+
66
+ it "returns a ClientError if the body is null" do
67
+ body.replace("null")
68
+ expect(error).to be_a(ClientError)
69
+ expect(error.response_body).to eq(body)
70
+ end
71
+
72
+ it "adds error data from the body" do
73
+ error_data = {
74
+ "type" => "FB error type",
75
+ "code" => "FB error code",
76
+ "error_subcode" => "FB error subcode",
77
+ "message" => "An error occurred!",
78
+ "error_user_msg" => "A user msg",
79
+ "error_user_title" => "usr title",
80
+ "fbtrace_id" => "fbtrace_id"
81
+ }
82
+ body.replace({"error" => error_data}.to_json)
83
+
84
+ expect(error.fb_error_trace_id).to eq(error_data["fbtrace_id"])
85
+ expect(error.fb_error_type).to eq(error_data["type"])
86
+ expect(error.fb_error_code).to eq(error_data["code"])
87
+ expect(error.fb_error_subcode).to eq(error_data["error_subcode"])
88
+ expect(error.fb_error_message).to eq(error_data["message"])
89
+ expect(error.fb_error_user_msg).to eq(error_data["error_user_msg"])
90
+ expect(error.fb_error_user_title).to eq(error_data["error_user_title"])
91
+ end
92
+
93
+ it "adds the FB debug headers to the errors" do
94
+ headers.merge!(
95
+ "x-fb-debug" => double("fb debug"),
96
+ "x-fb-rev" => double("fb rev"),
97
+ "x-fb-trace-id" => double("fb trace id"),
98
+ "x-business-use-case-usage" => { 'a' => 1, 'b' => 2 }.to_json,
99
+ "x-ad-account-usage" => { 'c' => 3, 'd' => 4 }.to_json,
100
+ "x-app-usage" => { 'e' => 5, 'f' => 6 }.to_json
101
+ )
102
+ expect(error.fb_error_debug_trace_id).to eq(headers["x-fb-trace-id"])
103
+ expect(error.fb_error_debug).to eq(headers["x-fb-debug"])
104
+ expect(error.fb_error_rev).to eq(headers["x-fb-rev"])
105
+ expect(error.fb_buc_usage).to eq({ 'a' => 1, 'b' => 2 })
106
+ expect(error.fb_ada_usage).to eq({ 'c' => 3, 'd' => 4 })
107
+ expect(error.fb_app_usage).to eq({ 'e' => 5, 'f' => 6 })
108
+ end
109
+
110
+ it "logs if one of the FB debug headers can't be parsed" do
111
+ headers.merge!(
112
+ "x-app-usage" => '{invalid:json}'
113
+ )
114
+
115
+ expect(Koala::Utils.logger).to receive(:error).with("JSON::ParserError: expected object key, got 'invalid:json}' at line 1 column 2 while parsing x-app-usage = {invalid:json}")
116
+ expect(error.fb_app_usage).to eq(nil)
117
+ end
118
+
119
+ context "it returns an AuthenticationError" do
120
+ it "if FB says it's an OAuthException and it has no code" do
121
+ body.replace({"error" => {"type" => "OAuthException"}}.to_json)
122
+ expect(error).to be_an(AuthenticationError)
123
+ end
124
+
125
+ GraphErrorChecker::AUTHENTICATION_ERROR_CODES.each do |error_code|
126
+ it "if FB says it's an OAuthException and it has code #{error_code}" do
127
+ body.replace({"error" => {"type" => "OAuthException", "code" => error_code}}.to_json)
128
+ expect(error).to be_an(AuthenticationError)
129
+ end
130
+ end
131
+ end
132
+
133
+ # Note: I'm not sure why this behavior was implemented, it may be incorrect. To
134
+ # investigate.
135
+ it "doesn't return an AuthenticationError if FB says it's an OAuthException but the code doesn't match" do
136
+ body.replace({"error" => {"type" => "OAuthException", "code" => 2499}}.to_json)
137
+ expect(error).to be_an(ClientError)
138
+ end
139
+ end
140
+
141
+ context "if the status is 4xx" do
142
+ it_should_behave_like :returns_an_error, 400
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,250 @@
1
+ require "spec_helper"
2
+
3
+ module Koala
4
+ module HTTPService
5
+ RSpec.describe Request do
6
+ let(:path) { "/foo" }
7
+ let(:args) { {"a" => 2, "b" => 3, "access_token" => "a_token"} }
8
+ let(:verb) { "get" }
9
+ let(:options) { {an: :option} }
10
+ let(:request) { Request.new(path: path, args: args, verb: verb, options: options) }
11
+
12
+ shared_context post: true do
13
+ let(:verb) { "post" }
14
+ end
15
+
16
+ shared_context delete: true do
17
+ let(:verb) { "delete" }
18
+ end
19
+
20
+ shared_context put: true do
21
+ let(:verb) { "put" }
22
+ end
23
+
24
+ it "raises an ArgumentError if no verb is supplied" do
25
+ expect { Request.new(path: path) }.to raise_exception(ArgumentError)
26
+ end
27
+
28
+ it "raises an ArgumentError if no path is supplied" do
29
+ expect { Request.new(verb: verb) }.to raise_exception(ArgumentError)
30
+ end
31
+
32
+ describe "#verb" do
33
+ it "returns get for get" do
34
+ expect(Request.new(path: path, verb: "get").verb).to eq("get")
35
+ end
36
+
37
+ it "returns post for post" do
38
+ expect(Request.new(path: path, verb: "post").verb).to eq("post")
39
+ end
40
+
41
+ it "returns post for put" do
42
+ expect(Request.new(path: path, verb: "put").verb).to eq("post")
43
+ end
44
+
45
+ it "returns post for delete" do
46
+ expect(Request.new(path: path, verb: "delete").verb).to eq("post")
47
+ end
48
+ end
49
+
50
+ describe "#path" do
51
+ context "if there's no API version" do
52
+ it "returns the path" do
53
+ expect(request.path).to eq(path)
54
+ end
55
+ end
56
+
57
+ context "if there's an API version" do
58
+ shared_examples_for :including_the_version do
59
+ it "prefixes the version appropriately when the path starts with /" do
60
+ expect(Request.new(path: "/foo", verb: "get", options: options).path).to eq("/#{version}/foo")
61
+ end
62
+
63
+ it "prefixes the version appropriately when the path doesn't start with /" do
64
+ expect(Request.new(path: "foo", verb: "get", options: options).path).to eq("/#{version}/foo")
65
+ end
66
+ end
67
+
68
+ context "set by Koala.config" do
69
+ let(:version) { "v2.7" }
70
+
71
+ before :each do
72
+ Koala.configure do |config|
73
+ config.api_version = version
74
+ end
75
+ end
76
+
77
+ it_should_behave_like :including_the_version
78
+ end
79
+
80
+ context "set in options" do
81
+ let(:version) { "v2.8" }
82
+
83
+ let(:options) { {api_version: version} }
84
+
85
+ it_should_behave_like :including_the_version
86
+ end
87
+
88
+ context "for versions without a ." do
89
+ let(:version) { "v21" }
90
+
91
+ let(:options) { {api_version: version} }
92
+
93
+ it_should_behave_like :including_the_version
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#post_args" do
99
+ it "returns {} for get" do
100
+ expect(request.post_args).to eq({})
101
+ end
102
+
103
+ it "returns args for post", :post do
104
+ expect(request.post_args).to eq(args)
105
+ end
106
+
107
+ it "returns args + method: delete for delete", :delete do
108
+ expect(request.post_args).to eq(args.merge(method: "delete"))
109
+ end
110
+
111
+ it "returns args + method: put for put", :put do
112
+ expect(request.post_args).to eq(args.merge(method: "put"))
113
+ end
114
+
115
+ it "turns any UploadableIOs to UploadIOs" do
116
+ # technically this is done for all requests, but you don't send GET requests with files
117
+ upload_io = double("UploadIO")
118
+ u = Koala::HTTPService::UploadableIO.new("/path/to/stuff", "img/jpg")
119
+ allow(u).to receive(:to_upload_io).and_return(upload_io)
120
+ request = Request.new(path: path, verb: "post", args: {an_upload: u})
121
+ expect(request.post_args[:an_upload]).to eq(upload_io)
122
+ end
123
+ end
124
+
125
+ describe "#json?" do
126
+ it "returns false if the option for JSON isn't set" do
127
+ expect(request).not_to be_json
128
+ end
129
+
130
+ context "if JSON is set" do
131
+ let(:options) { {format: :json} }
132
+
133
+ it "returns true if it is" do
134
+ expect(request).to be_json
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "#options" do
140
+ it "returns the options provided" do
141
+ expect(request.options).to include(options)
142
+ end
143
+
144
+ it "merges in any global options" do
145
+ global_options = {some: :opts}
146
+ HTTPService.http_options = global_options
147
+ results = request.options
148
+ HTTPService.http_options = {}
149
+ expect(results).to include(global_options)
150
+ end
151
+
152
+ it "overwrites any global options with equivalent ones" do
153
+ options = {foo: :bar}
154
+ HTTPService.http_options = {foo: 2}
155
+ results = Request.new(path: path, args: args, verb: verb, options: options).options
156
+ HTTPService.http_options = {}
157
+ expect(results).to include(options)
158
+ end
159
+
160
+ describe "get parameters" do
161
+ it "includes the parameters for a get " do
162
+ expect(request.options[:params]).to eq(args)
163
+ end
164
+
165
+ it "returns {} for post", :post do
166
+ expect(request.options[:params]).to eq({})
167
+ end
168
+
169
+ it "returns {} for delete", :delete do
170
+ expect(request.options[:params]).to eq({})
171
+ end
172
+
173
+ it "returns {} for put", :put do
174
+ expect(request.options[:params]).to eq({})
175
+ end
176
+ end
177
+
178
+ describe "ssl options" do
179
+ it "includes the default SSL options even if there's no access token" do
180
+ request_options = Request.new(path: path, args: args.delete_if {|k, _v| k == "access_token"}, verb: verb, options: options).options
181
+ expect(request_options).to include(use_ssl: true, ssl: {verify: true})
182
+ end
183
+
184
+ context "if there is an access_token" do
185
+ it "includes the default SSL options" do
186
+ expect(request.options).to include(use_ssl: true, ssl: {verify: true})
187
+ end
188
+
189
+ it "incorporates any provided SSL options" do
190
+ new_ssl_options = {an: :option2}
191
+ request_options = Request.new(path: path, args: args, verb: verb, options: options.merge(ssl: new_ssl_options)).options
192
+ expect(request_options[:ssl]).to include(new_ssl_options)
193
+ end
194
+
195
+ it "overrides default SSL options with what's provided" do
196
+ new_ssl_options = {use_ssl: false, ssl:{verify: :dunno}}
197
+ request_options = Request.new(path: path, args: args, verb: verb, options: options.merge(new_ssl_options)).options
198
+ expect(request_options).to include(new_ssl_options)
199
+ end
200
+ end
201
+ end
202
+
203
+ describe "server" do
204
+ describe "with no options" do
205
+ it "returns the graph server by default" do
206
+ expect(request.server).to eq("https://graph.facebook.com")
207
+ end
208
+
209
+ it "uses another base server if specified" do
210
+ Koala.config.graph_server = "foo"
211
+ expect(request.server).to eq("https://foo")
212
+ end
213
+
214
+ context "if there is no access token" do
215
+ let(:args) { {"a" => "b"} }
216
+
217
+ it "uses https" do
218
+ expect(request.server).to eq("https://graph.facebook.com")
219
+ end
220
+ end
221
+
222
+ context "if options[:use_ssl] is false" do
223
+ let(:options) { {use_ssl: false} }
224
+
225
+ it "uses http" do
226
+ expect(request.server).to eq("http://graph.facebook.com")
227
+ end
228
+ end
229
+
230
+ context "if options[:beta]" do
231
+ let(:options) { {beta: true} }
232
+
233
+ it "returns https if options[:beta]" do
234
+ expect(request.server).to eq("https://graph.beta.facebook.com")
235
+ end
236
+ end
237
+
238
+ context "if options[:video]" do
239
+ let(:options) { {video: true} }
240
+
241
+ it "returns https if options[:video]" do
242
+ expect(request.server).to eq("https://graph-video.facebook.com")
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ module Koala::HTTPService
4
+ RSpec.describe Response do
5
+ describe "#data" do
6
+ it "returns nothing if the body is empty" do
7
+ expect(Response.new(200, "", {}).data).to be_nil
8
+ end
9
+
10
+ it "parses the JSON if there's a body" do
11
+ result = {"foo" => "bar"}
12
+ expect(Response.new(200, result.to_json, {}).data).to eq(result)
13
+ end
14
+
15
+ it "allows raw values" do
16
+ expect(Response.new(200, "true", {}).data).to be true
17
+ end
18
+
19
+ it "raises a ParserError if it's invalid JSON" do
20
+ expect { Response.new(200, "{", {}).data }.to raise_exception(JSON::ParserError)
21
+ end
22
+ end
23
+ end
24
+ end