restfully 0.2.1

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 (42) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +90 -0
  5. data/Rakefile +75 -0
  6. data/TODO.rdoc +3 -0
  7. data/VERSION +1 -0
  8. data/bin/restfully +80 -0
  9. data/examples/grid5000.rb +28 -0
  10. data/lib/restfully.rb +19 -0
  11. data/lib/restfully/collection.rb +63 -0
  12. data/lib/restfully/error.rb +4 -0
  13. data/lib/restfully/extensions.rb +41 -0
  14. data/lib/restfully/http.rb +9 -0
  15. data/lib/restfully/http/adapters/abstract_adapter.rb +30 -0
  16. data/lib/restfully/http/adapters/patron_adapter.rb +16 -0
  17. data/lib/restfully/http/adapters/rest_client_adapter.rb +31 -0
  18. data/lib/restfully/http/error.rb +20 -0
  19. data/lib/restfully/http/headers.rb +20 -0
  20. data/lib/restfully/http/request.rb +24 -0
  21. data/lib/restfully/http/response.rb +19 -0
  22. data/lib/restfully/link.rb +35 -0
  23. data/lib/restfully/parsing.rb +31 -0
  24. data/lib/restfully/resource.rb +117 -0
  25. data/lib/restfully/session.rb +61 -0
  26. data/lib/restfully/special_array.rb +5 -0
  27. data/lib/restfully/special_hash.rb +5 -0
  28. data/restfully.gemspec +99 -0
  29. data/spec/collection_spec.rb +93 -0
  30. data/spec/fixtures/grid5000-sites.json +489 -0
  31. data/spec/http/error_spec.rb +18 -0
  32. data/spec/http/headers_spec.rb +17 -0
  33. data/spec/http/request_spec.rb +45 -0
  34. data/spec/http/response_spec.rb +15 -0
  35. data/spec/http/rest_client_adapter_spec.rb +33 -0
  36. data/spec/link_spec.rb +58 -0
  37. data/spec/parsing_spec.rb +25 -0
  38. data/spec/resource_spec.rb +198 -0
  39. data/spec/restfully_spec.rb +13 -0
  40. data/spec/session_spec.rb +105 -0
  41. data/spec/spec_helper.rb +13 -0
  42. metadata +117 -0
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__)+'/../spec_helper'
2
+ describe Restfully::HTTP::Error do
3
+ it "should have a response reader" do
4
+ response = mock("restfully response", :status => 404, :body => {'title' => 'Not Found', 'message' => 'The requested resource cannot be found.', 'code' => 404})
5
+ error = Restfully::HTTP::Error.new(response)
6
+ error.response.should == response
7
+ end
8
+ it "should work properly" do
9
+ response = mock("restfully response", :status => 404, :body => {'title' => 'Not Found', 'message' => 'The requested resource cannot be found.', 'code' => 404})
10
+ begin
11
+ raise Restfully::HTTP::Error.new(response)
12
+ rescue Restfully::HTTP::Error => e
13
+ e.response.should == response
14
+ e.message.should == "404 Not Found. The requested resource cannot be found."
15
+ e.backtrace.should_not be_empty
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__)+'/../spec_helper'
2
+ describe Restfully::HTTP::Headers do
3
+ class IncludeHeadersModule
4
+ include Restfully::HTTP::Headers
5
+ end
6
+
7
+ it "should correctly parse headers" do
8
+ sanitized_headers = IncludeHeadersModule.new.sanitize_http_headers('accept' => 'application/json', :x_remote_ident => 'crohr', 'X_GVI' => 'sid', 'CACHE-CONTROL' => ['max-age=0', 'no-cache'], 'Content-Length' => 22)
9
+ sanitized_headers.should == {
10
+ 'Accept' => 'application/json',
11
+ 'X-Remote-Ident' => 'crohr',
12
+ 'X-Gvi' => 'sid',
13
+ 'Cache-Control' => 'max-age=0, no-cache',
14
+ 'Content-Length' => 22
15
+ }
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__)+'/../spec_helper'
2
+ describe Restfully::HTTP::Request do
3
+
4
+ it "should correctly initialize the attributes" do
5
+ request = Restfully::HTTP::Request.new(
6
+ 'https://api.grid5000.fr/sid/grid5000?q1=v1&q2=v2',
7
+ :headers => {'accept' => 'application/json', :cache_control => 'max-age=0'},
8
+ :query => {'custom_param1' => [3, 4, 5, 6], 'custom_param2' => 'value_custom_param2'}
9
+ )
10
+ request.uri.should be_a URI
11
+ request.uri.to_s.should == 'https://api.grid5000.fr/sid/grid5000?q1=v1&q2=v2&custom_param1=3,4,5,6&custom_param2=value_custom_param2'
12
+ request.uri.query.should == "q1=v1&q2=v2&custom_param1=3,4,5,6&custom_param2=value_custom_param2"
13
+ request.headers.should == {
14
+ 'Accept' => 'application/json',
15
+ 'Cache-Control' => 'max-age=0'
16
+ }
17
+ request.retries.should == 0
18
+ end
19
+
20
+ it "should accept a URI object as url" do
21
+ request = Restfully::HTTP::Request.new(uri=URI.parse('https://api.grid5000.fr/sid/grid5000'))
22
+ request.uri.to_s.should == 'https://api.grid5000.fr/sid/grid5000'
23
+ end
24
+
25
+ it "should not fail if there are query parameters but no query string in the given URL" do
26
+ request = Restfully::HTTP::Request.new('https://api.grid5000.fr/grid5000', :query => {:q1 => 'v1'})
27
+ request.uri.query.should == "q1=v1"
28
+ end
29
+
30
+ it "should not change the query string if none is given as an option" do
31
+ request = Restfully::HTTP::Request.new('https://api.grid5000.fr/grid5000?q1=v1&q2=v2')
32
+ request.uri.to_s.should == 'https://api.grid5000.fr/grid5000?q1=v1&q2=v2'
33
+ request.headers.should == {}
34
+ end
35
+
36
+ it "should offer a function to add headers" do
37
+ request = Restfully::HTTP::Request.new('https://api.grid5000.fr/grid5000?q1=v1&q2=v2', :headers => {:accept => 'application/json'})
38
+ expected_headers = {
39
+ 'Accept' => 'application/json',
40
+ 'Cache-Control' => 'max-age=0'
41
+ }
42
+ request.add_headers(:cache_control => 'max-age=0').should == expected_headers
43
+ request.headers.should == expected_headers
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__)+'/../spec_helper'
2
+ describe Restfully::HTTP::Response do
3
+
4
+ it "should correctly initialize the attributes" do
5
+ response = Restfully::HTTP::Response.new(404, {:content_type => 'application/json;charset=utf-8'}, '{"property1": "value1", "property2": "value2"}')
6
+ response.status.should == 404
7
+ response.headers.should == {
8
+ 'Content-Type' => 'application/json;charset=utf-8'
9
+ }
10
+ response.body.should == {
11
+ 'property1' => 'value1',
12
+ 'property2' => 'value2'
13
+ }
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__)+'/../spec_helper'
2
+ describe Restfully::HTTP::Adapters::RestClientAdapter do
3
+ it "should correctly get the resource corresponding to the request" do
4
+ adapter = Restfully::HTTP::Adapters::RestClientAdapter.new("https://api.grid5000.fr", :username => 'crohr', :password => 'password')
5
+ request = Restfully::HTTP::Request.new('http://api.local/sid/grid5000', :headers => {:accept => 'application/json'}, :query => {:q1 => 'v1'})
6
+ RestClient::Resource.should_receive(:new).with('http://api.local/sid/grid5000?q1=v1', :password => 'password', :user => 'crohr').and_return(resource = mock("restclient resource"))
7
+ resource.should_receive(:get).with(request.headers).and_return(mock("restclient response", :headers => {:content_type => 'application/json;charset=utf-8'}, :to_s => "", :code => 200))
8
+ response = adapter.get(request)
9
+ response.status.should == 200
10
+ response.body.should be_nil
11
+ response.headers.should == {
12
+ 'Content-Type' => 'application/json;charset=utf-8'
13
+ }
14
+ end
15
+ it "should transform the username option into a user option" do
16
+ adapter = Restfully::HTTP::Adapters::RestClientAdapter.new("https://api.grid5000.fr", :username => 'crohr', :password => 'password')
17
+ adapter.options[:user].should == 'crohr'
18
+ adapter.options[:username].should be_nil
19
+ end
20
+ it "should raise a not implemented error when trying to use functions not implemented yet" do
21
+ adapter = Restfully::HTTP::Adapters::RestClientAdapter.new("https://api.grid5000.fr")
22
+ lambda{adapter.post(mock("restfully request"))}.should raise_error NotImplementedError, "POST is not supported by your adapter."
23
+ end
24
+ it "should rescue any RestClient::Exception and correctly populate the response" do
25
+ res = mock(Net::HTTPResponse, :code => 404, :body => '{"message":"whatever"}', :to_hash => {'Content-Type' => 'application/json;charset=utf-8', 'Content-Length' => 22}, :[] => '')
26
+ RestClient::Resource.should_receive(:new).and_raise RestClient::ResourceNotFound.new(res)
27
+ adapter = Restfully::HTTP::Adapters::RestClientAdapter.new("https://api.grid5000.fr", :username => 'crohr', :password => 'password')
28
+ response = adapter.get(mock("request", :uri => "uri"))
29
+ response.status.should == 404
30
+ response.headers.should == {'Content-Type' => 'application/json;charset=utf-8', 'Content-Length' => 22}
31
+ response.body.should == {'message' => 'whatever'}
32
+ end
33
+ end
data/spec/link_spec.rb ADDED
@@ -0,0 +1,58 @@
1
+ require File.dirname(__FILE__)+'/spec_helper'
2
+
3
+ include Restfully
4
+ describe Link do
5
+
6
+ it "should have a rel reader" do
7
+ link = Link.new
8
+ link.should_not respond_to(:rel=)
9
+ link.should respond_to(:rel)
10
+ end
11
+ it "should have a href reader" do
12
+ link = Link.new
13
+ link.should_not respond_to(:href=)
14
+ link.should respond_to(:href)
15
+ end
16
+ it "should have a title reader" do
17
+ link = Link.new
18
+ link.should_not respond_to(:title=)
19
+ link.should respond_to(:title)
20
+ end
21
+ it "should have a errors reader" do
22
+ link = Link.new
23
+ link.should_not respond_to(:errors=)
24
+ link.should respond_to(:errors)
25
+ end
26
+ it "should respond to valid?" do
27
+ link = Link.new
28
+ link.should respond_to(:valid?)
29
+ end
30
+ it "should respond to resolved?" do
31
+ link = Link.new
32
+ link.should respond_to(:resolved?)
33
+ end
34
+ it "should respond to resolvable?" do
35
+ link = Link.new
36
+ link.should respond_to(:resolvable?)
37
+ end
38
+ it "by default, should not be resolvable" do
39
+ link = Link.new
40
+ link.should_not be_resolvable
41
+ end
42
+ it "by default, should not be resolved" do
43
+ link = Link.new
44
+ link.should_not be_resolved
45
+ end
46
+ it "should not be valid if there is no href" do
47
+ link = Link.new 'rel' => 'collection', 'title' => 'my collection'
48
+ link.should_not be_valid
49
+ end
50
+ it "should not be valid if there is no rel" do
51
+ link = Link.new 'href' => '/', 'title' => 'my collection'
52
+ link.should_not be_valid
53
+ end
54
+ it "should not be valid if the rel is valid but requires a title that is not given" do
55
+ link = Link.new 'rel' => 'collection', 'href' => '/'
56
+ link.should_not be_valid
57
+ end
58
+ end
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__)+'/spec_helper'
2
+
3
+ include Restfully::Parsing
4
+
5
+ describe Restfully::Parsing do
6
+ class IncludesParsingModule
7
+ include Restfully::Parsing
8
+ end
9
+ it "should make available the serialize and unserialize methods" do
10
+ klass = IncludesParsingModule.new
11
+ klass.should respond_to(:unserialize)
12
+ klass.should respond_to(:serialize)
13
+ end
14
+ it "should raise a ParserNotFound error if the object cannot be parsed" do
15
+ lambda{unserialize("whatever", :content_type => 'unknown')}.should raise_error(Restfully::Parsing::ParserNotFound, "Content-Type 'unknown' is not supported. Cannot parse the given object.")
16
+ end
17
+ it "should correctly unserialize json content" do
18
+ object = {'p1' => 'v1'}
19
+ unserialize(object.to_json, :content_type => 'application/json;charset=utf-8').should == object
20
+ end
21
+ it "should correctly serialize an object into json" do
22
+ object = {'p1' => 'v1'}
23
+ serialize(object, :content_type => 'application/json;charset=utf-8').should == object.to_json
24
+ end
25
+ end
@@ -0,0 +1,198 @@
1
+ require File.dirname(__FILE__)+'/spec_helper'
2
+
3
+ include Restfully
4
+
5
+ describe Resource do
6
+ before do
7
+ @logger = Logger.new(STDOUT)
8
+ end
9
+
10
+ it "should have all the methods of a hash" do
11
+ resource = Resource.new("uri", session=mock('session'))
12
+ resource.size.should == 0
13
+ resource['whatever'] = 'thing'
14
+ resource.size.should == 1
15
+ resource.should == {'whatever' => 'thing'}
16
+ resource.should respond_to(:each)
17
+ resource.should respond_to(:length)
18
+ end
19
+
20
+ describe "accessors" do
21
+ it "should have a reader on the session" do
22
+ resource = Resource.new("uri", session=mock("session"))
23
+ resource.should_not respond_to(:session=)
24
+ resource.session.should == session
25
+ end
26
+ it "should have a reader on the uri" do
27
+ resource = Resource.new("uri", session=mock("session"))
28
+ resource.should_not respond_to(:uri=)
29
+ resource.uri.should == "uri"
30
+ end
31
+ it "should have a reader on the raw property" do
32
+ resource = Resource.new("uri", session=mock("session"), 'raw' => {})
33
+ resource.should_not respond_to(:raw=)
34
+ resource.raw.should == {}
35
+ end
36
+ it "should have a reader on the state property" do
37
+ resource = Resource.new("uri", session=mock("session"))
38
+ resource.should_not respond_to(:state=)
39
+ resource.state.should == :unloaded
40
+ end
41
+ end
42
+
43
+
44
+ describe "loading" do
45
+
46
+ before do
47
+ @raw = {
48
+ 'links' => [
49
+ {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
50
+ {'rel' => 'parent', 'href' => '/grid5000'},
51
+ {'rel' => 'invalid_rel', 'href' => '/whatever'},
52
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/status', 'title' => 'status'},
53
+ {'rel' => 'member', 'href' => '/grid5000/sites/rennes/versions/123', 'title' => 'version'},
54
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/versions', 'resolvable' => false, 'title' => 'versions'},
55
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters', 'resolvable' => true, 'resolved' => true, 'title' => 'clusters'},
56
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/environments/versions/123', 'resolvable' => true, 'resolved' => false, 'title' => 'environments'},
57
+ {'rel' => 'collection', 'href' => '/has/no/title'}
58
+ ],
59
+ 'uid' => 'rennes',
60
+ 'whatever' => 'whatever',
61
+ 'an_array' => [1, 2, 3],
62
+ 'clusters' => {
63
+ 'paradent' => {
64
+ 'uid' => 'paradent',
65
+ 'links' => [
66
+ {'rel' => 'self', 'href' => '/grid5000/sites/rennes/clusters/paradent'},
67
+ {'rel' => 'parent', 'href' => '/grid5000/sites/rennes'},
68
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paradent/nodes', 'title' => 'nodes', 'resolvable' => true, 'resolved' => false},
69
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paradent/versions', 'resolvable' => false, 'title' => 'versions'},
70
+ {'rel' => 'member', 'href' => '/grid5000/sites/rennes/clusters/paradent/versions/123', 'title' => 'version'},
71
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paradent/status', 'title' => 'status'}
72
+ ],
73
+ 'model' => 'XYZ'
74
+ },
75
+ 'paramount' => {
76
+ 'uid' => 'paramount',
77
+ 'links' => [
78
+ {'rel' => 'self', 'href' => '/grid5000/sites/rennes/clusters/paramount'},
79
+ {'rel' => 'parent', 'href' => '/grid5000/sites/rennes'},
80
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paramount/nodes', 'title' => 'nodes', 'resolvable' => true, 'resolved' => false},
81
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paramount/versions', 'resolvable' => false, 'title' => 'versions'},
82
+ {'rel' => 'member', 'href' => '/grid5000/sites/rennes/clusters/paramount/versions/123', 'title' => 'version'},
83
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paramount/status', 'title' => 'status'}
84
+ ],
85
+ 'model' => 'XYZ1b'
86
+ }
87
+ }
88
+ }.to_json
89
+ @response_200 = Restfully::HTTP::Response.new(200, {'Content-Type' => 'application/json;utf-8', 'Content-Length' => @raw.length}, @raw)
90
+ end
91
+ it "should not be loaded in its initial state" do
92
+ resource = Resource.new(mock("uri"), mock('session'))
93
+ resource.should_not be_loaded
94
+ end
95
+ it "should get the raw representation of the resource via the session if it doesn't have it" do
96
+ resource = Resource.new(uri=mock("uri"), session = mock("session", :logger => Logger.new(STDOUT)))
97
+ resource.stub!(:define_link) # do not define links
98
+ resource.raw.should be_nil
99
+ session.should_receive(:get).with(uri, {}).and_return(@response_200)
100
+ resource.load
101
+ resource.should be_loaded
102
+ end
103
+ it "should get the raw representation of the resource via the session if there are query parameters" do
104
+ resource = Resource.new(uri=mock("uri"), session = mock("session", :logger => Logger.new(STDOUT)))
105
+ resource.stub!(:define_link) # do not define links
106
+ resource.stub!(:loaded?).and_return(true) # should force reload even if already loaded
107
+ resource.raw.should be_nil
108
+ session.should_receive(:get).with(uri, {:query => {:q1 => 'v1'}}).and_return(@response_200)
109
+ resource.load(:query => {:q1 => 'v1'})
110
+ end
111
+ it "should get the raw representation of the resource if forced to do so" do
112
+ resource = Resource.new(uri=mock("uri"), session = mock("session", :logger => Logger.new(STDOUT)))
113
+ resource.stub!(:define_link) # do not define links
114
+ resource.stub!(:loaded?).and_return(true) # should force reload even if already loaded
115
+ resource.raw.should be_nil
116
+ session.should_receive(:get).with(uri, {}).and_return(@response_200)
117
+ resource.load(:reload => true)
118
+ end
119
+ it "should correctly define the functions to access simple values" do
120
+ resource = Resource.new("uri", session = mock("session", :get => @response_200, :logger => @logger))
121
+ resource.stub!(:define_link) # do not define links
122
+ resource.load
123
+ resource['whatever'].should == 'whatever'
124
+ resource.uri.should == 'uri'
125
+ resource.uid.should == 'rennes'
126
+ resource['an_array'].should be_a(SpecialArray)
127
+ resource['an_array'].should == [1,2,3]
128
+ lambda{resource.clusters}.should raise_error(NoMethodError)
129
+ end
130
+
131
+ it "should correctly define a collection association" do
132
+ resource = Resource.new("uri", session = mock("session", :get => mock("restfully response", :body => {
133
+ 'links' => [
134
+ {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
135
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/versions', 'resolvable' => false, 'title' => 'versions'}
136
+ ],
137
+ 'uid' => 'rennes'
138
+ }), :logger => @logger))
139
+ Collection.should_receive(:new).with('/grid5000/sites/rennes/versions', session, :title => 'versions', :raw => nil).and_return(collection=mock("restfully collection"))
140
+ resource.load
141
+ resource.associations['versions'].should == collection
142
+ end
143
+ it "should NOT update the URI with the self link" do
144
+ resource = Resource.new("uri", session = mock("session", :get => mock("restfully response", :body => {
145
+ 'links' => [
146
+ {'rel' => 'self', 'href' => '/grid5000/sites/rennes'}
147
+ ],
148
+ 'uid' => 'rennes'
149
+ }), :logger => @logger))
150
+ resource.uri.should == "uri"
151
+ resource.load
152
+ resource.uri.should == "uri"
153
+ end
154
+ it "should correctly define a member association" do
155
+ resource = Resource.new("uri", session = mock("session", :get => mock("restfully response", :body => {
156
+ 'links' => [
157
+ {'rel' => 'member', 'href' => '/grid5000/sites/rennes/versions/123', 'title' => 'version'}
158
+ ],
159
+ 'uid' => 'rennes'
160
+ }), :logger => @logger))
161
+ Resource.should_receive(:new).with('/grid5000/sites/rennes/versions/123', session, :title => 'version', :raw => nil).and_return(member=mock("restfully resource"))
162
+ resource.load
163
+ resource.associations['version'].should == member
164
+ end
165
+ it "should correctly define a parent association" do
166
+ resource = Resource.new("uri", session = mock("session", :get => mock("restfully response", :body => {
167
+ 'links' => [
168
+ {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
169
+ {'rel' => 'parent', 'href' => '/grid5000'}
170
+ ],
171
+ 'uid' => 'rennes'
172
+ }), :logger => @logger))
173
+ Resource.should_receive(:new).with('/grid5000', session).and_return(parent=mock("restfully resource"))
174
+ resource.load
175
+ resource.associations['parent'].should == parent
176
+ end
177
+ it "should ignore bad links" do
178
+ resource = Resource.new("uri", session = mock("session", :get => mock("restfully response", :body => {
179
+ 'links' => [
180
+ {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
181
+ {'rel' => 'invalid_rel', 'href' => '/whatever'},
182
+ {'rel' => 'collection', 'href' => '/has/no/title'}
183
+ ],
184
+ 'uid' => 'rennes'
185
+ }), :logger => @logger))
186
+ resource.load
187
+ resource.associations.should be_empty
188
+ end
189
+
190
+ it "should correctly define the functions to access links [integration test]" do
191
+ resource = Resource.new("uri", session = mock("session", :get => @response_200, :logger => @logger))
192
+ @logger.should_receive(:warn).with(/collection \/has\/no\/title has no title/)
193
+ @logger.should_receive(:warn).with(/invalid_rel is not a valid link relationship/)
194
+ resource.load
195
+ resource.associations.keys.should =~ ['versions', 'clusters', 'environments', 'status', 'parent', 'version']
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,13 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Restfully do
4
+ it "should have a default adapter" do
5
+ Restfully.adapter.should == Restfully::HTTP::Adapters::RestClientAdapter
6
+ end
7
+
8
+ it "should allow setting other adapters" do
9
+ require 'restfully/http/adapters/patron_adapter'
10
+ Restfully.adapter = Restfully::HTTP::Adapters::PatronAdapter
11
+ Restfully.adapter.should == Restfully::HTTP::Adapters::PatronAdapter
12
+ end
13
+ end
@@ -0,0 +1,105 @@
1
+ require File.dirname(__FILE__)+'/spec_helper'
2
+
3
+ include Restfully
4
+ describe Session do
5
+ describe "initialization" do
6
+ it "should have a logger reader" do
7
+ session = Session.new('https://api.grid5000.fr')
8
+ session.should_not respond_to(:logger=)
9
+ session.should respond_to(:logger)
10
+ end
11
+ it "should have a base_uri reader" do
12
+ session = Session.new('https://api.grid5000.fr')
13
+ session.should_not respond_to(:base_uri=)
14
+ session.base_uri.should == 'https://api.grid5000.fr'
15
+ end
16
+ it "should have a root_path reader that returns the path of the root resource, relative to the base URI" do
17
+ session = Session.new('https://api.grid5000.fr/sid', 'root_path' => '/grid5000')
18
+ session.should_not respond_to(:root_path=)
19
+ session.root_path.should == '/grid5000'
20
+ end
21
+ it "should set the default root_path to /" do
22
+ session = Session.new('https://api.grid5000.fr')
23
+ session.root_path.should == '/'
24
+ end
25
+ it "should log to NullLogger by default" do
26
+ NullLogger.should_receive(:new).and_return(logger = mock(NullLogger))
27
+ session = Session.new('https://api.grid5000.fr')
28
+ session.logger.should == logger
29
+ end
30
+ it "should use the given logger" do
31
+ logger = mock("custom logger")
32
+ session = Session.new('https://api.grid5000.fr', 'logger' => logger)
33
+ session.logger.should == logger
34
+ end
35
+ it "should set a default Accept HTTP header if no default headers are given" do
36
+ session = Session.new('https://api.grid5000.fr')
37
+ session.default_headers.should == {
38
+ 'Accept' => 'application/json'
39
+ }
40
+ end
41
+ it "should use the given default headers" do
42
+ session = Session.new('https://api.grid5000.fr', :default_headers => {:accept => 'application/xml'})
43
+ session.default_headers.should == {
44
+ :accept => 'application/xml'
45
+ }
46
+ end
47
+ it "should correctly initialize the connection" do
48
+ Restfully.adapter.should_receive(:new).with('https://api.grid5000.fr/sid', :user => 'crohr', :password => 'password').and_return(connection = mock("restfully connection"))
49
+ session = Session.new('https://api.grid5000.fr/sid', 'root_path' => '/grid5000', :user => 'crohr', :password => 'password')
50
+ session.connection.should == connection
51
+ end
52
+
53
+ it "should initialize the root resource" do
54
+ session = Session.new('https://api.grid5000.fr', 'root_path' => '/grid5000', :user => 'crohr', :password => 'password')
55
+ session.root.should be_a Restfully::Resource
56
+ session.root.uri.to_s.should == '/grid5000'
57
+ session.root.should_not be_loaded
58
+ end
59
+
60
+ it "should yield the loaded root resource and the session object" do
61
+ Restfully::Resource.stub!(:new).and_return(root_resource = mock(Restfully::Resource))
62
+ root_resource.should_receive(:load).and_return(root_resource)
63
+ Session.new('https://api.grid5000.fr', :root_path => '/grid5000', :user => 'crohr', :password => 'password') do |root, session|
64
+ session.root.should == root
65
+ root.should == root_resource
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "Getting resources" do
71
+ before(:each) do
72
+ @session = Session.new('https://api.grid5000.fr/sid', :root_path => '/grid5000', :user => 'crohr', :password => 'password', :default_headers => {})
73
+ @request = mock("restfully http request", :uri => mock("uri"), :headers => mock("headers"))
74
+ @response = mock("restfully http response", :status => 200, :headers => mock("headers"))
75
+ end
76
+ it "should create a new Request object and pass it to the connection" do
77
+ Restfully::HTTP::Request.should_receive(:new).with(URI.parse('https://api.grid5000.fr/sid/some/path'), :headers => {:cache_control => 'max-age=0'}, :query => {}).and_return(@request)
78
+ @session.connection.should_receive(:get).with(@request).and_return(@response)
79
+ @session.get('/some/path', :headers => {:cache_control => 'max-age=0'}).should == @response
80
+ end
81
+ it "should not use the base_url if the path is a complete url" do
82
+ Restfully::HTTP::Request.should_receive(:new).with(URI.parse('http://somehost.com/some/path'), :headers => {}, :query => {}).and_return(@request)
83
+ @session.connection.should_receive(:get).with(@request).and_return(@response)
84
+ @session.get('http://somehost.com/some/path').should == @response
85
+ end
86
+ it "should add the session's default headers to the request's headers" do
87
+ Restfully::HTTP::Request.should_receive(:new).with(URI.parse('http://somehost.com/some/path'), :headers => {}, :query => {}).and_return(@request)
88
+ @session.default_headers['cache-control'] = 'max-age=0'
89
+ @request.should_receive(:add_headers).with('cache-control' => 'max-age=0')
90
+ @session.connection.should_receive(:get).with(@request).and_return(@response)
91
+ @session.get('http://somehost.com/some/path').should == @response
92
+ end
93
+ it "should raise a Restfully::HTTP::ClientError error on 4xx errors" do
94
+ Restfully::HTTP::Request.should_receive(:new).with(URI.parse('http://somehost.com/some/path'), :headers => {}, :query => {}).and_return(@request)
95
+ @session.connection.should_receive(:get).with(@request).and_return(response = mock("404 response", :status => 404, :body => {'code' => 404, 'message' => 'The requested resource cannot be found.', 'title' => 'Not Found'}, :headers => mock("headers")))
96
+ lambda{ @session.get('http://somehost.com/some/path') }.should raise_error(Restfully::HTTP::ClientError, "404 Not Found. The requested resource cannot be found.")
97
+ end
98
+ it "should raise a Restfully::HTTP::ServerError error on 4xx errors" do
99
+ Restfully::HTTP::Request.should_receive(:new).with(URI.parse('http://somehost.com/some/path'), :headers => {}, :query => {}).and_return(@request)
100
+ @session.connection.should_receive(:get).with(@request).and_return(response = mock("404 response", :status => 500, :body => {'code' => 500, 'message' => 'Something went wrong.', 'title' => 'Internal Server Error'}, :headers => mock("headers")))
101
+ lambda{ @session.get('http://somehost.com/some/path') }.should raise_error(Restfully::HTTP::ServerError, "500 Internal Server Error. Something went wrong.")
102
+ end
103
+ end
104
+
105
+ end