Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #277 from arsduo/test-suite-refactor

[2.0] Restructure Test Suite
  • Loading branch information...
commit e7b31ce801d7b49918dd40002729b1bd4760fb74 2 parents 95c1c8a + 5095ef2
@arsduo authored
Showing with 1,659 additions and 1,599 deletions.
  1. +3 −0  changelog.md
  2. +15 −0 lib/koala/api/graph_api.rb
  3. +20 −20 lib/koala/api/graph_collection.rb
  4. +1 −1  lib/koala/http_service.rb
  5. +0 −666 spec/cases/graph_api_batch_spec.rb
  6. +0 −68 spec/cases/graph_api_spec.rb
  7. +7 −5 spec/fixtures/mock_facebook_responses.yml
  8. +668 −0 spec/lib/api/batch_spec.rb
  9. +54 −0 spec/lib/api/delete_spec.rb
  10. +295 −0 spec/lib/api/read_spec.rb
  11. +244 −0 spec/lib/api/write_spec.rb
  12. +0 −8 spec/{cases → lib}/api_spec.rb
  13. 0  spec/{cases → lib}/error_spec.rb
  14. +178 −0 spec/lib/graph_api_spec.rb
  15. 0  spec/{cases → lib}/graph_collection_spec.rb
  16. 0  spec/{cases → lib}/http_service_spec.rb
  17. 0  spec/{cases → lib}/koala_spec.rb
  18. 0  spec/{cases → lib}/koala_test_spec.rb
  19. 0  spec/{cases → lib}/legacy_spec.rb
  20. 0  spec/{cases → lib}/multipart_request_spec.rb
  21. +1 −2  spec/{cases → lib}/oauth_spec.rb
  22. 0  spec/{cases → lib}/realtime_updates_spec.rb
  23. +5 −1 spec/{cases → lib}/test_users_spec.rb
  24. +71 −0 spec/{cases → lib}/uploadable_io_spec.rb
  25. 0  spec/{cases → lib}/utils_spec.rb
  26. +18 −20 spec/spec_helper.rb
  27. +0 −660 spec/support/graph_api_shared_examples.rb
  28. +0 −44 spec/support/json_testing_fix.rb
  29. +2 −1  spec/support/koala_test.rb
  30. +77 −33 spec/support/mock_http_service.rb
  31. +0 −70 spec/support/uploadable_io_shared_examples.rb
View
3  changelog.md
@@ -5,6 +5,9 @@ Internal improvements:
* CHANGED: Gem version restrictions have been removed, and versions updated.
* CHANGED: How support files are loaded in spec_helper has been improved.
+Testing improvements:
+* FIXED: MockHTTPService compares Ruby objects rather than strings.
+
v1.6
====
View
15 lib/koala/api/graph_api.rb
@@ -368,6 +368,21 @@ def get_page_access_token(id, args = {}, options = {}, &block)
block ? block.call(access_token) : access_token
end
+ # Get an access token information
+ # The access token used to instantiate the API object needs to be
+ # the app access token or a valid User Access Token from a developer of the app.
+ # See https://developers.facebook.com/docs/howtos/login/debugging-access-tokens/#step1
+ #
+ # @param input_token the access token you want to inspect
+ # @param block (see Koala::Facebook::API#api)
+ #
+ # @return a JSON array containing data and a map of fields
+ def debug_token(input_token, &block)
+ access_token_info = graph_call("debug_token", {:input_token => input_token})
+
+ block ? block.call(access_token_info) : access_token_info
+ end
+
# Fetches the comments from fb:comments widgets for a given set of URLs (array or comma-separated string).
# See https://developers.facebook.com/blog/post/490.
#
View
40 lib/koala/api/graph_collection.rb
@@ -6,22 +6,22 @@ class API
# A light wrapper for collections returned from the Graph API.
# It extends Array to allow you to page backward and forward through
# result sets, and providing easy access to paging information.
- class GraphCollection < Array
-
+ class GraphCollection < Array
+
# The raw paging information from Facebook (next/previous URLs).
attr_reader :paging
# @return [Koala::Facebook::GraphAPI] the api used to make requests.
attr_reader :api
# The entire raw response from Facebook.
attr_reader :raw_response
-
+
# Initialize the array of results and store various additional paging-related information.
- #
+ #
# @param response the response from Facebook (a hash whose "data" key is an array)
# @param api the Graph {Koala::Facebook::API API} instance to use to make calls
# (usually the API that made the original call).
#
- # @return [Koala::Facebook::GraphCollection] an initialized GraphCollection
+ # @return [Koala::Facebook::GraphCollection] an initialized GraphCollection
# whose paging, raw_response, and api attributes are populated.
def initialize(response, api)
super response["data"]
@@ -31,32 +31,32 @@ def initialize(response, api)
end
# @private
- # Turn the response into a GraphCollection if they're pageable;
+ # Turn the response into a GraphCollection if they're pageable;
# if not, return the original response.
# The Ads API (uniquely so far) returns a hash rather than an array when queried
- # with get_connections.
+ # with get_connections.
def self.evaluate(response, api)
response.is_a?(Hash) && response["data"].is_a?(Array) ? self.new(response, api) : response
end
-
+
# Retrieve the next page of results.
- #
+ #
# @return a GraphCollection array of additional results (an empty array if there are no more results)
def next_page
base, args = next_page_params
base ? @api.get_page([base, args]) : nil
end
-
+
# Retrieve the previous page of results.
- #
+ #
# @return a GraphCollection array of additional results (an empty array if there are no earlier results)
def previous_page
base, args = previous_page_params
base ? @api.get_page([base, args]) : nil
end
-
- # Arguments that can be sent to {Koala::Facebook::API#graph_call} to retrieve the next page of results.
- #
+
+ # Arguments that can be sent to {Koala::Facebook::API#graph_call} to retrieve the next page of results.
+ #
# @example
# @api.graph_call(*collection.next_page_params)
#
@@ -64,9 +64,9 @@ def previous_page
def next_page_params
@paging && @paging["next"] ? parse_page_url(@paging["next"]) : nil
end
-
- # Arguments that can be sent to {Koala::Facebook::API#graph_call} to retrieve the previous page of results.
- #
+
+ # Arguments that can be sent to {Koala::Facebook::API#graph_call} to retrieve the previous page of results.
+ #
# @example
# @api.graph_call(*collection.previous_page_params)
#
@@ -74,7 +74,7 @@ def next_page_params
def previous_page_params
@paging && @paging["previous"] ? parse_page_url(@paging["previous"]) : nil
end
-
+
# @private
def parse_page_url(url)
GraphCollection.parse_page_url(url)
@@ -102,9 +102,9 @@ def self.parse_page_url(url)
end
end
end
-
+
# @private
- # legacy support for when GraphCollection lived directly under Koala::Facebook
+ # legacy support for when GraphCollection lived directly under Koala::Facebook
GraphCollection = API::GraphCollection
end
end
View
2  lib/koala/http_service.rb
@@ -218,4 +218,4 @@ def self.deprecated_interface
Faraday.default_adapter = :net_http
end
end
-end
+end
View
666 spec/cases/graph_api_batch_spec.rb
@@ -1,666 +0,0 @@
-require 'spec_helper'
-
-describe "Koala::Facebook::GraphAPI in batch mode" do
-
- before :each do
- @api = Koala::Facebook::API.new(@token)
- # app API
- @app_id = KoalaTest.app_id
- @app_access_token = KoalaTest.app_access_token
- @app_api = Koala::Facebook::API.new(@app_access_token)
- end
-
- describe Koala::Facebook::GraphBatchAPI::BatchOperation do
- before :each do
- @args = {
- :url => "my url",
- :args => {:a => 2, :b => 3},
- :method => "get",
- :access_token => "12345",
- :http_options => {},
- :post_processing => lambda { }
- }
- end
-
- describe ".new" do
- it "makes http_options accessible" do
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).http_options.should == @args[:http_options]
- end
-
- it "makes post_processing accessible" do
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).post_processing.should == @args[:post_processing]
- end
-
- it "makes access_token accessible" do
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).access_token.should == @args[:access_token]
- end
-
- it "doesn't change the original http_options" do
- @args[:http_options][:name] = "baz2"
- expected = @args[:http_options].dup
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)
- @args[:http_options].should == expected
- end
-
- it "leaves the file array nil by default" do
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).files.should be_nil
- end
-
- it "raises a KoalaError if no access token supplied" do
- expect { Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args.merge(:access_token => nil)) }.to raise_exception(Koala::KoalaError)
- end
-
- describe "when supplied binary files" do
- before :each do
- @binary = stub("Binary file")
- @uploadable_io = stub("UploadableIO 1")
-
- @batch_queue = []
- Koala::Facebook::GraphAPI.stub(:batch_calls).and_return(@batch_queue)
-
- Koala::UploadableIO.stub(:new).with(@binary).and_return(@uploadable_io)
- Koala::UploadableIO.stub(:binary_content?).and_return(false)
- Koala::UploadableIO.stub(:binary_content?).with(@binary).and_return(true)
- Koala::UploadableIO.stub(:binary_content?).with(@uploadable_io).and_return(true)
- @uploadable_io.stub(:is_a?).with(Koala::UploadableIO).and_return(true)
-
- @args[:method] = "post" # files are always post
- end
-
- it "adds binary files to the files attribute as UploadableIOs" do
- @args[:args].merge!("source" => @binary)
- batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
- batch_op.files.should_not be_nil
- batch_op.files.find {|k, v| v == @uploadable_io}.should_not be_nil
- end
-
- it "works if supplied an UploadableIO as an argument" do
- # as happens with put_picture at the moment
- @args[:args].merge!("source" => @uploadable_io)
- batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
- batch_op.files.should_not be_nil
- batch_op.files.find {|k, v| v == @uploadable_io}.should_not be_nil
- end
-
- it "assigns each binary parameter unique name" do
- @args[:args].merge!("source" => @binary, "source2" => @binary)
- batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
- # if the name wasn't unique, there'd just be one item
- batch_op.files.should have(2).items
- end
-
- it "assigns each binary parameter unique name across batch requests" do
- @args[:args].merge!("source" => @binary, "source2" => @binary)
- batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
- # simulate the batch operation, since it's used in determination
- @batch_queue << batch_op
- batch_op2 = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
- @batch_queue << batch_op2
- # if the name wasn't unique, we should have < 4 items since keys would be the same
- batch_op.files.merge(batch_op2.files).should have(4).items
- end
-
- it "removes the value from the arguments" do
- @args[:args].merge!("source" => @binary)
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:body].should_not =~ /source=/
- end
- end
-
- end
-
- describe "#to_batch_params" do
- describe "handling arguments and URLs" do
- shared_examples_for "request with no body" do
- it "adds the args to the URL string, with ? if no args previously present" do
- test_args = "foo"
- @args[:url] = url = "/"
- Koala.http_service.stub(:encode_params).and_return(test_args)
-
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == "#{url}?#{test_args}"
- end
-
- it "adds the args to the URL string, with & if args previously present" do
- test_args = "foo"
- @args[:url] = url = "/?a=2"
- Koala.http_service.stub(:encode_params).and_return(test_args)
-
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == "#{url}&#{test_args}"
- end
-
- it "adds nothing to the URL string if there are no args to be added" do
- @args[:args] = {}
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(@args[:access_token])[:relative_url].should == @args[:url]
- end
-
- it "adds nothing to the body" do
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:body].should be_nil
- end
- end
-
- shared_examples_for "requests with a body param" do
- it "sets the body to the encoded args string, if there are args" do
- test_args = "foo"
- Koala.http_service.stub(:encode_params).and_return(test_args)
-
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:body].should == test_args
- end
-
- it "does not set the body if there are no args" do
- test_args = ""
- Koala.http_service.stub(:encode_params).and_return(test_args)
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:body].should be_nil
- end
-
-
- it "doesn't change the url" do
- test_args = "foo"
- Koala.http_service.stub(:encode_params).and_return(test_args)
-
- Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == @args[:url]
- end
- end
-
- context "for get operations" do
- before :each do
- @args[:method] = :get
- end
-
- it_should_behave_like "request with no body"
- end
-
- context "for delete operations" do
- before :each do
- @args[:method] = :delete
- end
-
- it_should_behave_like "request with no body"
- end
-
- context "for get operations" do
- before :each do
- @args[:method] = :put
- end
-
- it_should_behave_like "requests with a body param"
- end
-
- context "for delete operations" do
- before :each do
- @args[:method] = :post
- end
-
- it_should_behave_like "requests with a body param"
- end
- end
-
- it "includes the access token if the token is not the main one for the request" do
- params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)
- params[:relative_url].should =~ /access_token=#{@args[:access_token]}/
- end
-
- it "includes the other arguments if the token is not the main one for the request" do
- @args[:args] = {:a => 2}
- params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)
- params[:relative_url].should =~ /a=2/
- end
-
- it "does not include the access token if the token is the main one for the request" do
- params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(@args[:access_token])
- params[:relative_url].should_not =~ /access_token=#{@args[:access_token]}/
- end
-
- it "includes the other arguments if the token is the main one for the request" do
- @args[:args] = {:a => 2}
- params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(@args[:access_token])
- params[:relative_url].should =~ /a=2/
- end
-
- it "includes any arguments passed as http_options[:batch_args]" do
- batch_args = {:name => "baz", :headers => {:some_param => true}}
- @args[:http_options][:batch_args] = batch_args
- params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)
- params.should include(batch_args)
- end
-
- it "includes the method" do
- params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(@args[:access_token])
- params[:method].should == @args[:method].to_s
- end
-
- it "works with nil http_options" do
- expect { Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args.merge(:http_options => nil)).to_batch_params(nil) }.not_to raise_exception
- end
-
- it "works with nil args" do
- expect { Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args.merge(:args => nil)).to_batch_params(nil) }.not_to raise_exception
- end
-
- describe "with binary files" do
- before :each do
- @binary = stub("Binary file")
- Koala::UploadableIO.stub(:binary_content?).and_return(false)
- Koala::UploadableIO.stub(:binary_content?).with(@binary).and_return(true)
- @uploadable_io = stub("UploadableIO")
- Koala::UploadableIO.stub(:new).with(@binary).and_return(@uploadable_io)
- @uploadable_io.stub(:is_a?).with(Koala::UploadableIO).and_return(true)
-
- @batch_queue = []
- Koala::Facebook::GraphAPI.stub(:batch_calls).and_return(@batch_queue)
-
- @args[:method] = "post" # files are always post
- end
-
- it "adds file identifiers as attached_files in a comma-separated list" do
- @args[:args].merge!("source" => @binary, "source2" => @binary)
- batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
- file_ids = batch_op.files.find_all {|k, v| v == @uploadable_io}.map {|k, v| k}
- params = batch_op.to_batch_params(nil)
- params[:attached_files].should == file_ids.join(",")
- end
- end
- end
-
- end
-
- describe "GraphAPI batch interface" do
- it "returns nothing for a batch operation" do
- Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, "[]", {}))
- @api.batch do |batch_api|
- batch_api.get_object('me').should be_nil
- end
- end
-
- describe "#batch" do
- before :each do
- @fake_response = Koala::HTTPService::Response.new(200, "[]", {})
- Koala.stub(:make_request).and_return(@fake_response)
- end
-
- describe "making the request" do
- context "with no calls" do
- it "does not make any requests if batch_calls is empty" do
- Koala.should_not_receive(:make_request)
- @api.batch {|batch_api|}
- end
-
- it "returns []" do
- @api.batch {|batch_api|}.should == []
- end
- end
-
- it "includes the first operation's access token as the main one in the args" do
- access_token = "foo"
- Koala.should_receive(:make_request).with(anything, hash_including("access_token" => access_token), anything, anything).and_return(@fake_response)
- Koala::Facebook::API.new(access_token).batch do |batch_api|
- batch_api.get_object('me')
- batch_api.get_object('me', {}, {'access_token' => 'bar'})
- end
- end
-
- it "sets args['batch'] to a json'd map of all the batch params" do
- access_token = "bar"
- op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(:access_token => access_token, :method => :get, :url => "/")
- op.stub(:to_batch_params).and_return({:a => 2})
- Koala::Facebook::GraphBatchAPI::BatchOperation.stub(:new).and_return(op)
-
- # two requests should generate two batch operations
- expected = MultiJson.dump([op.to_batch_params(access_token), op.to_batch_params(access_token)])
- Koala.should_receive(:make_request).with(anything, hash_including("batch" => expected), anything, anything).and_return(@fake_response)
- Koala::Facebook::API.new(access_token).batch do |batch_api|
- batch_api.get_object('me')
- batch_api.get_object('me')
- end
- end
-
- it "adds any files from the batch operations to the arguments" do
- # stub the batch operation
- # we test above to ensure that files are properly assimilated into the BatchOperation instance
- # right now, we want to make sure that batch_api handles them properly
- @key = "file0_0"
- @uploadable_io = stub("UploadableIO")
- batch_op = stub("Koala Batch Operation", :files => {@key => @uploadable_io}, :to_batch_params => {}, :access_token => "foo")
- Koala::Facebook::GraphBatchAPI::BatchOperation.stub(:new).and_return(batch_op)
-
- Koala.should_receive(:make_request).with(anything, hash_including(@key => @uploadable_io), anything, anything).and_return(@fake_response)
- Koala::Facebook::API.new("bar").batch do |batch_api|
- batch_api.put_picture("path/to/file", "image/jpeg")
- end
- end
-
- it "preserves operation order" do
- access_token = "bar"
- # two requests should generate two batch operations
- Koala.should_receive(:make_request) do |url, args, method, options|
- # test the batch operations to make sure they appear in the right order
- (args ||= {})["batch"].should =~ /.*me\/farglebarg.*otheruser\/bababa/
- @fake_response
- end
- Koala::Facebook::API.new(access_token).batch do |batch_api|
- batch_api.get_connections('me', "farglebarg")
- batch_api.get_connections('otheruser', "bababa")
- end
- end
-
- it "makes a POST request" do
- Koala.should_receive(:make_request).with(anything, anything, "post", anything).and_return(@fake_response)
- Koala::Facebook::API.new("foo").batch do |batch_api|
- batch_api.get_object('me')
- end
- end
-
- it "makes a request to /" do
- Koala.should_receive(:make_request).with("/", anything, anything, anything).and_return(@fake_response)
- Koala::Facebook::API.new("foo").batch do |batch_api|
- batch_api.get_object('me')
- end
- end
-
- it "includes any http options specified at the top level" do
- http_options = {"a" => "baz"}
- Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(http_options)).and_return(@fake_response)
- Koala::Facebook::API.new("foo").batch(http_options) do |batch_api|
- batch_api.get_object('me')
- end
- end
- end
-
- describe "processing the request" do
- it "returns the result headers as a hash if http_component is headers" do
- Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
- result = @api.batch do |batch_api|
- batch_api.get_object(KoalaTest.user1, {}, :http_component => :headers)
- end
- result[0].should == {"Content-Type" => "text/javascript; charset=UTF-8"}
- end
-
- describe "if it errors" do
- it "raises an APIError if the response is not 200" do
- Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(500, "[]", {}))
- expect {
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
- }.to raise_exception(Koala::Facebook::APIError)
- end
-
- it "raises a BadFacebookResponse if the body is empty" do
- Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, "", {}))
- expect {
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
- }.to raise_exception(Koala::Facebook::BadFacebookResponse)
- end
-
- context "with the old style" do
- before :each do
- Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(400, '{"error_code":190,"error_description":"Error validating access token."}', {}))
- end
-
- it "throws an error" do
- expect {
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
- }.to raise_exception(Koala::Facebook::APIError)
- end
-
- it "passes all the error details" do
- begin
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
- rescue Koala::Facebook::APIError => err
- err.fb_error_code.should == 190
- err.fb_error_message.should == "Error validating access token."
- err.http_status == 400
- err.response_body == '{"error_code":190,"error_description":"Error validating access token."}'
- end
- end
- end
-
- context "with the new style" do
- before :each do
- Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(400, '{"error":{"message":"Request 0 cannot depend on an unresolved request with name f. Requests can only depend on preceding requests","type":"GraphBatchException"}}', {}))
- end
-
- it "throws an error" do
- expect {
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
- }.to raise_exception(Koala::Facebook::APIError)
- end
-
- it "passes all the error details" do
- begin
- Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
- rescue Koala::Facebook::APIError => err
- err.fb_error_type.should == "GraphBatchException"
- err.fb_error_message.should == "Request 0 cannot depend on an unresolved request with name f. Requests can only depend on preceding requests"
- err.http_status == 400
- err.response_body == '{"error":{"message":"Request 0 cannot depend on an unresolved request with name f. Requests can only depend on preceding requests","type":"GraphBatchException"}}'
- end
- end
- end
- end
-
- it "returns the result status if http_component is status" do
- Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
- result = @api.batch do |batch_api|
- batch_api.get_object(KoalaTest.user1, {}, :http_component => :status)
- end
- result[0].should == 203
- end
- end
-
- it "is thread safe" do
- # ensure batch operations on one thread don't affect those on another
- thread_one_count = 0
- thread_two_count = 0
- first_count = 20
- second_count = 10
-
- Koala.stub(:make_request).and_return(@fake_response)
-
- thread1 = Thread.new do
- @api.batch do |batch_api|
- first_count.times {|i| batch_api.get_object("me"); sleep(0.01) }
- thread_one_count = batch_api.batch_calls.count
- end
- end
-
- thread2 = Thread.new do
- @api.batch do |batch_api|
- second_count.times {|i| batch_api.get_object("me"); sleep(0.01) }
- thread_two_count = batch_api.batch_calls.count
- end
- end
-
- thread1.join
- thread2.join
-
- thread_one_count.should == first_count
- thread_two_count.should == second_count
- end
- end
- end
-
- describe "usage tests" do
- it "gets two results at once" do
- me, koppel = @api.batch do |batch_api|
- batch_api.get_object('me')
- batch_api.get_object(KoalaTest.user1)
- end
- me['id'].should_not be_nil
- koppel['id'].should_not be_nil
- end
-
- it 'makes mixed calls inside of a batch' do
- me, friends = @api.batch do |batch_api|
- batch_api.get_object('me')
- batch_api.get_connections('me', 'friends')
- end
- friends.should be_a(Koala::Facebook::GraphCollection)
- end
-
- it 'turns pageable results into GraphCollections' do
- me, friends = @api.batch do |batch_api|
- batch_api.get_object('me')
- batch_api.get_connections('me', 'friends')
- end
- me['id'].should_not be_nil
- friends.should be_an(Array)
- end
-
- it 'makes a get_picture call inside of a batch' do
- pictures = @api.batch do |batch_api|
- batch_api.get_picture('me')
- end
- puts pictures.inspect
- pictures.first.should =~ /http\:\/\// # works both live & stubbed
- end
-
- it "handles requests for two different tokens" do
- me, insights = @api.batch do |batch_api|
- batch_api.get_object('me')
- batch_api.get_connections(@app_id, 'insights', {}, {"access_token" => @app_api.access_token})
- end
- me['id'].should_not be_nil
- insights.should be_an(Array)
- end
-
- it "inserts errors in the appropriate place, without breaking other results" do
- failed_call, koppel = @api.batch do |batch_api|
- batch_api.get_connection("2", "invalidconnection")
- batch_api.get_object(KoalaTest.user1, {}, {"access_token" => @app_api.access_token})
- end
- failed_call.should be_a(Koala::Facebook::ClientError)
- koppel["id"].should_not be_nil
- end
-
- it "handles different request methods" do
- result = @api.put_wall_post("Hello, world, from the test suite batch API!")
- wall_post = result["id"]
-
- wall_post, koppel = @api.batch do |batch_api|
- batch_api.put_like(wall_post)
- batch_api.delete_object(wall_post)
- end
- end
-
- it "allows FQL" do
- result = @api.batch do |batch_api|
- batch_api.graph_call("method/fql.query", {:query=>"select first_name from user where uid=#{KoalaTest.user1_id}"}, "post")
- end
-
- fql_result = result[0]
- fql_result[0].should be_a(Hash)
- fql_result[0]["first_name"].should == "Alex"
- end
-
- describe 'with post-processing callback' do
- let(:me_result) { stub("me result") }
- let(:friends_result) { stub("friends result") }
-
- let(:me_callback) { lambda {|data| me_result } }
- let(:friends_callback) { lambda {|data| friends_result } }
-
- it 'calls the callback with the appropriate data' do
- me_callback.should_receive(:call).with(hash_including(
- 'id' => KoalaTest.user1
- ))
- friends_callback.should_receive(:call).with([
- hash_including('id' => KoalaTest.user2)
- ])
- @api.batch do |batch_api|
- batch_api.get_object('me', &me_callback)
- batch_api.get_connections('me', 'friends', &friends_callback)
- end
- end
-
- it 'passes GraphCollections, not raw data' do
- friends_callback.should_receive(:call).with(kind_of(Koala::Facebook::API::GraphCollection))
- @api.batch do |batch_api|
- batch_api.get_object('me')
- batch_api.get_connections('me', 'friends', &friends_callback)
- end
- end
-
- it "returns the result of the callback" do
- @api.batch do |batch_api|
- batch_api.get_object('me', &me_callback)
- batch_api.get_connections('me', 'friends', &friends_callback)
- end.should == [me_result, friends_result]
- end
- end
-
- describe "binary files" do
- it "posts binary files" do
- file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
-
- Koala::Facebook::GraphBatchAPI::BatchOperation.instance_variable_set(:@identifier, 0)
- result = @api.batch do |batch_api|
- batch_api.put_picture(file)
- end
-
- @temporary_object_id = result[0]["id"]
- @temporary_object_id.should_not be_nil
- end
-
- it "posts binary files with multiple requests" do
- file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
- file2 = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
-
- Koala::Facebook::GraphBatchAPI::BatchOperation.instance_variable_set(:@identifier, 0)
- results = @api.batch do |batch_api|
- batch_api.put_picture(file)
- batch_api.put_picture(file2, {}, KoalaTest.user1)
- end
- results[0]["id"].should_not be_nil
- results[1]["id"].should_not be_nil
- end
- end
-
- describe "relating requests" do
- it "allows you create relationships between requests without omit_response_on_success" do
- results = @api.batch do |batch_api|
- batch_api.get_connections("me", "friends", {:limit => 5}, :batch_args => {:name => "get-friends"})
- batch_api.get_objects("{result=get-friends:$.data.*.id}")
- end
-
- results[0].should be_nil
- results[1].should be_an(Hash)
- end
-
- it "allows you create relationships between requests with omit_response_on_success" do
- results = @api.batch do |batch_api|
- batch_api.get_connections("me", "friends", {:limit => 5}, :batch_args => {:name => "get-friends", :omit_response_on_success => false})
- batch_api.get_objects("{result=get-friends:$.data.*.id}")
- end
-
- results[0].should be_an(Array)
- results[1].should be_an(Hash)
- end
-
- it "allows you to create dependencies" do
- me, koppel = @api.batch do |batch_api|
- batch_api.get_object("me", {}, :batch_args => {:name => "getme"})
- batch_api.get_object(KoalaTest.user1, {}, :batch_args => {:depends_on => "getme"})
- end
-
- me.should be_nil # gotcha! it's omitted because it's a successfully-executed dependency
- koppel["id"].should_not be_nil
- end
-
- it "properly handles dependencies that fail" do
- failed_call, koppel = @api.batch do |batch_api|
- batch_api.get_connections("2", "invalidconnection", {}, :batch_args => {:name => "getdata"})
- batch_api.get_object(KoalaTest.user1, {}, :batch_args => {:depends_on => "getdata"})
- end
-
- failed_call.should be_a(Koala::Facebook::ClientError)
- koppel.should be_nil
- end
-
- it "throws an error for badly-constructed request relationships" do
- expect {
- @api.batch do |batch_api|
- batch_api.get_connections("me", "friends", {:limit => 5})
- batch_api.get_objects("{result=i-dont-exist:$.data.*.id}")
- end
- }.to raise_exception(Koala::Facebook::ClientError)
- end
- end
- end
-end
View
68 spec/cases/graph_api_spec.rb
@@ -1,68 +0,0 @@
-require 'spec_helper'
-
-describe 'Koala::Facebook::GraphAPIMethods' do
- before do
- @api = Koala::Facebook::API.new(@token)
- # app API
- @app_id = KoalaTest.app_id
- @app_access_token = KoalaTest.app_access_token
- @app_api = Koala::Facebook::API.new(@app_access_token)
- end
-
- describe 'post-processing for' do
- let(:post_processing) { lambda {} }
-
- # Most API methods have the same signature, we test get_object representatively
- # and the other methods which do some post-processing locally
- context '#get_object' do
- it 'returns result of block' do
- result = {"id" => 1, "name" => 1, "updated_time" => 1}
- @api.stub(:api).and_return(result)
- post_processing.should_receive(:call).
- with(result).and_return('new result')
- @api.get_object('koppel', &post_processing).should == 'new result'
- end
- end
-
- context '#get_picture' do
- it 'returns result of block' do
- result = "http://facebook.com/"
- @api.stub(:api).and_return("Location" => result)
- post_processing.should_receive(:call).
- with(result).and_return('new result')
- @api.get_picture('lukeshepard', &post_processing).should == 'new result'
- end
- end
-
- context '#fql_multiquery' do
- before do
- @api.should_receive(:get_object).and_return([
- {"name" => "query1", "fql_result_set" => [{"id" => 123}]},
- {"name" => "query2", "fql_result_set" => ["id" => 456]}
- ])
- end
-
- it 'is called with resolved response' do
- resolved_result = {
- 'query1' => [{'id' => 123}],
- 'query2' => [{'id'=>456}]
- }
- post_processing.should_receive(:call).
- with(resolved_result).and_return('id'=>'123', 'id'=>'456')
- @api.fql_multiquery({}, &post_processing).should ==
- {'id'=>'123', 'id'=>'456'}
- end
- end
-
- context '#get_page_access_token' do
- it 'returns result of block' do
- token = Koala::MockHTTPService::APP_ACCESS_TOKEN
- @api.stub(:api).and_return("access_token" => token)
- post_processing.should_receive(:call).
- with(token).and_return('base64-encoded access token')
- @api.get_page_access_token('facebook', &post_processing).should ==
- 'base64-encoded access token'
- end
- end
- end
-end
View
12 spec/fixtures/mock_facebook_responses.yml
@@ -126,9 +126,6 @@ graph_api:
post:
with_token: '[{"code": 200, "body":"{\"id\": \"MOCK_PHOTO\"}"}, {"code": 200, "body":"{\"id\": \"MOCK_PHOTO\"}"}]'
-
-
-
/me:
no_args:
get:
@@ -166,7 +163,7 @@ graph_api:
link=http://oauth.twoalex.com/&message=Hello, world, from the test suite again!&name=OAuth Playground:
post:
with_token: '{"id": "FEED_ITEM_CONTEXT"}'
- link=http://oauth.twoalex.com/&message=body&name=It's a big question&picture=http://oauth.twoalex.com//images/logo.png&properties=<%= {"name"=>"Link1'", "text"=>"Left", "href"=>"http://oauth.twoalex.com/"}.to_s %>=<%= {"name"=>"other", "text"=>"Straight ahead"}.to_s %>&type=link:
+ link=http://oauth.twoalex.com/&message=body&name=It's a big question&picture=http://oauth.twoalex.com//images/logo.png&properties=<%= MultiJson.encode([{"name"=>"Link1'", "text"=>"Left", "href"=>"http://oauth.twoalex.com/"}, {"name"=>"other", "text"=>"Straight ahead"}]) %>&type=link:
post:
with_token: '{"id": "FEED_ITEM_CONTEXT"}'
@@ -308,6 +305,11 @@ graph_api:
post:
with_token: "true"
+ /debug_token:
+ input_token=<%= APP_ACCESS_TOKEN %>:
+ get:
+ with_token: '{ "data": { "app_id": <%= APP_ID %>, "application": "Social Cafe", "expires_at": 1352419328, "is_valid": true, "issued_at": 1347235328, "metadata": { "sso": "iphone-safari" }, "scopes": [ "email", "publish_actions" ], "user_id": 1207059 } }'
+
# -- OAuth responses --
/oauth/access_token:
client_id=<%= APP_ID %>&client_secret=<%= SECRET %>&code=<%= OAUTH_CODE %>&redirect_uri=<%= OAUTH_DATA["callback_url"] %>:
@@ -405,7 +407,7 @@ graph_api:
no_args:
<<: *item_deleted
get:
- with_token: '{"link":"http://oauth.twoalex.com/", "name": "OAuth Playground"}'
+ with_token: '{"link":"http://oauth.twoalex.com/", "message": "Hello, world, from the test suite again!", "name": "OAuth Playground"}'
/FEED_ITEM_CATS:
no_args:
View
668 spec/lib/api/batch_spec.rb
@@ -0,0 +1,668 @@
+require "spec_helper"
+
+module Koala
+ module Facebook
+ describe API do
+ before :each do
+ @api = Koala::Facebook::API.new(@token)
+ @api_without_token = Koala::Facebook::API.new
+ # app api
+ @app_id = KoalaTest.app_id
+ @app_access_token = KoalaTest.app_access_token
+ @app_api = Koala::Facebook::API.new(@app_access_token)
+ end
+
+ describe "the Batch API" do
+ describe GraphBatchAPI::BatchOperation do
+ before :each do
+ @args = {
+ :url => "my url",
+ :args => {:a => 2, :b => 3},
+ :method => "get",
+ :access_token => "12345",
+ :http_options => {},
+ :post_processing => lambda { }
+ }
+ end
+
+ describe ".new" do
+ it "makes http_options accessible" do
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).http_options.should == @args[:http_options]
+ end
+
+ it "makes post_processing accessible" do
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).post_processing.should == @args[:post_processing]
+ end
+
+ it "makes access_token accessible" do
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).access_token.should == @args[:access_token]
+ end
+
+ it "doesn't change the original http_options" do
+ @args[:http_options][:name] = "baz2"
+ expected = @args[:http_options].dup
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)
+ @args[:http_options].should == expected
+ end
+
+ it "leaves the file array nil by default" do
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).files.should be_nil
+ end
+
+ it "raises a KoalaError if no access token supplied" do
+ expect { Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args.merge(:access_token => nil)) }.to raise_exception(Koala::KoalaError)
+ end
+
+ describe "when supplied binary files" do
+ before :each do
+ @binary = stub("Binary file")
+ @uploadable_io = stub("UploadableIO 1")
+
+ @batch_queue = []
+ Koala::Facebook::GraphAPI.stub(:batch_calls).and_return(@batch_queue)
+
+ Koala::UploadableIO.stub(:new).with(@binary).and_return(@uploadable_io)
+ Koala::UploadableIO.stub(:binary_content?).and_return(false)
+ Koala::UploadableIO.stub(:binary_content?).with(@binary).and_return(true)
+ Koala::UploadableIO.stub(:binary_content?).with(@uploadable_io).and_return(true)
+ @uploadable_io.stub(:is_a?).with(Koala::UploadableIO).and_return(true)
+
+ @args[:method] = "post" # files are always post
+ end
+
+ it "adds binary files to the files attribute as UploadableIOs" do
+ @args[:args].merge!("source" => @binary)
+ batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
+ batch_op.files.should_not be_nil
+ batch_op.files.find {|k, v| v == @uploadable_io}.should_not be_nil
+ end
+
+ it "works if supplied an UploadableIO as an argument" do
+ # as happens with put_picture at the moment
+ @args[:args].merge!("source" => @uploadable_io)
+ batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
+ batch_op.files.should_not be_nil
+ batch_op.files.find {|k, v| v == @uploadable_io}.should_not be_nil
+ end
+
+ it "assigns each binary parameter unique name" do
+ @args[:args].merge!("source" => @binary, "source2" => @binary)
+ batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
+ # if the name wasn't unique, there'd just be one item
+ batch_op.files.should have(2).items
+ end
+
+ it "assigns each binary parameter unique name across batch requests" do
+ @args[:args].merge!("source" => @binary, "source2" => @binary)
+ batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
+ # simulate the batch operation, since it's used in determination
+ @batch_queue << batch_op
+ batch_op2 = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
+ @batch_queue << batch_op2
+ # if the name wasn't unique, we should have < 4 items since keys would be the same
+ batch_op.files.merge(batch_op2.files).should have(4).items
+ end
+
+ it "removes the value from the arguments" do
+ @args[:args].merge!("source" => @binary)
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:body].should_not =~ /source=/
+ end
+ end
+
+ end
+
+ describe "#to_batch_params" do
+ describe "handling arguments and URLs" do
+ shared_examples_for "request with no body" do
+ it "adds the args to the URL string, with ? if no args previously present" do
+ test_args = "foo"
+ @args[:url] = url = "/"
+ Koala.http_service.stub(:encode_params).and_return(test_args)
+
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == "#{url}?#{test_args}"
+ end
+
+ it "adds the args to the URL string, with & if args previously present" do
+ test_args = "foo"
+ @args[:url] = url = "/?a=2"
+ Koala.http_service.stub(:encode_params).and_return(test_args)
+
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == "#{url}&#{test_args}"
+ end
+
+ it "adds nothing to the URL string if there are no args to be added" do
+ @args[:args] = {}
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(@args[:access_token])[:relative_url].should == @args[:url]
+ end
+
+ it "adds nothing to the body" do
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:body].should be_nil
+ end
+ end
+
+ shared_examples_for "requests with a body param" do
+ it "sets the body to the encoded args string, if there are args" do
+ test_args = "foo"
+ Koala.http_service.stub(:encode_params).and_return(test_args)
+
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:body].should == test_args
+ end
+
+ it "does not set the body if there are no args" do
+ test_args = ""
+ Koala.http_service.stub(:encode_params).and_return(test_args)
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:body].should be_nil
+ end
+
+
+ it "doesn't change the url" do
+ test_args = "foo"
+ Koala.http_service.stub(:encode_params).and_return(test_args)
+
+ Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)[:relative_url].should == @args[:url]
+ end
+ end
+
+ context "for get operations" do
+ before :each do
+ @args[:method] = :get
+ end
+
+ it_should_behave_like "request with no body"
+ end
+
+ context "for delete operations" do
+ before :each do
+ @args[:method] = :delete
+ end
+
+ it_should_behave_like "request with no body"
+ end
+
+ context "for get operations" do
+ before :each do
+ @args[:method] = :put
+ end
+
+ it_should_behave_like "requests with a body param"
+ end
+
+ context "for delete operations" do
+ before :each do
+ @args[:method] = :post
+ end
+
+ it_should_behave_like "requests with a body param"
+ end
+ end
+
+ it "includes the access token if the token is not the main one for the request" do
+ params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)
+ params[:relative_url].should =~ /access_token=#{@args[:access_token]}/
+ end
+
+ it "includes the other arguments if the token is not the main one for the request" do
+ @args[:args] = {:a => 2}
+ params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)
+ params[:relative_url].should =~ /a=2/
+ end
+
+ it "does not include the access token if the token is the main one for the request" do
+ params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(@args[:access_token])
+ params[:relative_url].should_not =~ /access_token=#{@args[:access_token]}/
+ end
+
+ it "includes the other arguments if the token is the main one for the request" do
+ @args[:args] = {:a => 2}
+ params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(@args[:access_token])
+ params[:relative_url].should =~ /a=2/
+ end
+
+ it "includes any arguments passed as http_options[:batch_args]" do
+ batch_args = {:name => "baz", :headers => {:some_param => true}}
+ @args[:http_options][:batch_args] = batch_args
+ params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(nil)
+ params.should include(batch_args)
+ end
+
+ it "includes the method" do
+ params = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args).to_batch_params(@args[:access_token])
+ params[:method].should == @args[:method].to_s
+ end
+
+ it "works with nil http_options" do
+ expect { Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args.merge(:http_options => nil)).to_batch_params(nil) }.not_to raise_exception
+ end
+
+ it "works with nil args" do
+ expect { Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args.merge(:args => nil)).to_batch_params(nil) }.not_to raise_exception
+ end
+
+ describe "with binary files" do
+ before :each do
+ @binary = stub("Binary file")
+ Koala::UploadableIO.stub(:binary_content?).and_return(false)
+ Koala::UploadableIO.stub(:binary_content?).with(@binary).and_return(true)
+ @uploadable_io = stub("UploadableIO")
+ Koala::UploadableIO.stub(:new).with(@binary).and_return(@uploadable_io)
+ @uploadable_io.stub(:is_a?).with(Koala::UploadableIO).and_return(true)
+
+ @batch_queue = []
+ Koala::Facebook::GraphAPI.stub(:batch_calls).and_return(@batch_queue)
+
+ @args[:method] = "post" # files are always post
+ end
+
+ it "adds file identifiers as attached_files in a comma-separated list" do
+ @args[:args].merge!("source" => @binary, "source2" => @binary)
+ batch_op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(@args)
+ file_ids = batch_op.files.find_all {|k, v| v == @uploadable_io}.map {|k, v| k}
+ params = batch_op.to_batch_params(nil)
+ params[:attached_files].should == file_ids.join(",")
+ end
+ end
+ end
+
+ end
+
+ describe "GraphAPI batch interface" do
+ it "returns nothing for a batch operation" do
+ Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, "[]", {}))
+ @api.batch do |batch_api|
+ batch_api.get_object('me').should be_nil
+ end
+ end
+
+ describe "#batch" do
+ before :each do
+ @fake_response = Koala::HTTPService::Response.new(200, "[]", {})
+ Koala.stub(:make_request).and_return(@fake_response)
+ end
+
+ describe "making the request" do
+ context "with no calls" do
+ it "does not make any requests if batch_calls is empty" do
+ Koala.should_not_receive(:make_request)
+ @api.batch {|batch_api|}
+ end
+
+ it "returns []" do
+ @api.batch {|batch_api|}.should == []
+ end
+ end
+
+ it "includes the first operation's access token as the main one in the args" do
+ access_token = "foo"
+ Koala.should_receive(:make_request).with(anything, hash_including("access_token" => access_token), anything, anything).and_return(@fake_response)
+ Koala::Facebook::API.new(access_token).batch do |batch_api|
+ batch_api.get_object('me')
+ batch_api.get_object('me', {}, {'access_token' => 'bar'})
+ end
+ end
+
+ it "sets args['batch'] to a json'd map of all the batch params" do
+ access_token = "bar"
+ op = Koala::Facebook::GraphBatchAPI::BatchOperation.new(:access_token => access_token, :method => :get, :url => "/")
+ op.stub(:to_batch_params).and_return({:a => 2})
+ Koala::Facebook::GraphBatchAPI::BatchOperation.stub(:new).and_return(op)
+
+ # two requests should generate two batch operations
+ expected = MultiJson.dump([op.to_batch_params(access_token), op.to_batch_params(access_token)])
+ Koala.should_receive(:make_request).with(anything, hash_including("batch" => expected), anything, anything).and_return(@fake_response)
+ Koala::Facebook::API.new(access_token).batch do |batch_api|
+ batch_api.get_object('me')
+ batch_api.get_object('me')
+ end
+ end
+
+ it "adds any files from the batch operations to the arguments" do
+ # stub the batch operation
+ # we test above to ensure that files are properly assimilated into the BatchOperation instance
+ # right now, we want to make sure that batch_api handles them properly
+ @key = "file0_0"
+ @uploadable_io = stub("UploadableIO")
+ batch_op = stub("Koala Batch Operation", :files => {@key => @uploadable_io}, :to_batch_params => {}, :access_token => "foo")
+ Koala::Facebook::GraphBatchAPI::BatchOperation.stub(:new).and_return(batch_op)
+
+ Koala.should_receive(:make_request).with(anything, hash_including(@key => @uploadable_io), anything, anything).and_return(@fake_response)
+ Koala::Facebook::API.new("bar").batch do |batch_api|
+ batch_api.put_picture("path/to/file", "image/jpeg")
+ end
+ end
+
+ it "preserves operation order" do
+ access_token = "bar"
+ # two requests should generate two batch operations
+ Koala.should_receive(:make_request) do |url, args, method, options|
+ # test the batch operations to make sure they appear in the right order
+ (args ||= {})["batch"].should =~ /.*me\/farglebarg.*otheruser\/bababa/
+ @fake_response
+ end
+ Koala::Facebook::API.new(access_token).batch do |batch_api|
+ batch_api.get_connections('me', "farglebarg")
+ batch_api.get_connections('otheruser', "bababa")
+ end
+ end
+
+ it "makes a POST request" do
+ Koala.should_receive(:make_request).with(anything, anything, "post", anything).and_return(@fake_response)
+ Koala::Facebook::API.new("foo").batch do |batch_api|
+ batch_api.get_object('me')
+ end
+ end
+
+ it "makes a request to /" do
+ Koala.should_receive(:make_request).with("/", anything, anything, anything).and_return(@fake_response)
+ Koala::Facebook::API.new("foo").batch do |batch_api|
+ batch_api.get_object('me')
+ end
+ end
+
+ it "includes any http options specified at the top level" do
+ http_options = {"a" => "baz"}
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(http_options)).and_return(@fake_response)
+ Koala::Facebook::API.new("foo").batch(http_options) do |batch_api|
+ batch_api.get_object('me')
+ end
+ end
+ end
+
+ describe "processing the request" do
+ it "returns the result headers as a hash if http_component is headers" do
+ Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
+ result = @api.batch do |batch_api|
+ batch_api.get_object(KoalaTest.user1, {}, :http_component => :headers)
+ end
+ result[0].should == {"Content-Type" => "text/javascript; charset=UTF-8"}
+ end
+
+ describe "if it errors" do
+ it "raises an APIError if the response is not 200" do
+ Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(500, "[]", {}))
+ expect {
+ Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
+ }.to raise_exception(Koala::Facebook::APIError)
+ end
+
+ it "raises a BadFacebookResponse if the body is empty" do
+ Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, "", {}))
+ expect {
+ Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
+ }.to raise_exception(Koala::Facebook::BadFacebookResponse)
+ end
+
+ context "with the old style" do
+ before :each do
+ Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(400, '{"error_code":190,"error_description":"Error validating access token."}', {}))
+ end
+
+ it "throws an error" do
+ expect {
+ Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
+ }.to raise_exception(Koala::Facebook::APIError)
+ end
+
+ it "passes all the error details" do
+ begin
+ Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
+ rescue Koala::Facebook::APIError => err
+ err.fb_error_code.should == 190
+ err.fb_error_message.should == "Error validating access token."
+ err.http_status == 400
+ err.response_body == '{"error_code":190,"error_description":"Error validating access token."}'
+ end
+ end
+ end
+
+ context "with the new style" do
+ before :each do
+ Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(400, '{"error":{"message":"Request 0 cannot depend on an unresolved request with name f. Requests can only depend on preceding requests","type":"GraphBatchException"}}', {}))
+ end
+
+ it "throws an error" do
+ expect {
+ Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
+ }.to raise_exception(Koala::Facebook::APIError)
+ end
+
+ it "passes all the error details" do
+ begin
+ Koala::Facebook::API.new("foo").batch {|batch_api| batch_api.get_object('me') }
+ rescue Koala::Facebook::APIError => err
+ err.fb_error_type.should == "GraphBatchException"
+ err.fb_error_message.should == "Request 0 cannot depend on an unresolved request with name f. Requests can only depend on preceding requests"
+ err.http_status == 400
+ err.response_body == '{"error":{"message":"Request 0 cannot depend on an unresolved request with name f. Requests can only depend on preceding requests","type":"GraphBatchException"}}'
+ end
+ end
+ end
+ end
+
+ it "returns the result status if http_component is status" do
+ Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, '[{"code":203,"headers":[{"name":"Content-Type","value":"text/javascript; charset=UTF-8"}],"body":"{\"id\":\"1234\"}"}]', {}))
+ result = @api.batch do |batch_api|
+ batch_api.get_object(KoalaTest.user1, {}, :http_component => :status)
+ end
+ result[0].should == 203
+ end
+ end
+
+ it "is thread safe" do
+ # ensure batch operations on one thread don't affect those on another
+ thread_one_count = 0
+ thread_two_count = 0
+ first_count = 20
+ second_count = 10
+
+ Koala.stub(:make_request).and_return(@fake_response)
+
+ thread1 = Thread.new do
+ @api.batch do |batch_api|
+ first_count.times {|i| batch_api.get_object("me"); sleep(0.01) }
+ thread_one_count = batch_api.batch_calls.count
+ end
+ end
+
+ thread2 = Thread.new do
+ @api.batch do |batch_api|
+ second_count.times {|i| batch_api.get_object("me"); sleep(0.01) }
+ thread_two_count = batch_api.batch_calls.count
+ end
+ end
+
+ thread1.join
+ thread2.join
+
+ thread_one_count.should == first_count
+ thread_two_count.should == second_count
+ end
+ end
+ end
+
+ describe "usage tests" do
+ it "gets two results at once" do
+ me, koppel = @api.batch do |batch_api|
+ batch_api.get_object('me')
+ batch_api.get_object(KoalaTest.user1)
+ end
+ me['id'].should_not be_nil
+ koppel['id'].should_not be_nil
+ end
+
+ it 'makes mixed calls inside of a batch' do
+ results = @api.batch do |batch_api|
+ batch_api.get_object('me')
+ batch_api.get_connections('me', 'friends')
+ end
+ results.last.should be_a(Koala::Facebook::GraphCollection)
+ end
+
+ it 'turns pageable results into GraphCollections' do
+ me, friends = @api.batch do |batch_api|
+ batch_api.get_object('me')
+ batch_api.get_connections('me', 'friends')
+ end
+ me['id'].should_not be_nil
+ friends.should be_an(Array)
+ end
+
+ it 'makes a get_picture call inside of a batch' do
+ pictures = @api.batch do |batch_api|
+ batch_api.get_picture('me')
+ end
+ puts pictures.inspect
+ pictures.first.should =~ /http\:\/\// # works both live & stubbed
+ end
+
+ it "handles requests for two different tokens" do
+ me, insights = @api.batch do |batch_api|
+ batch_api.get_object('me')
+ batch_api.get_connections(@app_id, 'insights', {}, {"access_token" => @app_api.access_token})
+ end
+ me['id'].should_not be_nil
+ insights.should be_an(Array)
+ end
+
+ it "inserts errors in the appropriate place, without breaking other results" do
+ failed_call, koppel = @api.batch do |batch_api|
+ batch_api.get_connection("2", "invalidconnection")
+ batch_api.get_object(KoalaTest.user1, {}, {"access_token" => @app_api.access_token})
+ end
+ failed_call.should be_a(Koala::Facebook::ClientError)
+ koppel["id"].should_not be_nil
+ end
+
+ it "handles different request methods" do
+ result = @api.put_wall_post("Hello, world, from the test suite batch API!")
+ wall_post = result["id"]
+
+ @api.batch do |batch_api|
+ batch_api.put_like(wall_post)
+ batch_api.delete_object(wall_post)
+ end
+ end
+
+ it "allows FQL" do
+ result = @api.batch do |batch_api|
+ batch_api.graph_call("method/fql.query", {:query=>"select first_name from user where uid=#{KoalaTest.user1_id}"}, "post")
+ end
+
+ fql_result = result[0]
+ fql_result[0].should be_a(Hash)
+ fql_result[0]["first_name"].should == "Alex"
+ end
+
+ describe 'with post-processing callback' do
+ let(:me_result) { stub("me result") }
+ let(:friends_result) { [stub("friends result")] }
+
+ let(:me_callback) { lambda {|arg| {"result" => me_result, "args" => arg} } }
+ let(:friends_callback) { lambda {|arg| {"result" => friends_result, "args" => arg} } }
+
+ it 'calls the callback with the appropriate data' do
+ me, friends = @api.batch do |batch_api|
+ batch_api.get_object('me', &me_callback)
+ batch_api.get_connections('me', 'friends', &friends_callback)
+ end
+ me["args"].should include("id" => KoalaTest.user1)
+ friends["args"].first.should include("id" => KoalaTest.user2)
+ end
+
+ it 'passes GraphCollections, not raw data' do
+ me, friends = @api.batch do |batch_api|
+ batch_api.get_object('me')
+ batch_api.get_connections('me', 'friends', &friends_callback)
+ end
+ friends["args"].should be_a(Koala::Facebook::API::GraphCollection)
+ end
+
+ it "returns the result of the callback" do
+ @api.batch do |batch_api|
+ batch_api.get_object('me', &me_callback)
+ batch_api.get_connections('me', 'friends', &friends_callback)
+ end.map {|r| r["result"]}.should == [me_result, friends_result]
+ end
+ end
+
+ describe "binary files" do
+ it "posts binary files" do
+ file = File.open(File.join(File.dirname(__FILE__), "../../fixtures", "beach.jpg"))
+
+ Koala::Facebook::GraphBatchAPI::BatchOperation.instance_variable_set(:@identifier, 0)
+ result = @api.batch do |batch_api|
+ batch_api.put_picture(file)
+ end
+
+ @temporary_object_id = result[0]["id"]
+ @temporary_object_id.should_not be_nil
+ end
+
+ it "posts binary files with multiple requests" do
+ file = File.open(File.join(File.dirname(__FILE__), "../../fixtures", "beach.jpg"))
+ file2 = File.open(File.join(File.dirname(__FILE__), "../../fixtures", "beach.jpg"))
+
+ Koala::Facebook::GraphBatchAPI::BatchOperation.instance_variable_set(:@identifier, 0)
+ results = @api.batch do |batch_api|
+ batch_api.put_picture(file)
+ batch_api.put_picture(file2, {}, KoalaTest.user1)
+ end
+ results[0]["id"].should_not be_nil
+ results[1]["id"].should_not be_nil
+ end
+ end
+
+ describe "relating requests" do
+ it "allows you create relationships between requests without omit_response_on_success" do
+ results = @api.batch do |batch_api|
+ batch_api.get_connections("me", "friends", {:limit => 5}, :batch_args => {:name => "get-friends"})
+ batch_api.get_objects("{result=get-friends:$.data.*.id}")
+ end
+
+ results[0].should be_nil
+ results[1].should be_an(Hash)
+ end
+
+ it "allows you create relationships between requests with omit_response_on_success" do
+ results = @api.batch do |batch_api|
+ batch_api.get_connections("me", "friends", {:limit => 5}, :batch_args => {:name => "get-friends", :omit_response_on_success => false})
+ batch_api.get_objects("{result=get-friends:$.data.*.id}")
+ end
+
+ results[0].should be_an(Array)
+ results[1].should be_an(Hash)
+ end
+
+ it "allows you to create dependencies" do
+ me, koppel = @api.batch do |batch_api|
+ batch_api.get_object("me", {}, :batch_args => {:name => "getme"})
+ batch_api.get_object(KoalaTest.user1, {}, :batch_args => {:depends_on => "getme"})
+ end
+
+ me.should be_nil # gotcha! it's omitted because it's a successfully-executed dependency
+ koppel["id"].should_not be_nil
+ end
+
+ it "properly handles dependencies that fail" do
+ failed_call, koppel = @api.batch do |batch_api|
+ batch_api.get_connections("2", "invalidconnection", {}, :batch_args => {:name => "getdata"})
+ batch_api.get_object(KoalaTest.user1, {}, :batch_args => {:depends_on => "getdata"})
+ end
+
+ failed_call.should be_a(Koala::Facebook::ClientError)
+ koppel.should be_nil
+ end
+
+ it "throws an error for badly-constructed request relationships" do
+ expect {
+ @api.batch do |batch_api|
+ batch_api.get_connections("me", "friends", {:limit => 5})
+ batch_api.get_objects("{result=i-dont-exist:$.data.*.id}")
+ end
+ }.to raise_exception(Koala::Facebook::ClientError)
+ end
+ end
+ end
+ end
+ end
+ end
+end
View
54 spec/lib/api/delete_spec.rb
@@ -0,0 +1,54 @@
+require "spec_helper"
+
+module Koala
+ module Facebook
+ describe API do
+ before :each do
+ @api = Koala::Facebook::API.new(@token)
+ @api_without_token = Koala::Facebook::API.new
+ # app API
+ @app_id = KoalaTest.app_id
+ @app_access_token = KoalaTest.app_access_token
+ @app_api = Koala::Facebook::API.new(@app_access_token)
+ end
+
+ describe "deleting data from the graph" do
+ describe "#delete_object" do
+ context "without an access token" do
+ it "can't delete posts" do
+ # test post on the Ruby SDK Test application
+ lambda { @result = @api_without_token.delete_object("115349521819193_113815981982767") }.should raise_error(Koala::Facebook::AuthenticationError)
+ end
+ end
+
+ context "with an access token" do
+ it "can delete posts" do
+ result = @api.put_wall_post("Hello, world, from the test suite delete method!")
+ object_id_to_delete = result["id"]
+ delete_result = @api.delete_object(object_id_to_delete)
+ delete_result.should == true
+ end
+ end
+ end
+
+ describe "#delete_like" do
+ context "without an access token" do
+ it "can't delete a like" do
+ lambda { @api_without_token.delete_like("7204941866_119776748033392") }.should raise_error(Koala::Facebook::AuthenticationError)
+ end
+ end
+
+ context "with an access token" do
+ it "can delete likes" do
+ result = @api.put_wall_post("Hello, world, from the test suite delete like method!")
+ @temporary_object_id = result["id"]
+ @api.put_like(@temporary_object_id)
+ delete_like_result = @api.delete_like(@temporary_object_id)
+ delete_like_result.should == true
+ end
+ end
+ end
+ end
+ end
+ end
+end
View
295 spec/lib/api/read_spec.rb
@@ -0,0 +1,295 @@
+require "spec_helper"
+
+module Koala
+ module Facebook
+ describe API do
+ before :each do
+ @api = Koala::Facebook::API.new(@token)
+ @api_without_token = Koala::Facebook::API.new
+ # app API
+ @app_id = KoalaTest.app_id
+ @app_access_token = KoalaTest.app_access_token
+ @app_api = Koala::Facebook::API.new(@app_access_token)
+ end
+
+ describe "reading information from the graph" do
+ describe "#get_object" do
+ context "without an access token" do
+ it "gets public data about a user" do
+ result = @api_without_token.get_object(KoalaTest.user1)
+ # the results should have an ID and a name, among other things
+ (result["id"] && result["name"]).should_not be_nil
+ end
+
+ it "gets public data about a Page" do
+ result = @api_without_token.get_object(KoalaTest.page)
+ # the results should have an ID and a name, among other things
+ (result["id"] && result["name"]).should
+ end
+
+ it "can't get private data about a user" do
+ result = @api_without_token.get_object(KoalaTest.user1)
+ # updated_time should be a pretty fixed test case
+ result["updated_time"].should be_nil
+ end
+
+ it "can't get data about 'me'" do
+ lambda { @api_without_token.get_object("me") }.should raise_error(Koala::Facebook::ClientError)
+ end
+ end
+
+ context "with an access token" do
+ it "gets private data about a user" do
+ result = @api.get_object(KoalaTest.user1)
+ # updated_time should be a pretty fixed test case
+ result["updated_time"].should_not be_nil
+ end
+
+ it "gets data about 'me'" do
+ result = @api.get_object("me")
+ result["updated_time"].should
+ end
+ end
+ end
+
+ describe "#get_objects" do
+ it "returns [] from get_objects if passed an empty array" do
+ results = @api.get_objects([])
+ results.should == []
+ end
+
+ it "gets multiple objects" do
+ results = @api.get_objects([KoalaTest.page, KoalaTest.user1])
+ results.should have(2).items
+ end
+
+ it "gets multiple objects if they're a string" do
+ results = @api.get_objects("facebook,#{KoalaTest.user1}")
+ results.should have(2).items
+ end
+ end
+
+ describe "#get_picture" do
+ it "can access a user's picture" do
+ @api.get_picture(KoalaTest.user2).should =~ /http[s]*\:\/\//
+ end
+
+ it "can access a user's picture, given a picture type" do
+ @api.get_picture(KoalaTest.user2, {:type => 'large'}).should =~ /^http[s]*\:\/\//
+ end
+ end
+
+ describe "#get_connections" do
+ it "gets a GraphCollection when getting connections" do
+ @result = @api.get_connections(KoalaTest.page, "photos")
+ @result.should be_a(Koala::Facebook::GraphCollection)
+ end
+
+ it "returns nil if the get_collections call fails with nil" do
+ # this happens sometimes
+ @api.should_receive(:graph_call).and_return(nil)
+ @api.get_connections(KoalaTest.page, "photos").should be_nil
+ end
+
+ context "without an access token" do
+ it "can access connections from public Pages" do
+ result = @api_without_token.get_connections(KoalaTest.page, "photos")
+ result.should be_a(Array)
+ end
+
+ it "can't access connections from users" do
+ lambda { @api_without_token.get_connections(KoalaTest.user2, "friends") }.should raise_error(Koala::Facebook::ClientError)
+ end
+ end
+
+ context "with an access token" do
+ it "can access connections from users" do
+ result = @api.get_connections(KoalaTest.user2, "friends")
+ result.length.should > 0
+ end
+ end
+ end
+
+ describe "#get_comments_for_urls" do
+ it "can access comments for a URL" do
+ result = @api.get_comments_for_urls(["http://developers.facebook.com/blog/post/472"])
+ (result["http://developers.facebook.com/blog/post/472"]).should
+ end
+
+ it "can access comments for 2 URLs" do
+ result = @api.get_comments_for_urls(["http://developers.facebook.com/blog/post/490", "http://developers.facebook.com/blog/post/472"])
+ (result["http://developers.facebook.com/blog/post/490"] && result["http://developers.facebook.com/blog/post/472"]).should
+ end
+ end
+
+ describe "#get_page_access_token" do
+ it "gets the page object with the access_token field" do
+ # we can't test this live since test users (or random real users) can't be guaranteed to have pages to manage
+ @api.should_receive(:api).with("my_page", hash_including({:fields => "access_token"}), "get", anything)
+ @api.get_page_access_token("my_page")
+ end
+
+ it "merges in any other arguments" do
+ # we can't test this live since test users (or random real users) can't be guaranteed to have pages to manage
+ args = {:a => 3}
+ @api.should_receive(:api).with("my_page", hash_including(args), "get", anything)
+ @api.get_page_access_token("my_page", args)
+ end
+ end
+
+ describe "#debug_token" do
+ it "can get information about an access token" do
+ result = @api.debug_token(KoalaTest.app_access_token)
+ result.should be_kind_of(Hash)
+ result["data"].should be_kind_of(Hash)
+ result["data"]["app_id"].to_s.should == KoalaTest.app_id.to_s
+ result["data"]["application"].should_not be_nil
+ end
+ end
+
+ describe "FQL" do
+ describe "#fql_query" do
+ it "makes a request to /fql" do
+ @api.should_receive(:get_object).with("fql", anything, anything)
+ @api.fql_query stub('query string')
+ end
+
+ it "passes a query argument" do
+ query = stub('query string')
+ @api.should_receive(:get_object).with(anything, hash_including(:q => query), anything)
+ @api.fql_query(query)
+ end
+
+ it "passes on any other arguments provided" do
+ args = {:a => 2}
+ @api.should_receive(:get_object).with(anything, hash_including(args), anything)
+ @api.fql_query("a query", args)
+ end
+
+ context "without an access token" do
+ it "can access public information via FQL" do
+ result = @api_without_token.fql_query("select uid, first_name from user where uid = #{KoalaTest.user2_id}")
+ result.size.should == 1
+ result.first['first_name'].should == KoalaTest.user2_name
+ result.first['uid'].should == KoalaTest.user2_id.to_i
+ end
+
+ it "can't access protected information via FQL" do
+ lambda { @api_without_token.fql_query("select read_stream from permissions where uid = #{KoalaTest.user2_id}") }.should raise_error(Koala::Facebook::APIError)
+ end
+ end
+
+ context "with an access token" do
+ it "can access protected information via FQL" do
+ # Tests agains the permissions fql table
+
+ # get the current user's ID
+ # we're sneakily using the Graph API, which should be okay since it has its own tests
+ g = Koala::Facebook::API.new(@token)
+ id = g.get_object("me", :fields => "id")["id"]
+
+ # now send a query about your permissions
+ result = @api.fql_query("select read_stream from permissions where uid = #{id}")
+
+ result.size.should == 1
+ # we've verified that you have read_stream permissions, so we can test against that
+ result.first["read_stream"].should == 1
+ end
+ end
+ end
+
+ describe "#fql_multiquery" do
+ it "makes a request to /fql" do
+ @api.should_receive(:get_object).with("fql", anything, anything)
+ @api.fql_multiquery 'query string'
+ end
+
+ it "passes a queries argument" do
+ queries = stub('query string')
+ queries_json = "some JSON"
+ MultiJson.stub(:dump).with(queries).and_return(queries_json)
+
+ @api.should_receive(:get_object).with(anything, hash_including(:q => queries_json), anything)
+ @api.fql_multiquery(queries)
+ end
+
+ it "simplifies the response format" do
+ raw_results = [
+ {"name" => "query1", "fql_result_set" => [1, 2, 3]},
+ {"name" => "query2", "fql_result_set" => [:a, :b, :c]}
+ ]
+ expected_results = {
+ "query1" => [1, 2, 3],
+ "query2" => [:a, :b, :c]
+ }
+
+ @api.stub(:get_object).and_return(raw_results)
+ results = @api.fql_multiquery({:query => true})
+ results.should == expected_results
+ end
+
+ it "passes on any other arguments provided" do
+ args = {:a => 2}
+ @api.should_receive(:get_object).with(anything, hash_including(args), anything)
+ @api.fql_multiquery("a query", args)
+ end
+
+ context "without an access token" do
+ it "can access public information via FQL.multiquery" do
+ result = @api_without_token.fql_multiquery(
+ :query1 => "select uid, first_name from user where uid = #{KoalaTest.user2_id}",
+ :query2 => "select uid, first_name from user where uid = #{KoalaTest.user1_id}"
+ )
+ result.size.should == 2
+ # this should check for first_name, but there's an FB bug currently
+ result["query1"].first['uid'].should == KoalaTest.user2_id.to_i
+ # result["query1"].first['first_name'].should == KoalaTest.user2_name
+ result["query2"].first['first_name'].should == KoalaTest.user1_name
+ end
+
+ it "can't access protected information via FQL.multiquery" do
+ lambda {
+ @api_without_token.fql_multiquery(
+ :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)"
+ )
+ }.should raise_error(Koala::Facebook::APIError)
+ end
+ end
+
+ context "with an access token" do
+ it "can access protected information via FQL.multiquery" do
+ result = @api.fql_multiquery(
+ :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)"
+ )
+ result.size.should == 3
+ result.keys.should include("query1", "query2", "query3")
+ end
+ end
+ end
+ end
+
+ describe "#search" do
+ it "performs a Facebook search" do
+ result = @api.search("facebook")
+ result.length.should be_an(Integer)
+ end
+
+ it "gets a GraphCollection when searching" do
+ result = @api.search("facebook")
+ result.should be_a(Koala::Facebook::GraphCollection)
+ end
+
+ it "returns nil if the search call fails with nil" do
+ # this happens sometimes
+ @api.should_receive(:graph_call).and_return(nil)
+ @api.search("facebook").should be_nil
+ end
+ end
+ end
+ end
+ end
+end
View
244 spec/lib/api/write_spec.rb
@@ -0,0 +1,244 @@
+require "spec_helper"
+
+module Koala
+ module Facebook
+ describe API do
+ before :each do
+ @api = Koala::Facebook::API.new(@token)
+ @api_without_token = Koala::Facebook::API.new
+ # app API
+ @app_id = KoalaTest.app_id
+ @app_access_token = KoalaTest.app_access_token
+ @app_api = Koala::Facebook::API.new(@app_access_token)
+ end
+
+ describe "writing to the graph" do
+ describe "#put_connections" do
+ context "without an access token" do
+ it "can't put an object" do
+ lambda { @result = @api_without_token.put_connections(KoalaTest.user2, "feed", :message => "Hello, world") }.should raise_error(Koala::Facebook::AuthenticationError)
+ # legacy put_object syntax
+ lambda { @result = @api_without_token.put_object(KoalaTest.user2, "feed", :message => "Hello, world") }.should raise_error(Koala::Facebook::AuthenticationError)
+ end
+ end
+ end
+
+ describe "#put_wall_post" do
+ context "without an access token" do
+ # these are not strictly necessary as the other put methods resolve to put_connections,
+ # but are here for completeness
+ it "can't post to a feed" do
+ (lambda do
+ attachment = {:name => "OAuth Playground", :link => "http://oauth.twoalex.com/"}
+ @result = @api_without_token.put_wall_post("Hello, world", attachment, "facebook")
+ end).should raise_error(Koala::Facebook::AuthenticationError)
+ end
+ end
+
+ context "with an access token" do
+ it "writes a message to the wall" do
+ message = "Hello, world, from the test suite!"
+ put_result = @api.put_wall_post(message)
+ @temporary_object_id = put_result["id"]
+ end
+
+ it "posts a message with an attachment to a feed" do
+ attachment = {
+ "name" => "OAuth Playground",
+ "link" => "http://oauth.twoalex.com/"
+ }
+ result = @api.put_wall_post(
+ "Hello, world, from the test suite again!",
+ attachment
+ )
+ @temporary_object_id = result["id"]
+ @temporary_object_id.should_not be_nil
+
+ # verify it posted
+ get_result = @api.get_object(@temporary_object_id)
+
+ # make sure the result we fetch includes all the parameters we sent
+ attachment.each_pair do |key, value|
+ expect(get_result[key]).to eq(value)
+ end
+ end
+
+ it "can post a message whose attachment has a properties dictionary" do
+ url = KoalaTest.oauth_test_data["callback_url"]
+ options = {
+ "picture" => "#{KoalaTest.oauth_test_data["callback_url"]}/images/logo.png",
+ "name" => "It's a big question",
+ "type" => "link",
+ "link" => KoalaTest.oauth_test_data["callback_url"],
+ "properties" => [
+ {"name" => "Link1'", "text" => "Left", "href" => url},
+ {"name" => "other", "text" => "Straight ahead"}
+ ]
+ }
+
+ result = @api.put_wall_post("body", options)
+ @temporary_object_id = result["id"]
+ @temporary_object_id.should_not be_nil
+ end
+ end
+ end
+
+ describe "#put_picture" do
+ it "can post photos to the user's wall with an open file object" do
+ content_type = "image/jpg"
+ file = File.open(File.join(File.dirname(__FILE__), "../../fixtures", "beach.jpg"))
+
+ result = @api.put_picture(file, content_type)
+ @temporary_object_id = result["id"]
+ @temporary_object_id.should_not be_nil
+ end
+
+ it "can post photos to the user's wall without an open file object" do
+ content_type = "image/jpg",
+ file_path = File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg")
+
+ result = @api.put_picture(file_path, content_type)
+ @temporary_object_id = result["id"]
+ @temporary_object_id.should_not be_nil
+ end
+
+ it "can verify a photo posted to a user's wall" do
+ content_type = "image/jpg",
+ file_path = File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg")
+
+ expected_message = "This is the test message"
+
+ result = @api.put_picture(file_path, content_type, :message => expected_message)
+ @temporary_object_id = result["id"]
+ @temporary_object_id.should_not be_nil
+
+ get_result = @api.get_object(@temporary_object_id)
+ get_result["name"].should == expected_message
+ end
+
+
+ describe "using a URL instead of a file" do
+ before :each do
+ @url = "http://img.slate.com/images/redesign2008/slate_logo.gif"
+ end
+
+ it "can post photo to the user's wall using a URL" do
+ result = @api.put_picture(@url)
+ @temporary_object_id = result["id"]
+ @temporary_object_id.should_not be_nil
+ end
+
+ it "can post photo to the user's wall using a URL and an additional param" do
+ result = @api.put_picture(@url, :message => "my message")
+ @temporary_object_id = result["id"]
+ @temporary_object_id.should_not be_nil
+ end
+ end
+ end
+
+ describe "#put_video" do
+ before :each do
+ @cat_movie = File.join(File.dirname(__FILE__), "../..//fixtures", "cat.m4v")
+ @content_type = "video/mpeg4"
+ end
+
+ it "sets options[:video] to true" do
+ source = stub("UploadIO")
+ Koala::UploadableIO.stub(:new).and_return(source)
+ source.stub(:requires_base_http_service).and_return(false)
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(:video => true)).and_return(Koala::HTTPService::Response.new(200, "[]", {}))
+ @api.put_video("foo")
+ end
+
+ it "can post videos to the user's wall with an open file object" do
+ file = File.open(@cat_movie)
+
+ result = @api.put_video(file, @content_type)
+ @temporary_object_id = result["id"]
+ @temporary_object_id.should_not be_nil
+ end
+
+
+ it "can post videos to the user's wall without an open file object" do
+ result = @api.put_video(@cat_movie, @content_type)
+ @temporary_object_id = result["id"]
+ @temporary_object_id.should_not be_nil
+ end
+
+ # note: Facebook doesn't post videos immediately to the wall, due to processing time
+ # during which get_object(video_id) will return false
+ # hence we can't do the same verify test we do for photos
+ end
+
+ describe "#put_comment" do
+ context "without an access token" do
+ it "can't comment on an object" do
+ # random public post on the facebook wall
+ lambda { @result = @api_without_token.put_comment("7204941866_119776748033392", "The hackathon was great!") }.should raise_error(Koala::Facebook::AuthenticationError)
+ end
+ end
+
+ context "with an access token" do
+ it "posts a comment to another object" do
+ message_text = "Hello, world, from the test suite, testing comments again!"
+ result = @api.put_wall_post(message_text)
+ @temporary_object_id = result["id"]
+
+ # this will be deleted when the post gets deleted
+ comment_text = "it's my comment!"
+ comment_result = @api.put_comment(@temporary_object_id, comment_text)
+ get_result = @api.get_object(comment_result["id"])
+
+ # make sure the text of the comment matches what we sent
+ get_result["message"].should == comment_text
+ end
+ end
+ end
+
+ describe "#put_like" do
+ it "likes an object" do
+ result = @api.put_wall_post("Hello, world, from the test suite, testing liking!")
+ @temporary_object_id = result["id"]
+ like_result = @api.put_like(@temporary_object_id)
+ like_result.should be_true
+ end
+
+ context "without an access token" do
+ it "can't like an object" do
+ lambda { @api_without_token.put_like("7204941866_119776748033392") }.should raise_error(Koala::Facebook::AuthenticationError)
+ end
+ end
+ end
+
+ describe "#set_app_restrictions" do
+ before :all do
+ oauth = Koala::Facebook::OAuth.new(KoalaTest.app_id, KoalaTest.secret)
+ app_token = oauth.get_app_access_token
+ @app_api = Koala::Facebook::API.new(app_token)