restfully 0.6.3 → 0.7.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/README.md +166 -0
  2. data/Rakefile +35 -35
  3. data/bin/restfully +68 -10
  4. data/lib/restfully.rb +8 -14
  5. data/lib/restfully/collection.rb +70 -90
  6. data/lib/restfully/error.rb +2 -0
  7. data/lib/restfully/http.rb +3 -3
  8. data/lib/restfully/http/error.rb +1 -20
  9. data/lib/restfully/http/helper.rb +49 -0
  10. data/lib/restfully/http/request.rb +60 -24
  11. data/lib/restfully/http/response.rb +55 -24
  12. data/lib/restfully/link.rb +32 -24
  13. data/lib/restfully/media_type.rb +70 -0
  14. data/lib/restfully/media_type/abstract_media_type.rb +162 -0
  15. data/lib/restfully/media_type/application_json.rb +21 -0
  16. data/lib/restfully/media_type/application_vnd_bonfire_xml.rb +177 -0
  17. data/lib/restfully/media_type/application_x_www_form_urlencoded.rb +33 -0
  18. data/lib/restfully/media_type/grid5000.rb +67 -0
  19. data/lib/restfully/media_type/wildcard.rb +27 -0
  20. data/lib/restfully/rack.rb +1 -0
  21. data/lib/restfully/rack/basic_auth.rb +26 -0
  22. data/lib/restfully/resource.rb +134 -197
  23. data/lib/restfully/session.rb +127 -70
  24. data/lib/restfully/version.rb +3 -0
  25. data/spec/fixtures/bonfire-collection-with-fragments.xml +6 -0
  26. data/spec/fixtures/bonfire-compute-existing.xml +43 -0
  27. data/spec/fixtures/bonfire-empty-collection.xml +4 -0
  28. data/spec/fixtures/bonfire-experiment-collection.xml +51 -0
  29. data/spec/fixtures/bonfire-network-collection.xml +35 -0
  30. data/spec/fixtures/bonfire-network-existing.xml +6 -0
  31. data/spec/fixtures/bonfire-root.xml +5 -0
  32. data/spec/fixtures/grid5000-rennes-jobs.json +988 -146
  33. data/spec/fixtures/grid5000-rennes.json +63 -0
  34. data/spec/restfully/collection_spec.rb +87 -0
  35. data/spec/restfully/http/helper_spec.rb +18 -0
  36. data/spec/restfully/http/request_spec.rb +97 -0
  37. data/spec/restfully/http/response_spec.rb +53 -0
  38. data/spec/restfully/link_spec.rb +80 -0
  39. data/spec/restfully/media_type/application_vnd_bonfire_xml_spec.rb +153 -0
  40. data/spec/restfully/media_type_spec.rb +117 -0
  41. data/spec/restfully/resource_spec.rb +109 -0
  42. data/spec/restfully/session_spec.rb +229 -0
  43. data/spec/spec_helper.rb +10 -9
  44. metadata +162 -83
  45. data/.document +0 -5
  46. data/CHANGELOG +0 -62
  47. data/README.rdoc +0 -146
  48. data/TODO.rdoc +0 -3
  49. data/VERSION +0 -1
  50. data/examples/grid5000.rb +0 -33
  51. data/examples/scratch.rb +0 -37
  52. data/lib/restfully/extensions.rb +0 -34
  53. data/lib/restfully/http/adapters/abstract_adapter.rb +0 -29
  54. data/lib/restfully/http/adapters/patron_adapter.rb +0 -16
  55. data/lib/restfully/http/adapters/rest_client_adapter.rb +0 -75
  56. data/lib/restfully/http/headers.rb +0 -20
  57. data/lib/restfully/parsing.rb +0 -66
  58. data/lib/restfully/special_array.rb +0 -5
  59. data/lib/restfully/special_hash.rb +0 -5
  60. data/restfully.gemspec +0 -114
  61. data/spec/collection_spec.rb +0 -120
  62. data/spec/fixtures/configuration_file.yml +0 -4
  63. data/spec/fixtures/grid5000-sites.json +0 -540
  64. data/spec/http/error_spec.rb +0 -18
  65. data/spec/http/headers_spec.rb +0 -17
  66. data/spec/http/request_spec.rb +0 -49
  67. data/spec/http/response_spec.rb +0 -19
  68. data/spec/http/rest_client_adapter_spec.rb +0 -35
  69. data/spec/link_spec.rb +0 -61
  70. data/spec/parsing_spec.rb +0 -40
  71. data/spec/resource_spec.rb +0 -320
  72. data/spec/restfully_spec.rb +0 -16
  73. data/spec/session_spec.rb +0 -171
@@ -1,18 +0,0 @@
1
- require File.expand_path(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
@@ -1,17 +0,0 @@
1
- require File.expand_path(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
@@ -1,49 +0,0 @@
1
- require File.expand_path(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/xml', :cache_control => 'max-age=0', 'Content-Type' => 'application/json'},
8
- :query => {'custom_param1' => [3, 4, 5, 6], 'custom_param2' => 'value_custom_param2'},
9
- :body => {"key" => "value"}.to_json
10
- )
11
- request.uri.should be_a URI
12
- request.uri.to_s.should == 'https://api.grid5000.fr/sid/grid5000?q1=v1&q2=v2&custom_param1%5B%5D=3&custom_param1%5B%5D=4&custom_param1%5B%5D=5&custom_param1%5B%5D=6&custom_param2=value_custom_param2'
13
- request.uri.query.should == "q1=v1&q2=v2&custom_param1%5B%5D=3&custom_param1%5B%5D=4&custom_param1%5B%5D=5&custom_param1%5B%5D=6&custom_param2=value_custom_param2"
14
- request.headers.should == {
15
- "Content-Type"=>"application/json",
16
- 'Accept' => 'application/xml',
17
- 'Cache-Control' => 'max-age=0'
18
- }
19
- request.body.should == {"key" => "value"}
20
- request.raw_body.should == "{\"key\":\"value\"}"
21
- request.retries.should == 0
22
- end
23
-
24
- it "should accept a URI object as url" do
25
- request = Restfully::HTTP::Request.new(uri=URI.parse('https://api.grid5000.fr/sid/grid5000'))
26
- request.uri.to_s.should == 'https://api.grid5000.fr/sid/grid5000'
27
- end
28
-
29
- it "should not fail if there are query parameters but no query string in the given URL" do
30
- request = Restfully::HTTP::Request.new('https://api.grid5000.fr/grid5000', :query => {:q1 => 'v1'})
31
- request.uri.query.should == "q1=v1"
32
- end
33
-
34
- it "should not change the query string if none is given as an option" do
35
- request = Restfully::HTTP::Request.new('https://api.grid5000.fr/grid5000?q1=v1&q2=v2')
36
- request.uri.to_s.should == 'https://api.grid5000.fr/grid5000?q1=v1&q2=v2'
37
- request.headers.should == {}
38
- end
39
-
40
- it "should offer a function to add headers" do
41
- request = Restfully::HTTP::Request.new('https://api.grid5000.fr/grid5000?q1=v1&q2=v2', :headers => {:accept => 'application/json'})
42
- expected_headers = {
43
- 'Accept' => 'application/json',
44
- 'Cache-Control' => 'max-age=0'
45
- }
46
- request.add_headers(:cache_control => 'max-age=0').should == expected_headers
47
- request.headers.should == expected_headers
48
- end
49
- end
@@ -1,19 +0,0 @@
1
- require File.expand_path(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
- it "should return the unserialized body" do
16
- response = Restfully::HTTP::Response.new(404, {:content_type => 'application/json;charset=utf-8'}, '{"property1": "value1", "property2": "value2"}')
17
- response.raw_body.should == "{\"property1\": \"value1\", \"property2\": \"value2\"}"
18
- end
19
- end
@@ -1,35 +0,0 @@
1
- require File.expand_path(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({:accept => 'application/json'}).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
- require 'restfully/http/adapters/patron_adapter'
22
- adapter = Restfully::HTTP::Adapters::PatronAdapter.new("https://api.grid5000.fr")
23
- lambda{adapter.put(mock("restfully request"))}.should raise_error NotImplementedError, "PUT is not supported by your adapter."
24
- end
25
- it "should rescue any RestClient::Exception and correctly populate the response" do
26
- res = mock(RestClient::Response, :code => 404, :to_s => '{"message":"whatever"}', :headers => {:content_type => 'application/json;charset=utf-8', :content_length => 22})
27
- RestClient::Resource.should_receive(:new).and_raise RestClient::ResourceNotFound.new(res)
28
- adapter = Restfully::HTTP::Adapters::RestClientAdapter.new("https://api.grid5000.fr", :username => 'crohr', :password => 'password')
29
- response = adapter.get(mock("request", :uri => "uri"))
30
- response.status.should == 404
31
- response.headers.should == {'Content-Type' => 'application/json;charset=utf-8', 'Content-Length' => 22}
32
- response.raw_body.should == '{"message":"whatever"}'
33
- response.body.should == {"message"=>"whatever"}
34
- end
35
- end
data/spec/link_spec.rb DELETED
@@ -1,61 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/spec_helper')
2
-
3
- include Restfully
4
- describe Link do
5
- before do
6
- @uri = URI.parse('/x/y/z')
7
- end
8
- it "should have a rel reader" do
9
- link = Link.new
10
- link.should_not respond_to(:rel=)
11
- link.should respond_to(:rel)
12
- end
13
- it "should have a href reader" do
14
- link = Link.new
15
- link.should_not respond_to(:href=)
16
- link.should respond_to(:href)
17
- end
18
- it "should have a title reader" do
19
- link = Link.new
20
- link.should_not respond_to(:title=)
21
- link.should respond_to(:title)
22
- end
23
- it "should have a errors reader" do
24
- link = Link.new
25
- link.should_not respond_to(:errors=)
26
- link.should respond_to(:errors)
27
- end
28
- it "should respond to valid?" do
29
- link = Link.new
30
- link.should respond_to(:valid?)
31
- end
32
- it "should respond to resolved?" do
33
- link = Link.new("href" => @uri)
34
- link.should respond_to(:resolved?)
35
- end
36
- it "should respond to resolvable?" do
37
- link = Link.new
38
- link.should respond_to(:resolvable?)
39
- end
40
- it "by default, should not be resolvable" do
41
- link = Link.new
42
- link.should_not be_resolvable
43
- end
44
- it "by default, should not be resolved" do
45
- link = Link.new
46
- link.should_not be_resolved
47
- end
48
- it "should be valid even if the href is ''" do
49
- link = Link.new 'rel' => 'collection', 'title' => 'my collection'
50
- link.should be_valid
51
- link.href.should == URI.parse("")
52
- end
53
- it "should not be valid if there is no rel" do
54
- link = Link.new 'href' => '/', 'title' => 'my collection'
55
- link.should_not be_valid
56
- end
57
- it "should not be valid if the rel is valid but requires a title that is not given" do
58
- link = Link.new 'rel' => 'collection', 'href' => '/'
59
- link.should_not be_valid
60
- end
61
- end
data/spec/parsing_spec.rb DELETED
@@ -1,40 +0,0 @@
1
- require File.expand_path(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, "Cannot find a parser to parse 'unknown' content.")
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
- it "should correctly unserialize text content" do
26
- object = "anything"
27
- unserialize(object, :content_type => 'text/plain;charset=utf-8').should == object
28
- end
29
- it "should allow to define its own parser" do
30
- Restfully::Parsing::PARSERS.push({
31
- :supported_types => "text/unknown",
32
- :parse => lambda{|object, options| object.gsub(/x/, "y")},
33
- :dump => lambda{|object, options| object.gsub(/y/, "x")}
34
- })
35
- object = "xyz"
36
- parsed = unserialize(object, :content_type => 'text/unknown')
37
- parsed.should == "yyz"
38
- serialize(parsed, :content_type => 'text/unknown').should == "xxz"
39
- end
40
- end
@@ -1,320 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/spec_helper')
2
-
3
- include Restfully
4
-
5
- describe Resource do
6
- before do
7
- @logger = Logger.new(STDOUT)
8
- @uri = URI.parse("http://api.local/x/y/z")
9
- end
10
-
11
- describe "accessors" do
12
- before(:each) do
13
- @resource = Resource.new(@uri, @session=mock("session"))
14
- end
15
- it "should have a reader on the session" do
16
- @resource.should_not respond_to(:session=)
17
- @resource.session.should == @session
18
- end
19
- it "should have a reader on the uri" do
20
- @resource.should_not respond_to(:uri=)
21
- @resource.uri.should == @uri
22
- end
23
- end
24
-
25
- describe "reloading" do
26
- it "should reload the resource" do
27
- resource = Resource.new(@uri, mock('session'))
28
- resource.should_receive(:load).with(:reload => true)
29
- resource.reload
30
- end
31
- end
32
-
33
- describe "loading" do
34
- before do
35
- @session = Restfully::Session.new(:base_uri => "http://api.local")
36
- @raw = {
37
- 'links' => [
38
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
39
- {'rel' => 'parent', 'href' => '/grid5000'},
40
- {'rel' => 'invalid_rel', 'href' => '/whatever'},
41
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/status', 'title' => 'status'},
42
- {'rel' => 'member', 'href' => '/grid5000/sites/rennes/versions/123', 'title' => 'version'},
43
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/versions', 'resolvable' => false, 'title' => 'versions'},
44
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters', 'resolvable' => true, 'resolved' => true, 'title' => 'clusters'},
45
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/environments/versions/123', 'resolvable' => true, 'resolved' => false, 'title' => 'environments'},
46
- {'rel' => 'collection', 'href' => '/has/no/title'}
47
- ],
48
- 'uid' => 'rennes',
49
- 'whatever' => 'whatever',
50
- 'an_array' => [1, 2, 3],
51
- 'clusters' => {
52
- 'paradent' => {
53
- 'uid' => 'paradent',
54
- 'links' => [
55
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes/clusters/paradent'},
56
- {'rel' => 'parent', 'href' => '/grid5000/sites/rennes'},
57
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paradent/nodes', 'title' => 'nodes', 'resolvable' => true, 'resolved' => false},
58
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paradent/versions', 'resolvable' => false, 'title' => 'versions'},
59
- {'rel' => 'member', 'href' => '/grid5000/sites/rennes/clusters/paradent/versions/123', 'title' => 'version'},
60
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paradent/status', 'title' => 'status'}
61
- ],
62
- 'model' => 'XYZ'
63
- },
64
- 'paramount' => {
65
- 'uid' => 'paramount',
66
- 'links' => [
67
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes/clusters/paramount'},
68
- {'rel' => 'parent', 'href' => '/grid5000/sites/rennes'},
69
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paramount/nodes', 'title' => 'nodes', 'resolvable' => true, 'resolved' => false},
70
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paramount/versions', 'resolvable' => false, 'title' => 'versions'},
71
- {'rel' => 'member', 'href' => '/grid5000/sites/rennes/clusters/paramount/versions/123', 'title' => 'version'},
72
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paramount/status', 'title' => 'status'}
73
- ],
74
- 'model' => 'XYZ1b'
75
- }
76
- }
77
- }
78
- @response_200 = Restfully::HTTP::Response.new(200, {'Content-Type' => 'application/json;utf-8', 'Content-Length' => @raw.length}, @raw.to_json)
79
- end
80
-
81
- it "should not be loaded in its initial state" do
82
- resource = Resource.new(@uri, mock('session'))
83
- resource.executed_requests.should == {}
84
- end
85
- it "should get the raw representation of the resource via the session if it doesn't have it" do
86
- resource = Resource.new(@uri, session = mock("session", :logger => Logger.new(STDOUT)))
87
- resource.stub!(:define_link) # do not define links
88
- session.should_receive(:get).with(@uri, {}).and_return(@response_200)
89
- resource.load
90
- end
91
- it "should get the raw representation of the resource via the session if there are query parameters" do
92
- resource = Resource.new(@uri, session = mock("session", :logger => Logger.new(STDOUT)))
93
- resource.stub!(:define_link) # do not define links
94
- session.should_receive(:get).with(@uri, {:query => {:q1 => 'v1'}}).and_return(@response_200)
95
- resource.load(:query => {:q1 => 'v1'})
96
- end
97
- it "should get the raw representation of the resource if forced to do so" do
98
- resource = Resource.new(@uri, session = mock("session", :logger => Logger.new(STDOUT)))
99
- resource.stub!(:define_link) # do not define links
100
- session.should_receive(:get).with(@uri, {}).and_return(@response_200)
101
- resource.load(:reload => true)
102
- end
103
- it "should correctly define the functions to access simple values" do
104
- stub_request(:get, @uri.to_s).to_return(
105
- :status => 200,
106
- :body => @raw.to_json,
107
- :headers => {'Content-Type' => 'application/json', 'Content-Length' => @raw.length}
108
- )
109
- resource = Resource.new(@uri, @session)
110
- resource.stub!(:define_link) # do not define links
111
- resource.load
112
- resource['whatever'].should == 'whatever'
113
- resource.uri.should == @uri
114
- resource["uid"].should == 'rennes'
115
- resource['an_array'].should be_a(SpecialArray)
116
- resource['an_array'].should == [1,2,3]
117
- end
118
-
119
- it "should correctly send custom headers" do
120
- stub_request(:get, @uri.to_s).with(:headers => {
121
- 'User-Agent'=>"Restfully/#{Restfully::VERSION}",
122
- 'Accept-Encoding'=>'gzip, deflate',
123
- 'Accept'=>'application/json'
124
- }).to_return(
125
- :status => 200,
126
- :body => @raw.to_json,
127
- :headers => {'Content-Type' => 'application/json', 'Content-Length' => @raw.length}
128
- )
129
- resource = Resource.new(@uri, @session)
130
- resource.load(:headers => {:accept => 'application/json'})
131
- end
132
-
133
- it "should correctly define a collection link" do
134
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
135
- 'links' => [
136
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
137
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/versions', 'resolvable' => false, 'title' => 'versions'}
138
- ],
139
- 'uid' => 'rennes'
140
- }, :headers => {}), :logger => @logger))
141
- Collection.should_receive(:new).with(@uri.merge('/grid5000/sites/rennes/versions'), session, :title => 'versions').and_return(collection=mock("restfully collection"))
142
- resource.load
143
- resource.links['versions'].should == collection
144
- end
145
- it "should NOT update the URI with the self link" do
146
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
147
- 'links' => [
148
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'}
149
- ],
150
- 'uid' => 'rennes'
151
- }, :headers => {}), :logger => @logger))
152
- resource.uri.should == @uri
153
- resource.load
154
- resource.uri.should == @uri
155
- end
156
- it "should correctly define a member association" do
157
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
158
- 'links' => [
159
- {'rel' => 'member', 'href' => '/grid5000/sites/rennes/versions/123', 'title' => 'version'}
160
- ],
161
- 'uid' => 'rennes'
162
- }, :headers => {}), :logger => @logger))
163
- Resource.should_receive(:new).with(@uri.merge('/grid5000/sites/rennes/versions/123'), session, :title => 'version').and_return(member=mock("restfully resource"))
164
- resource.load
165
- resource.links['version'].should == member
166
- end
167
- it "should correctly define a parent association" do
168
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
169
- 'links' => [
170
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
171
- {'rel' => 'parent', 'href' => '/grid5000'}
172
- ],
173
- 'uid' => 'rennes'
174
- }, :headers => {}), :logger => @logger))
175
- Resource.should_receive(:new).with(@uri.merge('/grid5000'), session).and_return(parent=mock("restfully resource"))
176
- resource.load
177
- resource.links['parent'].should == parent
178
- end
179
- it "should ignore bad links" do
180
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
181
- 'links' => [
182
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
183
- {'rel' => 'invalid_rel', 'href' => '/whatever'},
184
- {'rel' => 'collection', 'href' => '/has/no/title'}
185
- ],
186
- 'uid' => 'rennes'
187
- }, :headers => {}), :logger => @logger))
188
- resource.load
189
- resource.links.should be_empty
190
- end
191
-
192
- it "should correctly define the functions to access links [integration test]" do
193
- resource = Resource.new(@uri, session = mock("session", :get => @response_200, :logger => @logger))
194
- @logger.should_receive(:warn).with(/collection \/has\/no\/title has no title/)
195
- @logger.should_receive(:warn).with(/invalid_rel is not a valid link relationship/)
196
- resource.load
197
- resource.links.keys.should =~ ['versions', 'clusters', 'environments', 'status', 'parent', 'version']
198
- end
199
-
200
- it "should reload the resource if user forces reload [first loading]" do
201
- resource = Resource.new(@uri, session = mock("session"))
202
- session.should_receive(:get).and_return(response = mock("response", :headers => {}, :body => {}))
203
- resource.load(:reload => true, :body => mock("body"))
204
- end
205
- it "should reload the resource when user forces reload [has been loaded at least once before]" do
206
- resource = Resource.new(@uri, session = mock("session"))
207
- resource.instance_variable_set "@status", :loaded
208
- resource.should_not be_stale
209
- resource.should_receive(:executed_requests).at_least(1).and_return({
210
- 'GET' => {'options' => {:query => {:q=>1}}, 'body' => {'a' => 'b'}}
211
- })
212
- session.should_receive(:get).and_return(response = mock("response", :headers => {}, :body => {}))
213
- resource.load(:reload => true, :query => {:q => 1})
214
- end
215
- end
216
-
217
-
218
- describe "submitting" do
219
- before do
220
- @resource = Resource.new(@uri, @session = mock("session", :logger => @logger))
221
- @resource.stub!(:http_methods).and_return(['GET', 'POST'])
222
- @resource.stub!(:executed_requests).and_return({
223
- 'GET' => {'headers' => {'Content-Type' => 'application/vnd.fr.grid5000.api.Job+json;level=1,application/json'}, 'options' => {:query => {:q1 => 'v1'}}}
224
- })
225
- end
226
- describe "setting input body and options" do
227
- before do
228
- @resource.stub!(:reload).and_return(@resource)
229
- @response = mock("http response", :status => 200)
230
- end
231
- it "should raise a NotImplementedError if the resource does not allow POST" do
232
- @resource.should_receive(:http_methods).and_return(['GET'])
233
- lambda{@resource.submit("whatever")}.should raise_error(NotImplementedError, /POST method is not allowed for this resource/)
234
- end
235
- it "should raise an error if the input body is nil" do
236
- lambda{@resource.submit(nil)}.should raise_error(ArgumentError, /You must pass a payload/)
237
- end
238
- it "should pass the body as-is if the given body is a string" do
239
- @session.should_receive(:post).with(@resource.uri, "whatever", :headers => {
240
- :accept => 'application/vnd.fr.grid5000.api.Job+json;level=1,application/json',
241
- :content_type => 'application/json'}
242
- ).and_return(@response)
243
- @resource.submit("whatever")
244
- end
245
- it "should also pass the body as-is if the given body is an object" do
246
- body = {:key => 'value'}
247
- @session.should_receive(:post).with(@resource.uri, body, :headers => {
248
- :accept => 'application/vnd.fr.grid5000.api.Job+json;level=1,application/json',
249
- :content_type => 'application/json'}).and_return(@response)
250
- @resource.submit(body)
251
- end
252
- it "should set the Content-Type header to the specified mime-type if given" do
253
- @session.should_receive(:post).with(@resource.uri, "whatever", :headers => {
254
- :accept => 'application/vnd.fr.grid5000.api.Job+json;level=1,application/json',
255
- :content_type => 'application/xml'}).and_return(@response)
256
- @resource.submit("whatever", :headers => {:content_type => 'application/xml'})
257
- end
258
- end
259
- [201, 202].each do |status|
260
- it "should return the resource referenced in the Location header after a successful submit (status=#{status})" do
261
- @session.should_receive(:post).and_return(response = mock("http response", :status => status, :headers => {'Location' => '/path/to/new/resource'}))
262
- @resource.should_receive(:uri_for).with('/path/to/new/resource').and_return(new_resource_uri = mock("uri"))
263
- Resource.should_receive(:new).with(new_resource_uri, @session).and_return(new_resource = mock("resource"))
264
- new_resource.should_receive(:load).and_return(new_resource)
265
- @resource.submit("whatever").should == new_resource
266
- end
267
- end
268
- it "should reload the resource if the response status is 2xx (and not 201 or 202)" do
269
- @session.should_receive(:post).and_return(response = mock("http response", :status => 200, :headers => {'Location' => '/path/to/new/resource'}))
270
- @resource.should_receive(:reload).and_return(@resource)
271
- @resource.submit("whatever").should == @resource
272
- end
273
- end
274
-
275
- describe "deleting" do
276
- before do
277
- @resource = Resource.new(@uri, @session = mock("session", :logger => @logger))
278
- @resource.stub!(:http_methods).and_return(['GET', 'DELETE'])
279
- end
280
- it "should raise an error if the DELETE method is not supported by the service" do
281
- @resource.stub!(:http_methods).and_return(['GET'])
282
- lambda{@resource.delete}.should raise_error(NotImplementedError, /DELETE method is not allowed/)
283
- end
284
- it "should send a DELETE request to the resource URI" do
285
- @session.should_receive(:delete).with(@uri, :query => {:q => 'v'}, :headers => {'Accept' => 'application/json'}).and_return(response = mock("http response", :status => 204))
286
- @resource.delete(:query => {:q => 'v'}, :headers => {'Accept' => 'application/json'}).should be_true
287
- end
288
- end
289
-
290
- describe "supported http methodes" do
291
- before do
292
- @resource = Resource.new(@uri, @session = mock("session", :logger => @logger))
293
- end
294
- it "should reload if a GET request has not been executed before" do
295
- @resource.should_receive(:executed_requests).ordered.and_return({})
296
- @resource.should_receive(:reload).ordered
297
- @resource.should_receive(:executed_requests).ordered.and_return({
298
- 'GET' => {'body' => "xxx", "headers" => {'Allow' => 'POST, PUT'}}
299
- })
300
- @resource.http_methods.should == ['POST','PUT']
301
- end
302
- it "should reload if the headers are empty" do
303
- @resource.should_receive(:executed_requests).exactly(3).ordered.and_return({
304
- 'GET' => {'body' => "xxx", "headers" => {}}
305
- })
306
- @resource.should_receive(:reload).ordered
307
- @resource.should_receive(:executed_requests).once.ordered.and_return({
308
- 'GET' => {'body' => "xxx", "headers" => {'Allow' => 'POST, PUT'}}
309
- })
310
- @resource.http_methods.should == ['POST','PUT']
311
- end
312
- it "should not reload if the headers are not empty" do
313
- @resource.should_receive(:executed_requests).exactly(4).and_return({
314
- 'GET' => {'body' => "xxx", "headers" => {'Allow' => 'POST, PUT'}}
315
- })
316
- @resource.should_not_receive(:reload)
317
- @resource.http_methods.should == ['POST','PUT']
318
- end
319
- end
320
- end