koala 0.9.0 → 1.0.0.beta

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 (46) hide show
  1. data/CHANGELOG +33 -7
  2. data/Manifest +6 -14
  3. data/Rakefile +4 -3
  4. data/koala.gemspec +13 -8
  5. data/lib/koala/graph_api.rb +16 -3
  6. data/lib/koala/http_services.rb +100 -16
  7. data/lib/koala/rest_api.rb +73 -6
  8. data/lib/koala/test_users.rb +72 -0
  9. data/lib/koala.rb +101 -71
  10. data/readme.md +14 -13
  11. data/spec/facebook_data.yml +14 -8
  12. data/spec/koala/api_base_tests.rb +7 -0
  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 +9 -4
  16. data/spec/koala/graph_api/graph_api_no_access_token_tests.rb +10 -61
  17. data/spec/koala/graph_api/graph_api_tests.rb +86 -0
  18. data/spec/koala/graph_api/graph_api_with_access_token_tests.rb +130 -126
  19. data/spec/koala/graph_api/graph_collection_tests.rb +2 -2
  20. data/spec/koala/live_testing_data_helper.rb +40 -12
  21. data/spec/koala/net_http_service_tests.rb +401 -152
  22. data/spec/koala/oauth/oauth_tests.rb +41 -72
  23. data/spec/koala/rest_api/rest_api_no_access_token_tests.rb +7 -76
  24. data/spec/koala/rest_api/rest_api_tests.rb +118 -0
  25. data/spec/koala/rest_api/rest_api_with_access_token_tests.rb +6 -4
  26. data/spec/koala/test_users/test_users_tests.rb +208 -0
  27. data/spec/koala/typhoeus_service_tests.rb +156 -0
  28. data/spec/koala_spec_helper.rb +43 -4
  29. data/spec/koala_spec_without_mocks.rb +4 -4
  30. data/spec/mock_facebook_responses.yml +69 -3
  31. data/spec/mock_http_service.rb +16 -1
  32. metadata +49 -23
  33. data/examples/oauth_playground/Capfile +0 -2
  34. data/examples/oauth_playground/LICENSE +0 -22
  35. data/examples/oauth_playground/Rakefile +0 -4
  36. data/examples/oauth_playground/config/deploy.rb +0 -39
  37. data/examples/oauth_playground/config/facebook.yml +0 -13
  38. data/examples/oauth_playground/config.ru +0 -27
  39. data/examples/oauth_playground/lib/load_facebook.rb +0 -3
  40. data/examples/oauth_playground/lib/oauth_playground.rb +0 -187
  41. data/examples/oauth_playground/readme.md +0 -8
  42. data/examples/oauth_playground/spec/oauth_playground_spec.rb +0 -35
  43. data/examples/oauth_playground/spec/spec_helper.rb +0 -36
  44. data/examples/oauth_playground/tmp/restart.txt +0 -0
  45. data/examples/oauth_playground/views/index.erb +0 -206
  46. data/examples/oauth_playground/views/layout.erb +0 -39
@@ -0,0 +1,86 @@
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
+ it "should get public data about a user" do
42
+ result = @api.get_object("koppel")
43
+ # the results should have an ID and a name, among other things
44
+ (result["id"] && result["name"]).should_not be_nil
45
+ end
46
+
47
+ it "should get public data about a Page" do
48
+ result = @api.get_object("contextoptional")
49
+ # the results should have an ID and a name, among other things
50
+ (result["id"] && result["name"]).should
51
+ end
52
+
53
+ it "should be able to get multiple objects" do
54
+ results = @api.get_objects(["contextoptional", "naitik"])
55
+ results.length.should == 2
56
+ end
57
+
58
+ it "should be able to access a user's picture" do
59
+ @api.get_picture("chris.baclig").should =~ /http\:\/\//
60
+ end
61
+
62
+ it "should be able to access a user's picture, given a picture type" do
63
+ @api.get_picture("chris.baclig", {:type => 'large'}).should =~ /^http\:\/\//
64
+ end
65
+
66
+ it "should be able to access connections from public Pages" do
67
+ result = @api.get_connections("contextoptional", "photos")
68
+ result.should be_a(Array)
69
+ end
70
+
71
+ # SEARCH
72
+ it "should be able to search" do
73
+ result = @api.search("facebook")
74
+ result.length.should be_an(Integer)
75
+ end
76
+
77
+ # PAGING THROUGH COLLECTIONS
78
+ # see also graph_collection_tests
79
+ it "should make a request for a page when provided a specific set of page params" do
80
+ query = [1, 2]
81
+ @api.should_receive(:graph_call).with(*query)
82
+ @api.get_page(query)
83
+ end
84
+
85
+
86
+ end
@@ -1,150 +1,154 @@
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
29
-
30
- it "should be able to access a user's picture" do
31
- @api.get_picture("chris.baclig").should =~ /http\:\/\//
32
- end
33
-
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
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
37
37
 
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
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
42
45
 
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
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
62
+
63
+ it "should be able to post photos to the user's wall with an open file object" do
64
+ file_hash = {
65
+ "content_type" => "image/jpg",
66
+ "path" => File.join(File.dirname(__FILE__), "..", "assets", "beach.jpg"),
67
+ "file" => File.open(File.join(File.dirname(__FILE__), "..", "assets", "beach.jpg"))
68
+ }
69
+ result = @api.put_picture(file_hash)
70
+ @temporary_object_id = result["id"]
71
+ @temporary_object_id.should_not be_nil
72
+ end
73
+
74
+ it "should be able to post photos to the user's wall without an open file object" do
75
+ file_hash = {
76
+ "content_type" => "image/jpg",
77
+ "path" => File.join(File.dirname(__FILE__), "..", "assets", "beach.jpg")
78
+ }
79
+ result = @api.put_picture(file_hash)
80
+ @temporary_object_id = result["id"]
81
+ @temporary_object_id.should_not be_nil
82
+ end
47
83
 
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
84
+ it "should be able to verify a photo posted to a user's wall" do
85
+ file_hash = {
86
+ "content_type" => "image/jpg",
87
+ "path" => File.join(File.dirname(__FILE__), "..", "assets", "beach.jpg")
88
+ }
89
+ expected_message = "This is the test message"
55
90
 
91
+ result = @api.put_picture(file_hash, :message => expected_message)
92
+ @temporary_object_id = result["id"]
93
+ @temporary_object_id.should_not be_nil
56
94
 
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
63
-
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
95
+ get_result = @api.get_object(@temporary_object_id)
96
+ get_result["name"].should == expected_message
97
+ end
98
+
99
+ it "should be able to verify a message with an attachment posted to a feed" do
100
+ attachment = {"name" => "Context Optional", "link" => "http://www.contextoptional.com/"}
101
+ result = @api.put_wall_post("Hello, world, from the test suite again!", attachment)
102
+ @temporary_object_id = result["id"]
103
+ get_result = @api.get_object(@temporary_object_id)
71
104
 
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
105
+ # make sure the result we fetch includes all the parameters we sent
106
+ it_matches = attachment.inject(true) {|valid, param| valid && (get_result[param[0]] == attachment[param[0]])}
107
+ it_matches.should == true
108
+ end
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
+ it "should be able to comment on an object" do
111
+ result = @api.put_wall_post("Hello, world, from the test suite, testing comments!")
112
+ @temporary_object_id = result["id"]
99
113
 
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
114
+ # this will be deleted when the post gets deleted
115
+ comment_result = @api.put_comment(@temporary_object_id, "it's my comment!")
116
+ comment_result.should_not be_nil
117
+ end
122
118
 
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
119
+ it "should be able to verify a comment posted about an object" do
120
+ message_text = "Hello, world, from the test suite, testing comments!"
121
+ result = @api.put_wall_post(message_text)
122
+ @temporary_object_id = result["id"]
128
123
 
129
- # SEARCH
130
- it "should be able to search" do
131
- result = @api.search("facebook")
132
- result.length.should be_an(Integer)
133
- end
124
+ # this will be deleted when the post gets deleted
125
+ comment_text = "it's my comment!"
126
+ comment_result = @api.put_comment(@temporary_object_id, comment_text)
127
+ get_result = @api.get_object(comment_result["id"])
134
128
 
135
- # API
136
- # the above tests test this already, but we should consider additional api tests
129
+ # make sure the text of the comment matches what we sent
130
+ get_result["message"].should == comment_text
131
+ end
137
132
 
138
- it_should_behave_like "Koala GraphAPI with GraphCollection"
133
+ it "should be able to like an object" do
134
+ result = @api.put_wall_post("Hello, world, from the test suite, testing comments!")
135
+ @temporary_object_id = result["id"]
136
+ like_result = @api.put_like(@temporary_object_id)
137
+ like_result.should be_true
138
+ end
139
139
  end
140
140
 
141
141
  class FacebookWithAccessTokenTests < Test::Unit::TestCase
142
142
  describe "Koala GraphAPI with an access token" do
143
- it_should_behave_like "live testing examples"
144
- it_should_behave_like "Koala GraphAPI with an access token"
145
-
143
+ include LiveTestingDataHelper
144
+
146
145
  before :each do
147
146
  @api = Koala::Facebook::GraphAPI.new(@token)
148
147
  end
148
+
149
+ it_should_behave_like "Koala GraphAPI"
150
+ it_should_behave_like "Koala GraphAPI with an access token"
151
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
152
+
149
153
  end
150
154
  end
@@ -65,7 +65,7 @@ shared_examples_for "Koala GraphAPI with GraphCollection" do
65
65
  @api.should_receive(:graph_call).with(@base, @args).and_return(@second_page)
66
66
  Koala::Facebook::GraphCollection.should_receive(:new).with(@second_page, @api).and_return(@page_of_results)
67
67
 
68
- @result.previous_page(@api).should == @page_of_results
68
+ @result.previous_page.should == @page_of_results
69
69
  end
70
70
 
71
71
  it "should return the next page of results" do
@@ -79,7 +79,7 @@ shared_examples_for "Koala GraphAPI with GraphCollection" do
79
79
  it "should return nil it there are no other pages" do
80
80
  %w{next previous}.each do |this|
81
81
  @result.should_receive("#{this}_page_params".to_sym).and_return(nil)
82
- @result.send("#{this}_page", @api).should == nil
82
+ @result.send("#{this}_page").should == nil
83
83
  end
84
84
  end
85
85
  end
@@ -1,15 +1,43 @@
1
- shared_examples_for "live testing examples" do
2
- before :each do
3
- @token = $testing_data["oauth_token"]
4
- raise Exception, "Must supply access token to run FacebookWithAccessTokenTests!" unless @token
5
- end
6
-
7
- after :each do
8
- # clean up any temporary objects
9
- if @temporary_object_id
10
- puts "\nCleaning up temporary object #{@temporary_object_id.to_s}"
11
- result = @api.delete_object(@temporary_object_id)
12
- raise "Unable to clean up temporary Graph object #{@temporary_object_id}!" unless result
1
+ module LiveTestingDataHelper
2
+ # in RSpec 2, included example groups no longer share any hooks or state with outside examples
3
+ # even if in the same block
4
+ # so we have to use a module to provide setup and teardown hooks for live testing
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ before :each do
9
+ @token = $testing_data["oauth_token"]
10
+ raise Exception, "Must supply access token to run FacebookWithAccessTokenTests!" unless @token
11
+ # track temporary objects created
12
+ @temporary_object_ids = []
13
+ end
14
+
15
+ after :each do
16
+ # clean up any temporary objects
17
+ @temporary_object_ids << @temporary_object_id if @temporary_object_id
18
+ count = @temporary_object_ids.length
19
+ errors = []
20
+
21
+ if count > 0
22
+ print "\nCleaning up #{count} temporary #{count > 1 ? "objects" : "object (#{@temporary_object_ids.first})"}..."
23
+ @temporary_object_ids.each do |id|
24
+ # get our API
25
+ api = @api || (@test_users ? @test_users.graph_api : nil)
26
+ raise "Unable to locate API when passed temporary object to delete!" unless api
27
+
28
+ # delete the object
29
+ result = (api.delete_object(id) rescue false)
30
+ # if we errored out or Facebook returned false, track that
31
+ errors << id unless result
32
+ end
33
+
34
+ if errors.length == 0
35
+ puts "done."
36
+ else
37
+ puts "cleaned up #{count - errors.length} objects, but errored out on the following:\n #{errors.join(", ")}"
38
+ end
39
+ end
13
40
  end
14
41
  end
42
+ end
15
43
  end