koala 0.10.0 → 1.0.0.beta2

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 (34) hide show
  1. data/CHANGELOG +33 -7
  2. data/Manifest +8 -1
  3. data/Rakefile +4 -4
  4. data/koala.gemspec +10 -8
  5. data/lib/koala/graph_api.rb +188 -123
  6. data/lib/koala/http_services.rb +93 -17
  7. data/lib/koala/rest_api.rb +73 -6
  8. data/lib/koala/test_users.rb +18 -5
  9. data/lib/koala/uploadable_io.rb +115 -0
  10. data/lib/koala.rb +95 -72
  11. data/readme.md +18 -12
  12. data/spec/facebook_data.yml +9 -3
  13. data/spec/koala/assets/beach.jpg +0 -0
  14. data/spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb +5 -1
  15. data/spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb +8 -3
  16. data/spec/koala/graph_api/graph_api_no_access_token_tests.rb +12 -61
  17. data/spec/koala/graph_api/graph_api_tests.rb +85 -0
  18. data/spec/koala/graph_api/graph_api_with_access_token_tests.rb +167 -123
  19. data/spec/koala/http_services/http_service_tests.rb +51 -0
  20. data/spec/koala/http_services/net_http_service_tests.rb +339 -0
  21. data/spec/koala/http_services/typhoeus_service_tests.rb +162 -0
  22. data/spec/koala/live_testing_data_helper.rb +1 -1
  23. data/spec/koala/oauth/oauth_tests.rb +35 -64
  24. data/spec/koala/rest_api/rest_api_no_access_token_tests.rb +5 -74
  25. data/spec/koala/rest_api/rest_api_tests.rb +118 -0
  26. data/spec/koala/rest_api/rest_api_with_access_token_tests.rb +5 -3
  27. data/spec/koala/test_users/test_users_tests.rb +49 -48
  28. data/spec/koala/uploadable_io/uploadable_io_tests.rb +246 -0
  29. data/spec/koala_spec_helper.rb +30 -4
  30. data/spec/koala_spec_without_mocks.rb +3 -3
  31. data/spec/mock_facebook_responses.yml +41 -18
  32. data/spec/mock_http_service.rb +16 -3
  33. metadata +38 -8
  34. data/spec/koala/net_http_service_tests.rb +0 -186
@@ -0,0 +1,85 @@
1
+ shared_examples_for "Koala GraphAPI" do
2
+ # all Graph API instances should pass these tests, regardless of configuration
3
+
4
+ # API
5
+ it "should never use the rest api server" do
6
+ Koala.should_receive(:make_request).with(
7
+ anything,
8
+ anything,
9
+ anything,
10
+ hash_not_including(:rest_api => true)
11
+ ).and_return(Koala::Response.new(200, "", {}))
12
+
13
+ @api.api("anything")
14
+ end
15
+
16
+ # GRAPH CALL
17
+ describe "graph_call" do
18
+ it "should pass all arguments to the api method" do
19
+ args = ["koppel", {}, "get", {:a => :b}]
20
+
21
+ @api.should_receive(:api).with(*args)
22
+
23
+ @api.graph_call(*args)
24
+ end
25
+
26
+ it "should throw an APIError if the result hash has an error key" do
27
+ Koala.stub(:make_request).and_return(Koala::Response.new(500, {"error" => "An error occurred!"}, {}))
28
+ lambda { @api.graph_call("koppel", {}) }.should raise_exception(Koala::Facebook::APIError)
29
+ end
30
+ end
31
+
32
+ # SEARCH
33
+ it "should be able to search" do
34
+ result = @api.search("facebook")
35
+ result.length.should be_an(Integer)
36
+ end
37
+
38
+ # DATA
39
+ # access public info
40
+
41
+ # get_object
42
+ it "should get public data about a user" do
43
+ result = @api.get_object("koppel")
44
+ # the results should have an ID and a name, among other things
45
+ (result["id"] && result["name"]).should_not be_nil
46
+ end
47
+
48
+ it "should get public data about a Page" do
49
+ result = @api.get_object("contextoptional")
50
+ # the results should have an ID and a name, among other things
51
+ (result["id"] && result["name"]).should
52
+ end
53
+
54
+ it "should be able to get multiple objects" do
55
+ results = @api.get_objects(["contextoptional", "naitik"])
56
+ results.length.should == 2
57
+ end
58
+
59
+ it "should be able to access a user's picture" do
60
+ @api.get_picture("chris.baclig").should =~ /http\:\/\//
61
+ end
62
+
63
+ it "should be able to access a user's picture, given a picture type" do
64
+ @api.get_picture("chris.baclig", {:type => 'large'}).should =~ /^http\:\/\//
65
+ end
66
+
67
+ it "should be able to access connections from public Pages" do
68
+ result = @api.get_connections("contextoptional", "photos")
69
+ result.should be_a(Array)
70
+ end
71
+
72
+ # SEARCH
73
+ it "should be able to search" do
74
+ result = @api.search("facebook")
75
+ result.length.should be_an(Integer)
76
+ end
77
+
78
+ # PAGING THROUGH COLLECTIONS
79
+ # see also graph_collection_tests
80
+ it "should make a request for a page when provided a specific set of page params" do
81
+ query = [1, 2]
82
+ @api.should_receive(:graph_call).with(*query)
83
+ @api.get_page(query)
84
+ end
85
+ end
@@ -1,150 +1,194 @@
1
1
  shared_examples_for "Koala GraphAPI with an access token" do
2
- it "should get public data about a user" do
3
- result = @api.get_object("koppel")
4
- # the results should have an ID and a name, among other things
5
- (result["id"] && result["name"]).should_not be_nil
6
- end
7
2
 
8
- it "should get private data about a user" do
9
- result = @api.get_object("koppel")
10
- # updated_time should be a pretty fixed test case
11
- result["updated_time"].should_not be_nil
12
- end
3
+ it "should get private data about a user" do
4
+ result = @api.get_object("koppel")
5
+ # updated_time should be a pretty fixed test case
6
+ result["updated_time"].should_not be_nil
7
+ end
13
8
 
14
- it "should get public data about a Page" do
15
- result = @api.get_object("contextoptional")
16
- # the results should have an ID and a name, among other things
17
- (result["id"] && result["name"]).should
18
- end
19
-
20
- it "should get data about 'me'" do
21
- result = @api.get_object("me")
22
- result["updated_time"].should
23
- end
24
-
25
- it "should be able to get multiple objects" do
26
- result = @api.get_objects(["contextoptional", "naitik"])
27
- result.length.should == 2
28
- end
9
+ it "should get data about 'me'" do
10
+ result = @api.get_object("me")
11
+ result["updated_time"].should
12
+ end
13
+
14
+ it "should be able to get multiple objects" do
15
+ result = @api.get_objects(["contextoptional", "naitik"])
16
+ result.length.should == 2
17
+ end
18
+ it "should be able to access connections from users" do
19
+ result = @api.get_connections("lukeshepard", "likes")
20
+ result.length.should > 0
21
+ end
22
+
23
+ # PUT
24
+ it "should be able to write an object to the graph" do
25
+ result = @api.put_wall_post("Hello, world, from the test suite!")
26
+ @temporary_object_id = result["id"]
27
+ @temporary_object_id.should_not be_nil
28
+ end
29
+
30
+ # DELETE
31
+ it "should be able to delete posts" do
32
+ result = @api.put_wall_post("Hello, world, from the test suite delete method!")
33
+ object_id_to_delete = result["id"]
34
+ delete_result = @api.delete_object(object_id_to_delete)
35
+ delete_result.should == true
36
+ end
29
37
 
30
- it "should be able to access a user's picture" do
31
- @api.get_picture("chris.baclig").should =~ /http\:\/\//
32
- end
38
+ it "should be able to delete likes" do
39
+ result = @api.put_wall_post("Hello, world, from the test suite delete method!")
40
+ @temporary_object_id = result["id"]
41
+ @api.put_like(@temporary_object_id)
42
+ delete_like_result = @api.delete_like(@temporary_object_id)
43
+ delete_like_result.should == true
44
+ end
45
+
46
+ # additional put tests
47
+ it "should be able to verify messages posted to a wall" do
48
+ message = "the cats are asleep"
49
+ put_result = @api.put_wall_post(message)
50
+ @temporary_object_id = put_result["id"]
51
+ get_result = @api.get_object(@temporary_object_id)
52
+
53
+ # make sure the message we sent is the message that got posted
54
+ get_result["message"].should == message
55
+ end
56
+
57
+ it "should be able to post a message with an attachment to a feed" do
58
+ result = @api.put_wall_post("Hello, world, from the test suite again!", {:name => "Context Optional", :link => "http://www.contextoptional.com/"})
59
+ @temporary_object_id = result["id"]
60
+ @temporary_object_id.should_not be_nil
61
+ end
33
62
 
34
- it "should be able to access a user's picture, given a picture type" do
35
- @api.get_picture("chris.baclig", {:type => 'large'}).should =~ /^http\:\/\//
36
- end
63
+ it "should be able to post photos to the user's wall with an open file object" do
64
+ content_type = "image/jpg"
65
+ file = File.open(File.join(File.dirname(__FILE__), "..", "assets", "beach.jpg"))
66
+
67
+ result = @api.put_picture(file, content_type)
68
+ @temporary_object_id = result["id"]
69
+ @temporary_object_id.should_not be_nil
70
+ end
37
71
 
38
- it "should be able to access connections from users" do
39
- result = @api.get_connections("lukeshepard", "likes")
40
- result.length.should > 0
41
- end
42
-
43
- it "should be able to access connections from public Pages" do
44
- result = @api.get_connections("contextoptional", "photos")
45
- result.should be_a(Array)
46
- end
72
+ it "should be able to post photos to the user's wall without an open file object" do
73
+ content_type = "image/jpg",
74
+ file_path = File.join(File.dirname(__FILE__), "..", "assets", "beach.jpg")
47
75
 
48
- # paging
49
- # see also graph_collection_tests
50
- it "should make a request for a page when provided a specific set of page params" do
51
- query = [1, 2]
52
- @api.should_receive(:graph_call).with(*query)
53
- @api.get_page(query)
54
- end
76
+ result = @api.put_picture(file_path, content_type)
77
+ @temporary_object_id = result["id"]
78
+ @temporary_object_id.should_not be_nil
79
+ end
55
80
 
81
+ it "should be able to verify a photo posted to a user's wall" do
82
+ content_type = "image/jpg",
83
+ file_path = File.join(File.dirname(__FILE__), "..", "assets", "beach.jpg")
56
84
 
57
- # PUT
58
- it "should be able to write an object to the graph" do
59
- result = @api.put_wall_post("Hello, world, from the test suite!")
60
- @temporary_object_id = result["id"]
61
- @temporary_object_id.should_not be_nil
62
- end
85
+ expected_message = "This is the test message"
86
+
87
+ result = @api.put_picture(file_path, content_type, :message => expected_message)
88
+ @temporary_object_id = result["id"]
89
+ @temporary_object_id.should_not be_nil
90
+
91
+ get_result = @api.get_object(@temporary_object_id)
92
+ get_result["name"].should == expected_message
93
+ end
94
+
95
+ it "should be able to verify a message with an attachment posted to a feed" do
96
+ attachment = {"name" => "Context Optional", "link" => "http://www.contextoptional.com/"}
97
+ result = @api.put_wall_post("Hello, world, from the test suite again!", attachment)
98
+ @temporary_object_id = result["id"]
99
+ get_result = @api.get_object(@temporary_object_id)
63
100
 
64
- # DELETE
65
- it "should be able to delete posts" do
66
- result = @api.put_wall_post("Hello, world, from the test suite delete method!")
67
- object_id_to_delete = result["id"]
68
- delete_result = @api.delete_object(object_id_to_delete)
69
- delete_result.should == true
70
- end
101
+ # make sure the result we fetch includes all the parameters we sent
102
+ it_matches = attachment.inject(true) {|valid, param| valid && (get_result[param[0]] == attachment[param[0]])}
103
+ it_matches.should == true
104
+ end
71
105
 
72
- # additional put tests
73
- it "should be able to verify messages posted to a wall" do
74
- message = "the cats are asleep"
75
- put_result = @api.put_wall_post(message)
76
- @temporary_object_id = put_result["id"]
77
- get_result = @api.get_object(@temporary_object_id)
78
-
79
- # make sure the message we sent is the message that got posted
80
- get_result["message"].should == message
81
- end
106
+ it "should be able to comment on an object" do
107
+ result = @api.put_wall_post("Hello, world, from the test suite, testing comments!")
108
+ @temporary_object_id = result["id"]
82
109
 
83
- it "should be able to post a message with an attachment to a feed" do
84
- result = @api.put_wall_post("Hello, world, from the test suite again!", {:name => "Context Optional", :link => "http://www.contextoptional.com/"})
85
- @temporary_object_id = result["id"]
86
- @temporary_object_id.should_not be_nil
87
- end
88
-
89
- it "should be able to verify a message with an attachment posted to a feed" do
90
- attachment = {"name" => "Context Optional", "link" => "http://www.contextoptional.com/"}
91
- result = @api.put_wall_post("Hello, world, from the test suite again!", attachment)
92
- @temporary_object_id = result["id"]
93
- get_result = @api.get_object(@temporary_object_id)
94
-
95
- # make sure the result we fetch includes all the parameters we sent
96
- it_matches = attachment.inject(true) {|valid, param| valid && (get_result[param[0]] == attachment[param[0]])}
97
- it_matches.should == true
98
- end
110
+ # this will be deleted when the post gets deleted
111
+ comment_result = @api.put_comment(@temporary_object_id, "it's my comment!")
112
+ comment_result.should_not be_nil
113
+ end
99
114
 
100
- it "should be able to comment on an object" do
101
- result = @api.put_wall_post("Hello, world, from the test suite, testing comments!")
102
- @temporary_object_id = result["id"]
103
-
104
- # this will be deleted when the post gets deleted
105
- comment_result = @api.put_comment(@temporary_object_id, "it's my comment!")
106
- comment_result.should_not be_nil
107
- end
108
-
109
- it "should be able to verify a comment posted about an object" do
110
- message_text = "Hello, world, from the test suite, testing comments!"
111
- result = @api.put_wall_post(message_text)
112
- @temporary_object_id = result["id"]
113
-
114
- # this will be deleted when the post gets deleted
115
- comment_text = "it's my comment!"
116
- comment_result = @api.put_comment(@temporary_object_id, comment_text)
117
- get_result = @api.get_object(comment_result["id"])
118
-
119
- # make sure the text of the comment matches what we sent
120
- get_result["message"].should == comment_text
121
- end
115
+ it "should be able to verify a comment posted about an object" do
116
+ message_text = "Hello, world, from the test suite, testing comments!"
117
+ result = @api.put_wall_post(message_text)
118
+ @temporary_object_id = result["id"]
122
119
 
123
- it "should be able to like an object" do
124
- result = @api.put_wall_post("Hello, world, from the test suite, testing comments!")
125
- @temporary_object_id = result["id"]
126
- like_result = @api.put_like(@temporary_object_id)
127
- end
120
+ # this will be deleted when the post gets deleted
121
+ comment_text = "it's my comment!"
122
+ comment_result = @api.put_comment(@temporary_object_id, comment_text)
123
+ get_result = @api.get_object(comment_result["id"])
128
124
 
129
- # SEARCH
130
- it "should be able to search" do
131
- result = @api.search("facebook")
132
- result.length.should be_an(Integer)
133
- end
125
+ # make sure the text of the comment matches what we sent
126
+ get_result["message"].should == comment_text
127
+ end
134
128
 
135
- # API
136
- # the above tests test this already, but we should consider additional api tests
129
+ it "should be able to like an object" do
130
+ result = @api.put_wall_post("Hello, world, from the test suite, testing comments!")
131
+ @temporary_object_id = result["id"]
132
+ like_result = @api.put_like(@temporary_object_id)
133
+ like_result.should be_true
134
+ end
135
+
136
+
137
+ # test all methods to make sure they pass data through to the API
138
+ # we run the tests here (rather than in the common shared example group)
139
+ # since some require access tokens
140
+ describe "HTTP options" do
141
+ # Each of the below methods should take an options hash as their last argument
142
+ # ideally we'd use introspection to determine how many arguments a method has
143
+ # but some methods require specially formatted arguments for processing
144
+ # (and anyway, Ruby 1.8's arity method fails (for this) for methods w/ 2+ optional arguments)
145
+ # (Ruby 1.9's parameters method is perfect, but only in 1.9)
146
+ # so we have to double-document
147
+ {
148
+ :get_object => 3, :put_object => 4, :delete_object => 2,
149
+ :get_connections => 4, :put_connections => 4, :delete_connections => 4,
150
+ :put_wall_post => 4,
151
+ :put_comment => 3,
152
+ :put_like => 2, :delete_like => 2,
153
+ :search => 3,
154
+ # methods that have special arguments
155
+ :put_picture => ["x.jpg", "image/jpg", {}, "me"],
156
+ :get_objects => [["x"], {}]
157
+ }.each_pair do |method_name, params|
158
+ it "should pass http options through for #{method_name}" do
159
+ options = {:a => 2}
160
+ # graph call should ultimately receive options as the fourth argument
161
+ @api.should_receive(:graph_call).with(anything, anything, anything, options)
137
162
 
138
- it_should_behave_like "Koala GraphAPI with GraphCollection"
163
+ # if we supply args, use them (since some methods process params)
164
+ # the method should receive as args n-1 anythings and then options
165
+ args = (params.is_a?(Integer) ? ([{}] * (params - 1)) : params) + [options]
166
+
167
+ @api.send(method_name, *args)
168
+ end
169
+ end
170
+
171
+ # also test get_picture, which merges a parameter into options
172
+ it "should pass http options through for get_picture" do
173
+ options = {:a => 2}
174
+ # graph call should ultimately receive options as the fourth argument
175
+ @api.should_receive(:graph_call).with(anything, anything, anything, hash_including(options)).and_return({})
176
+ @api.send(:get_picture, "x", {}, options)
177
+ end
178
+ end
139
179
  end
140
180
 
141
181
  class FacebookWithAccessTokenTests < Test::Unit::TestCase
142
182
  describe "Koala GraphAPI with an access token" do
143
183
  include LiveTestingDataHelper
144
- it_should_behave_like "Koala GraphAPI with an access token"
145
-
184
+
146
185
  before :each do
147
186
  @api = Koala::Facebook::GraphAPI.new(@token)
148
187
  end
188
+
189
+ it_should_behave_like "Koala GraphAPI"
190
+ it_should_behave_like "Koala GraphAPI with an access token"
191
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
192
+
149
193
  end
150
194
  end
@@ -0,0 +1,51 @@
1
+ require 'koala/http_services'
2
+ class HTTPServiceTests < Test::Unit::TestCase
3
+ class Bear
4
+ include Koala::HTTPService
5
+ end
6
+
7
+ describe "common methods" do
8
+ describe "always_use_ssl accessor" do
9
+ it "should be added" do
10
+ # in Ruby 1.8, .methods returns strings
11
+ # in Ruby 1.9, .method returns symbols
12
+ Bear.methods.collect {|m| m.to_sym}.should include(:always_use_ssl)
13
+ Bear.methods.collect {|m| m.to_sym}.should include(:always_use_ssl=)
14
+ end
15
+ end
16
+
17
+ describe "server" do
18
+ describe "without options[:beta]" do
19
+ it "should return the rest server if options[:rest_api]" do
20
+ Bear.server(:rest_api => true).should == Koala::Facebook::REST_SERVER
21
+ end
22
+
23
+ it "should return the rest server if !options[:rest_api]" do
24
+ Bear.server(:rest_api => false).should == Koala::Facebook::GRAPH_SERVER
25
+ Bear.server({}).should == Koala::Facebook::GRAPH_SERVER
26
+ end
27
+ end
28
+
29
+ describe "without options[:beta]" do
30
+ before :each do
31
+ @options = {:beta => true}
32
+ end
33
+
34
+ it "should return the rest server if options[:rest_api]" do
35
+ server = Bear.server(@options.merge(:rest_api => true))
36
+ server.should =~ Regexp.new(Koala::Facebook::REST_SERVER)
37
+ server.should =~ /beta\./
38
+ end
39
+
40
+ it "should return the rest server if !options[:rest_api]" do
41
+ server = Bear.server(:beta => true)
42
+ server.should =~ Regexp.new(Koala::Facebook::GRAPH_SERVER)
43
+ server.should =~ /beta\./
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end