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
@@ -0,0 +1,63 @@
1
+ {
2
+ "compilation_server":false,
3
+ "description":"",
4
+ "email_contact":"rennes-staff@lists.grid5000.fr",
5
+ "latitude":48.1,
6
+ "location":"Rennes,France",
7
+ "longitude":-1.6667,
8
+ "name":"Rennes",
9
+ "security_contact":"rennes-staff@lists.grid5000.fr",
10
+ "sys_admin_contact":"rennes-staff@lists.grid5000.fr",
11
+ "type":"site",
12
+ "uid":"rennes",
13
+ "user_support_contact":"rennes-staff@lists.grid5000.fr",
14
+ "web":"http://www.irisa.fr",
15
+ "version":"fe8e18aaf62cceff002252bcdfa83e4f4c4d9f63",
16
+ "links": [
17
+ {
18
+ "rel":"self",
19
+ "type":"application/vnd.grid5000+json",
20
+ "href":"/grid5000/sites/rennes"
21
+ },
22
+ {
23
+ "rel":"parent",
24
+ "type":"application/vnd.grid5000+json",
25
+ "href":"/grid5000"
26
+ },
27
+ {
28
+ "rel":"versions",
29
+ "type":"application/vnd.grid5000+json",
30
+ "href":"/grid5000/sites/rennes/versions"
31
+ },
32
+ {
33
+ "rel":"clusters",
34
+ "type":"application/vnd.grid5000+json",
35
+ "href":"/grid5000/sites/rennes/clusters"
36
+ },
37
+ {
38
+ "rel":"environments",
39
+ "type":"application/vnd.grid5000+json",
40
+ "href":"/grid5000/sites/rennes/environments"
41
+ },
42
+ {
43
+ "rel":"jobs",
44
+ "type":"application/vnd.grid5000+json",
45
+ "href":"/grid5000/sites/rennes/jobs"
46
+ },
47
+ {
48
+ "rel":"deployments",
49
+ "type":"application/vnd.grid5000+json",
50
+ "href":"/grid5000/sites/rennes/deployments"
51
+ },
52
+ {
53
+ "rel":"metrics",
54
+ "type":"application/vnd.grid5000+json",
55
+ "href":"/grid5000/sites/rennes/metrics"
56
+ },
57
+ {
58
+ "rel":"status",
59
+ "type":"application/vnd.grid5000+json",
60
+ "href":"/grid5000/sites/rennes/status"
61
+ }
62
+ ]
63
+ }
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfully::Collection do
4
+ before do
5
+ Restfully::MediaType.register Restfully::MediaType::ApplicationVndBonfireXml
6
+
7
+ @session = Restfully::Session.new(
8
+ :uri => "https://api.grid5000.fr"
9
+ )
10
+ @request = Restfully::HTTP::Request.new(
11
+ @session, :get, "/grid5000/sites/rennes/jobs",
12
+ :head => {'Accept' => 'application/vnd.grid5000+json'}
13
+ )
14
+ @response = Restfully::HTTP::Response.new(
15
+ @session, 200, {
16
+ 'Content-Type' => 'application/vnd.grid5000+json; charset=utf-8',
17
+ 'Allow' => 'GET,POST'
18
+ }, fixture('grid5000-rennes-jobs.json')
19
+ )
20
+ Restfully::MediaType.register Restfully::MediaType::Grid5000
21
+ @resource = Restfully::Resource.new(@session, @response, @request)
22
+ end
23
+
24
+ it "should include the Collection module if the resource is a collection" do
25
+ @resource.should_receive(:extend).with(Restfully::Collection)
26
+ @resource.load
27
+ end
28
+
29
+ it "should return the total number of elements" do
30
+ @resource.load.total.should == 366486
31
+ end
32
+
33
+ it "should return the offset" do
34
+ @resource.load.offset.should == 0
35
+ end
36
+
37
+ it "should iterate over the collection" do
38
+ items = []
39
+ @resource.load.each{|i| items << i}
40
+ items.all?{|i| i.kind_of?(Restfully::Resource)}.should be_true
41
+ items[0].relationships.map(&:to_s).sort.should == ["parent", "self"]
42
+ items[0]['uid'].should == 376505
43
+ end
44
+
45
+ it "should be empty if no item" do
46
+ @response = Restfully::HTTP::Response.new(
47
+ @session, 200, {
48
+ 'Content-Type' => 'application/vnd.bonfire+xml; charset=utf-8'
49
+ }, fixture('bonfire-empty-collection.xml')
50
+ )
51
+ @resource = Restfully::Resource.new(@session, @response, @request).load
52
+ @resource.collection?.should be_true
53
+ @resource.should be_empty
54
+ @resource.total.should == 0
55
+ @resource.length.should == 0
56
+ @resource.offset.should == 0
57
+ @resource.map{|i| i}.should == []
58
+ end
59
+
60
+ describe "finders" do
61
+ before do
62
+ @response = Restfully::HTTP::Response.new(
63
+ @session, 200, {
64
+ 'Content-Type' => 'application/vnd.bonfire+xml; charset=utf-8'
65
+ }, fixture('bonfire-experiment-collection.xml')
66
+ )
67
+ @resource = Restfully::Resource.new(@session, @response, @request).load
68
+ end
69
+ it "should find an item by uid" do
70
+ @resource[:'3'].should_not be_nil
71
+ end
72
+ it "should find an item by index" do
73
+ @resource[0].should == @resource.first
74
+ end
75
+ it "should reload the item if not complete" do
76
+ @resource.map{|i|
77
+ if i["id"] == "3"
78
+ i.media_type.should_receive(:complete?).and_return(false)
79
+ i.should_receive(:reload).once
80
+ end
81
+ }
82
+ @resource[:'3'].should_not be_nil
83
+ end
84
+ end
85
+
86
+
87
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfully::HTTP::Helper do
4
+ include Restfully::HTTP::Helper
5
+
6
+ it "should correctly sanitize the headers" do
7
+ sanitize_head({
8
+ 'Cache-control' => 'no-cache',
9
+ :some_header => 'some value',
10
+ 'accept' => 'application/json'
11
+ }).should == {
12
+ 'Cache-Control' => 'no-cache',
13
+ 'Some-Header' => 'some value',
14
+ 'Accept' => 'application/json'
15
+ }
16
+ end
17
+
18
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfully::HTTP::Request do
4
+ before do
5
+ @session = mock(
6
+ Restfully::Session,
7
+ :default_headers => {
8
+ 'Accept' => '*/*; application/xml',
9
+ :accept_encoding => "gzip, deflate"
10
+ }
11
+ )
12
+
13
+ @options = {
14
+ :head => {
15
+ 'Cache-control' => 'no-cache',
16
+ :some_header => 'some value',
17
+ 'accept' => 'application/json'
18
+ },
19
+ :body => {:hello => 'world'}
20
+ }
21
+
22
+ @session.should_receive(:uri_to).with("/path").
23
+ and_return(Addressable::URI.parse("https://api.grid5000.fr/path"))
24
+ end
25
+
26
+ it "should correctly build the request [no body]" do
27
+
28
+ request = Restfully::HTTP::Request.new(@session, :get, "/path", @options)
29
+ request.head.should == {
30
+ 'Cache-Control' => 'no-cache',
31
+ 'Some-Header' => 'some value',
32
+ 'Accept' => 'application/json',
33
+ 'Accept-Encoding' => 'gzip, deflate'
34
+ }
35
+ request.method.should == :get
36
+ end
37
+
38
+ it "should correctly add the query parameters to the URI" do
39
+ @options[:query] = {:k1 => 'v1'}
40
+ request = Restfully::HTTP::Request.new(@session, :get, "/path", @options)
41
+ request.uri.to_s.should == "https://api.grid5000.fr/path?k1=v1"
42
+ end
43
+
44
+ it "should correctly update the request with the given parameters" do
45
+ @options[:query] = {:k1 => 'v1', :k2 => [1,2,3]}
46
+ @options[:head]['Accept'] = 'text/plain'
47
+ request = Restfully::HTTP::Request.new(@session, :get, "/path", @options)
48
+ request.update!(:headers => {'whatever' => 'value'}, :query => {'k1' => 'xx'}).should_not be_nil
49
+ request.uri.to_s.should == 'https://api.grid5000.fr/path?k1=xx'
50
+ request.head.should include("Whatever"=>"value", 'Accept' => 'text/plain')
51
+ end
52
+
53
+ it "should return nil if no changes were made when updating the request" do
54
+ @options[:query] = {:k1 => 'v1', :k2 => [1,2,3]}
55
+ request = Restfully::HTTP::Request.new(@session, :get, "/path", @options)
56
+ request.update!(@options).should be_nil
57
+ end
58
+
59
+ it "should tell if the request is no-cache" do
60
+ @options[:head]['Cache-Control'] = 'no-cache, max-age=0'
61
+ request = Restfully::HTTP::Request.new(@session, :get, "/path", @options)
62
+ request.no_cache?.should be_true
63
+ end
64
+
65
+ describe "with body" do
66
+ it "should correctly build the request [body as hash, no content-type]" do
67
+ @options[:body] = {"k1" => 'value1', 'k2' => ['a','b','c']}
68
+ request = Restfully::HTTP::Request.new(@session, :post, "/path", @options)
69
+ request.head['Content-Type'].
70
+ should == 'application/x-www-form-urlencoded'
71
+ request.method.should == :post
72
+ request.body.should == "k1=value1&k2[]=a&k2[]=b&k2[]=c"
73
+ end
74
+
75
+ it "should correctly build the request [body as hash, content-type=json]" do
76
+ @options[:body] = {"k1" => 'value1', 'k2' => ['a','b','c']}
77
+ @options[:head][:content_type] = 'application/json'
78
+
79
+ request = Restfully::HTTP::Request.new(@session, :post, "/path", @options)
80
+ request.head['Content-Type'].
81
+ should == 'application/json'
82
+ request.method.should == :post
83
+ request.body.should == JSON.dump(@options[:body])
84
+ end
85
+
86
+ it "should correctly build the request [body as string, content-type=json]" do
87
+ @options[:body] = JSON.dump({"k1" => 'value1', 'k2' => ['a','b','c']})
88
+ @options[:head][:content_type] = 'application/json'
89
+ request = Restfully::HTTP::Request.new(@session, :post, "/path", @options)
90
+
91
+ request.head['Content-Type'].
92
+ should == 'application/json'
93
+ request.body.should == @options[:body]
94
+ end
95
+ end
96
+
97
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfully::HTTP::Response do
4
+ before do
5
+ @session = mock(Restfully::Session, :logger => Logger.new(STDERR))
6
+ @io = StringIO.new(fixture('grid5000-rennes.json'))
7
+ end
8
+ it "should correctly initialize the response" do
9
+ content = @io.read
10
+ @io.rewind
11
+ response = Restfully::HTTP::Response.new(@session, 200, {
12
+ 'Content-Type' => 'application/json'
13
+ }, @io)
14
+
15
+ response.code.should == 200
16
+ response.head.should == {"Content-Type"=>"application/json"}
17
+ response.media_type.should be_a(Restfully::MediaType::ApplicationJson)
18
+ response.io.should be_a(StringIO)
19
+ response.body.should == content
20
+ response.io.read.should == content
21
+ end
22
+
23
+
24
+ it "should always allow :get" do
25
+ response = Restfully::HTTP::Response.new(@session, 200, {
26
+ 'Content-Type' => 'application/json'
27
+ }, @io)
28
+ response.allow?(:get).should be_true
29
+ end
30
+
31
+ it "should find the allowed methods in the header (if any)" do
32
+ response = Restfully::HTTP::Response.new(@session, 200, {
33
+ 'Content-Type' => 'application/json',
34
+ 'Allow' => 'GET, POST'
35
+ }, @io)
36
+
37
+ response.allow?(:get).should be_true
38
+ response.allow?(:post).should be_true
39
+ end
40
+
41
+ it "should raise an error if it cannot find a corresponding media-type" do
42
+ response = Restfully::HTTP::Response.new(@session, 200, {
43
+ 'Content-Type' => 'whatever'
44
+ }, @io)
45
+ lambda{
46
+ response.media_type
47
+ }.should raise_error(
48
+ Restfully::Error,
49
+ "Cannot find a media-type for content-type=\"whatever\""
50
+ )
51
+
52
+ end
53
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfully::Link do
4
+ before do
5
+ @uri = URI.parse('/x/y/z')
6
+ @valid_attributes = {
7
+ :rel => 'something',
8
+ :type => 'application/json',
9
+ :href => '/'
10
+ }
11
+ end
12
+
13
+ describe "accessors" do
14
+ it "should have a rel reader" do
15
+ link = Restfully::Link.new
16
+ link.should_not respond_to(:rel=)
17
+ link.should respond_to(:rel)
18
+ end
19
+ it "should have a href reader" do
20
+ link = Restfully::Link.new
21
+ link.should_not respond_to(:href=)
22
+ link.should respond_to(:href)
23
+ end
24
+ it "should have a title reader" do
25
+ link = Restfully::Link.new
26
+ link.should_not respond_to(:title=)
27
+ link.should respond_to(:title)
28
+ end
29
+ it "should have a errors reader" do
30
+ link = Restfully::Link.new
31
+ link.should_not respond_to(:errors=)
32
+ link.should respond_to(:errors)
33
+ end
34
+ it "should respond to valid?" do
35
+ link = Restfully::Link.new
36
+ link.should respond_to(:valid?)
37
+ end
38
+ end
39
+
40
+ describe "validations" do
41
+ before do
42
+ @media_type = mock(Restfully::MediaType::AbstractMediaType)
43
+ end
44
+
45
+ it "should not be valid if there is no type" do
46
+ link = Restfully::Link.new(@valid_attributes.merge(:type => ''))
47
+ link.should_not be_valid
48
+ link.errors.should == ["type cannot be blank"]
49
+ end
50
+
51
+ it "should not be valid if a media_type cannot be found" do
52
+ Restfully::MediaType.catalog.clear
53
+ link = Restfully::Link.new(@valid_attributes)
54
+ link.should_not be_valid
55
+ link.errors.should == ["cannot find a MediaType for type \"application/json\""]
56
+ end
57
+
58
+
59
+ describe "with a valid catalog" do
60
+ before do
61
+ Restfully::MediaType.stub!(:find).and_return(@media_type)
62
+ end
63
+
64
+ it "should be valid" do
65
+ link = Restfully::Link.new(@valid_attributes)
66
+ link.should be_valid
67
+ end
68
+
69
+ it "should be valid even if the href is ''" do
70
+ link = Restfully::Link.new(@valid_attributes.merge(:href => ''))
71
+ link.should be_valid
72
+ link.href.should == URI.parse("")
73
+ end
74
+ end
75
+
76
+
77
+ end
78
+
79
+
80
+ end
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfully::MediaType::ApplicationVndBonfireXml do
4
+ before do
5
+ Restfully::MediaType.register Restfully::MediaType::ApplicationVndBonfireXml
6
+
7
+ @expected_compute_hash = {"name"=>"Compute name", "startup"=>{"href"=>"file:///path/to/startup-script/sh"}, "instance_type"=>"small", "__type__"=>"compute", "description"=>"Compute description", "nic"=>[{"device"=>"eth0", "mac"=>"AA:AA:AA:AA", "network"=>{"href"=>"/locations/fr-inria/networks/1"}, "ip"=>"123.123.123.123"}, {"device"=>"eth1", "mac"=>"BB:BB:BB:BB", "network"=>{"href"=>"/locations/fr-inria/networks/2"}, "ip"=>"123.123.124.2"}], "context"=>{"bonfire_credentials"=>"crohr:p4ssw0rd", "monitoring_ip"=>"123.123.123.2"}, "link"=>[{"href"=>"/locations/fr-inria", "rel"=>"location", "type"=>"application/vnd.bonfire+xml"}, {"href"=>"/locations/uk-epcc/computes/1", "rel"=>"self"}], "disk"=>[{"type"=>"OS", "storage"=>{"href"=>"/locations/fr-inria/storages/1"}, "target"=>"sda"}, {"type"=>"CDROM", "storage"=>{"href"=>"/locations/fr-inria/storages/2"}, "target"=>"sdc"}], "state"=>"ACTIVE"}
8
+ @fixture = fixture("bonfire-compute-existing.xml")
9
+ @collection = fixture("bonfire-network-collection.xml")
10
+ @session = Restfully::Session.new(:uri => "http://localhost:8000")
11
+ end
12
+
13
+ describe "loading from xml" do
14
+ it "should load the io into a Hash" do
15
+ media = Restfully::MediaType::ApplicationVndBonfireXml.new(@fixture, @session)
16
+ media.io.should == @fixture
17
+ media.unserialized.should be_a(Hash)
18
+ end
19
+
20
+ it "should build the Hash from the XML doc [resource]" do
21
+ media = Restfully::MediaType::ApplicationVndBonfireXml.new(@fixture, @session)
22
+ media.property.should == @expected_compute_hash
23
+ end
24
+
25
+ it "should build the Hash from the XML doc [resource] 2" do
26
+ media = Restfully::MediaType::ApplicationVndBonfireXml.new(
27
+ fixture("bonfire-network-existing.xml"), @session
28
+ )
29
+ media.property.should == {"name"=>"Private LAN", "public"=>"YES", "id"=>"29", "__type__"=>"network", "link"=>[{"href"=>"/locations/de-hlrs/networks/29", "rel"=>"self"}]}
30
+ end
31
+
32
+ it "should build the Hash from the XML doc [collection]" do
33
+ media = Restfully::MediaType::ApplicationVndBonfireXml.new(@collection, @session)
34
+ media.should be_collection
35
+ media.property.should == {"items"=>[{"address"=>"10.0.0.1", "name"=>"Network1", "size"=>"C", "__type__"=>"network", "description"=>"Network1 description", "link"=>[{"href"=>"/locations/fr-inria", "rel"=>"location"}, {"href"=>"/locations/fr-inria/networks/1", "rel"=>"self"}], "visibility"=>"public"}, {"address"=>"10.0.0.1", "name"=>"Network2", "size"=>"C", "__type__"=>"network", "description"=>"Network2 description", "link"=>[{"href"=>"/locations/fr-inria", "rel"=>"location"}, {"href"=>"/locations/fr-inria/networks/2", "rel"=>"self"}], "visibility"=>"private"}], "total"=>2, "offset"=>0, "__type__"=>"collection", "link"=>[{"href"=>"/locations/fr-inria/networks?limit=20", "rel"=>"top", "type"=>"application/vnd.bonfire+xml"}, {"href"=>"/locations/fr-inria", "rel"=>"parent", "type"=>"application/vnd.bonfire+xml"}, {"href"=>"/locations/fr-inria/networks", "rel"=>"self"}]}
36
+ end
37
+
38
+ it "should build the Hash from the XML doc [empty collection]" do
39
+ media = Restfully::MediaType::ApplicationVndBonfireXml.new(
40
+ fixture("bonfire-empty-collection.xml"), @session
41
+ )
42
+ media.should be_collection
43
+ items = []
44
+ media.each{|i| items << i}
45
+ items.should be_empty
46
+ end
47
+
48
+ it "should be able to iterate over a collection" do
49
+ media = Restfully::MediaType::ApplicationVndBonfireXml.new(fixture("bonfire-experiment-collection.xml"), @session)
50
+ items = []
51
+ media.each{|i| items<<i}
52
+ items.length.should == 3
53
+ end
54
+
55
+
56
+ end
57
+
58
+ describe "restfully resource" do
59
+ it "should correctly load a resource" do
60
+ req = Restfully::HTTP::Request.new(@session, :get, "http://localhost:8000/locations/de-hlrs/networks/29", :head => {
61
+ 'Accept' => 'application/vnd.bonfire+xml'
62
+ })
63
+
64
+ res = Restfully::HTTP::Response.new(@session, 200, {
65
+ 'Content-Type' => 'application/vnd.bonfire+xml'
66
+ }, fixture("bonfire-network-existing.xml"))
67
+
68
+ res.media_type.should be_a(
69
+ Restfully::MediaType::ApplicationVndBonfireXml
70
+ )
71
+
72
+ resource = Restfully::Resource.new(@session, res, req).load
73
+
74
+ resource.uri.to_s.should == "http://localhost:8000/locations/de-hlrs/networks/29"
75
+ resource.collection?.should be_false
76
+ resource.properties.should == {"name"=>"Private LAN", "public"=>"YES", "id"=>"29"}
77
+ end
78
+
79
+ it "should correctly load and iterate over a collection" do
80
+ req = Restfully::HTTP::Request.new(@session, :get, "http://localhost:8000/experiments", :head => {
81
+ 'Accept' => 'application/vnd.bonfire+xml'
82
+ })
83
+
84
+ res = Restfully::HTTP::Response.new(@session, 200, {
85
+ 'Content-Type' => 'application/vnd.bonfire+xml'
86
+ }, fixture("bonfire-experiment-collection.xml"))
87
+
88
+ res.media_type.should be_a(Restfully::MediaType::ApplicationVndBonfireXml)
89
+
90
+ collection = Restfully::Resource.new(@session, res, req).load
91
+
92
+ collection.uri.to_s.should == "http://localhost:8000/experiments"
93
+ collection.collection?.should be_true
94
+ collection.length.should == 3
95
+ collection.total.should == 3
96
+ collection.offset.should == 0
97
+ collection.all?{|i| i.kind_of? Restfully::Resource}.should be_true
98
+ collection[0].should == collection.first
99
+ collection[-1].uri.to_s.should == "http://localhost:8000/experiments/3"
100
+ collection[-1].should == collection[2]
101
+ collection[-1].should == collection.last
102
+ collection[10].should be_nil
103
+ end
104
+
105
+ it "should correctly load an empty collection" do
106
+ req = Restfully::HTTP::Request.new(@session, :get, "http://localhost:8000/locations/uk-epcc/computes", :head => {
107
+ 'Accept' => 'application/vnd.bonfire+xml'
108
+ })
109
+
110
+ res = Restfully::HTTP::Response.new(@session, 200, {
111
+ 'Content-Type' => 'application/vnd.bonfire+xml'
112
+ }, fixture("bonfire-empty-collection.xml"))
113
+
114
+ res.media_type.should be_a(Restfully::MediaType::ApplicationVndBonfireXml)
115
+
116
+ collection = Restfully::Resource.new(@session, res, req).load
117
+ collection.collection?.should be_true
118
+ collection.length.should == 0
119
+ collection.inspect.should == "[]"
120
+ collection.should be_empty
121
+ end
122
+
123
+ it "should correctly load a collection with non-detailed items" do
124
+ req = Restfully::HTTP::Request.new(@session, :get, "http://localhost:8000/locations/de-hlrs/networks", :head => {
125
+ 'Accept' => 'application/vnd.bonfire+xml'
126
+ })
127
+
128
+ res = Restfully::HTTP::Response.new(@session, 200, {
129
+ 'Content-Type' => 'application/vnd.bonfire+xml'
130
+ }, fixture("bonfire-collection-with-fragments.xml"))
131
+
132
+ collection = Restfully::Resource.new(@session, res, req).load
133
+ collection.any?{|item| item.media_type.complete?}.should be_false
134
+ collection.inspect.should == "[{...}]"
135
+
136
+ stub_request(:get, "http://localhost:8000/locations/de-hlrs/networks/29").
137
+ with(:headers => {'Accept'=>'application/vnd.bonfire+xml', 'Accept-Encoding'=>'gzip, deflate', 'Cache-Control'=>'no-cache'}).
138
+ to_return(:status => 200, :body => fixture("bonfire-network-existing.xml"), :headers => {'Content-Type' => 'application/vnd.bonfire+xml'})
139
+ network = collection[0]
140
+ network.properties.should == {"name"=>"Private LAN", "public"=>"YES", "id"=>"29"}
141
+ end
142
+ end
143
+
144
+ describe "serializing to xml" do
145
+ it "should correctly serialize a resource" do
146
+ serialized = Restfully::MediaType::ApplicationVndBonfireXml.serialize(
147
+ @expected_compute_hash,
148
+ :uri => Addressable::URI.parse('http://path/to/computes')
149
+ )
150
+ serialized.should == "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<compute xmlns=\"http://api.bonfire-project.eu/doc/schemas/occi\">\n <startup href=\"file:///path/to/startup-script/sh\"/>\n <name>Compute name</name>\n <instance_type>small</instance_type>\n <link href=\"/locations/fr-inria\" rel=\"location\" type=\"application/vnd.bonfire+xml\"/>\n <link href=\"/locations/uk-epcc/computes/1\" rel=\"self\"/>\n <context>\n <bonfire_credentials>crohr:p4ssw0rd</bonfire_credentials>\n <monitoring_ip>123.123.123.2</monitoring_ip>\n </context>\n <nic>\n <device>eth0</device>\n <mac>AA:AA:AA:AA</mac>\n <network href=\"/locations/fr-inria/networks/1\"/>\n <ip>123.123.123.123</ip>\n </nic>\n <nic>\n <device>eth1</device>\n <mac>BB:BB:BB:BB</mac>\n <network href=\"/locations/fr-inria/networks/2\"/>\n <ip>123.123.124.2</ip>\n </nic>\n <description>Compute description</description>\n <disk>\n <type>OS</type>\n <storage href=\"/locations/fr-inria/storages/1\"/>\n <target>sda</target>\n </disk>\n <disk>\n <type>CDROM</type>\n <storage href=\"/locations/fr-inria/storages/2\"/>\n <target>sdc</target>\n </disk>\n <state>ACTIVE</state>\n</compute>\n"
151
+ end
152
+ end
153
+ end