restfully 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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