You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by mf...@redhat.com on 2012/04/17 15:40:11 UTC

[PATCH core 32/32] Core: Added Minitest suite to test API abilities

From: Michal Fojtik <mf...@redhat.com>


Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/tests/drivers/api/api_test.rb               |  115 +++++++
 server/tests/drivers/api/buckets_test.rb           |  194 +++++++++++
 server/tests/drivers/api/common.rb                 |   58 ++++
 server/tests/drivers/api/drivers_test.rb           |  120 +++++++
 server/tests/drivers/api/hardware_profiles_test.rb |  221 +++++++++++++
 server/tests/drivers/api/images_test.rb            |  194 +++++++++++
 server/tests/drivers/api/instances_test.rb         |  340 ++++++++++++++++++++
 server/tests/drivers/api/keys_test.rb              |  158 +++++++++
 server/tests/drivers/api/realms_test.rb            |  129 ++++++++
 server/tests/drivers/api/storage_snapshots_test.rb |  111 +++++++
 server/tests/drivers/api/storage_volumes_test.rb   |  119 +++++++
 11 files changed, 1759 insertions(+)
 create mode 100644 server/tests/drivers/api/api_test.rb
 create mode 100644 server/tests/drivers/api/buckets_test.rb
 create mode 100644 server/tests/drivers/api/common.rb
 create mode 100644 server/tests/drivers/api/drivers_test.rb
 create mode 100644 server/tests/drivers/api/hardware_profiles_test.rb
 create mode 100644 server/tests/drivers/api/images_test.rb
 create mode 100644 server/tests/drivers/api/instances_test.rb
 create mode 100644 server/tests/drivers/api/keys_test.rb
 create mode 100644 server/tests/drivers/api/realms_test.rb
 create mode 100644 server/tests/drivers/api/storage_snapshots_test.rb
 create mode 100644 server/tests/drivers/api/storage_volumes_test.rb

diff --git a/server/tests/drivers/api/api_test.rb b/server/tests/drivers/api/api_test.rb
new file mode 100644
index 0000000..3308365
--- /dev/null
+++ b/server/tests/drivers/api/api_test.rb
@@ -0,0 +1,115 @@
+describe 'Deltacloud API' do
+  include Deltacloud::Test
+
+  it 'return HTTP_OK when accessing API entrypoint' do
+    get API_ROOT_URL
+    last_response.status.must_equal 200
+  end
+
+  it 'advertise the current driver in API entrypoint' do
+    get API_ROOT_URL
+    xml_response.root[:driver].must_equal ENV['API_DRIVER']
+  end
+
+  it 'advertise the current API version in API entrypoint' do
+    get API_ROOT_URL
+    xml_response.root[:version].must_equal API_VERSION
+  end
+
+  it 'advertise the current API version in HTTP headers' do
+    get API_ROOT_URL
+    last_response.headers['Server'].must_equal "Apache-Deltacloud/#{API_VERSION}"
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    get API_ROOT_URL
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'advertise collections in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link').wont_be_empty
+  end
+
+  it 'include the :href and :rel attribute for each collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link').each do |collection|
+      collection[:href].wont_be_nil
+      collection[:rel].wont_be_nil
+    end
+  end
+
+  it 'uses the absolute URI in the :href attribute for each collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link').each do |collection|
+      collection[:href].must_match /^http/
+    end
+  end
+
+  it 'advertise features for some collections in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link/feature').wont_be_empty
+  end
+
+  it 'advertise the name of the feature for some collections in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link/feature').each do |f|
+      f[:name].wont_be_nil
+    end
+  end
+
+  it 'must change the media type from XML to JSON using Accept headers' do
+    header 'Accept', 'application/json'
+    get API_ROOT_URL
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must change the media type to JSON using the "?format" parameter in URL' do
+    get API_ROOT_URL, { :format => 'json' }
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must change the driver when using X-Deltacloud-Driver HTTP header' do
+    header 'X-Deltacloud-Driver', 'ec2'
+    get API_ROOT_URL
+    xml_response.root[:driver].must_equal 'ec2'
+    header 'X-Deltacloud-Driver', 'mock'
+    get API_ROOT_URL
+    xml_response.root[:driver].must_equal 'mock'
+  end
+
+  it 'must change the features when driver is swapped using HTTP headers' do
+    header 'X-Deltacloud-Driver', 'ec2'
+    get API_ROOT_URL
+    # The 'user_name' feature is not supported currently for the EC2 driver
+    (xml_response/'api/link/feature').map { |f| f[:name] }.wont_include 'user_name'
+    header 'X-Deltacloud-Driver', 'mock'
+    get API_ROOT_URL
+    # But it's supported in Mock driver
+    (xml_response/'api/link/feature').map { |f| f[:name] }.must_include 'user_name'
+  end
+
+  it 'must re-validate the driver credentials when using "?force_auth" parameter in URL' do
+    get API_ROOT_URL, { :force_auth => '1' }
+    last_response.status.must_equal 401
+    auth_as_mock
+    get API_ROOT_URL, { :force_auth => '1' }
+    last_response.status.must_equal 200
+  end
+
+  it 'must change the API PROVIDER using the /api;provider matrix parameter in URI' do
+    get API_ROOT_URL + ';provider=test1'
+    xml_response.root[:provider].wont_be_nil
+    xml_response.root[:provider].must_equal 'test1'
+    get API_ROOT_URL + ';provider=test2'
+    xml_response.root[:provider].must_equal 'test2'
+  end
+
+  it 'must change the API DRIVER using the /api;driver matrix parameter in URI' do
+    get API_ROOT_URL + ';driver=ec2'
+    xml_response.root[:driver].must_equal 'ec2'
+    get API_ROOT_URL + ';driver=mock'
+    xml_response.root[:driver].must_equal 'mock'
+  end
+
+end
diff --git a/server/tests/drivers/api/buckets_test.rb b/server/tests/drivers/api/buckets_test.rb
new file mode 100644
index 0000000..47b8007
--- /dev/null
+++ b/server/tests/drivers/api/buckets_test.rb
@@ -0,0 +1,194 @@
+describe 'Deltacloud API buckets' do
+  include Deltacloud::Test
+
+  it 'must advertise have the buckets collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link[@rel=buckets]').wont_be_empty
+  end
+
+  it 'must require authentication to access the "bucket" collection' do
+    get collection_url(:buckets)
+    last_response.status.must_equal 401
+  end
+
+  it 'should respond with HTTP_OK when accessing the :buckets collection with authentication' do
+    auth_as_mock
+    get collection_url(:buckets)
+    last_response.status.must_equal 200
+  end
+
+  it 'should support the JSON media type' do
+    auth_as_mock
+    header 'Accept', 'application/json'
+    get collection_url(:buckets)
+    last_response.status.must_equal 200
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    auth_as_mock
+    get collection_url(:buckets)
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'must have the "buckets" element on top level' do
+    auth_as_mock
+    get collection_url(:buckets)
+    xml_response.root.name.must_equal 'buckets'
+  end
+
+  it 'must have some "bucket" elements inside "buckets"' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').wont_be_empty
+  end
+
+  it 'must provide the :id attribute for each bucket in collection' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "bucket" element in collection' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the bucket' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must return the list of valid parameters for the :index action' do
+    auth_as_mock
+    options collection_url(:buckets) + '/index'
+    last_response.headers['Allow'].wont_be_nil
+  end
+
+  it 'must have the "name" element defined for each bucket in collection' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      (r/'name').wont_be_nil
+    end
+  end
+
+  it 'must have the "state" element defined for each bucket in collection' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      (r/'state').wont_be_nil
+    end
+  end
+
+  it 'must return the full "bucket" when following the URL in bucket element' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      get collection_url(:buckets) + '/' + r[:id]
+      last_response.status.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the bucket and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      get collection_url(:buckets) + '/' + r[:id]
+      (xml_response/'name').wont_be_empty
+      (xml_response/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+  it 'must have the "size" element for the bucket and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      get collection_url(:buckets) + '/' + r[:id]
+      (xml_response/'size').wont_be_empty
+      (xml_response/'size').first.text.must_equal((r/'size').first.text)
+    end
+  end
+
+  it 'must have the "blob" elements for the bucket and it should match with the ones in collection' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      get collection_url(:buckets) + '/' + r[:id]
+      (xml_response/'bucket/blob').wont_be_empty
+      (xml_response/'bucket/blob').size.to_s.must_equal (xml_response/'size').first.text
+      (xml_response/'bucket/blob').each do |b|
+        b[:id].wont_be_nil
+        b[:href].wont_be_nil
+        b[:href].must_match /^http/
+        b[:href].must_match /#{r[:id]}\/#{b[:id]}$/
+      end
+    end
+  end
+
+  it 'must have the "blob" elements for the bucket and it should match with the ones in collection' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      get collection_url(:buckets) + '/' + r[:id]
+      (xml_response/'bucket/blob').wont_be_empty
+      (xml_response/'bucket/blob').size.to_s.must_equal (xml_response/'size').first.text
+      (xml_response/'bucket/blob').each do |b|
+        b[:id].wont_be_nil
+        b[:href].wont_be_nil
+        b[:href].must_match /^http/
+        b[:href].must_match /#{r[:id]}\/#{b[:id]}$/
+      end
+    end
+  end
+
+  it 'must allow to get all blobs details and the details should be set correctly' do
+    auth_as_mock
+    get collection_url(:buckets)
+    (xml_response/'buckets/bucket').each do |r|
+      get collection_url(:buckets) + '/' + r[:id]
+      (xml_response/'bucket/blob').each do |b|
+        get collection_url(:buckets) + '/' + r[:id] + '/' + b[:id]
+        xml_response.root.name.must_equal 'blob'
+        xml_response.root[:id].must_equal b[:id]
+        (xml_response/'bucket').wont_be_empty
+        (xml_response/'bucket').size.must_equal 1
+        (xml_response/'bucket').first[:id].wont_be_nil
+        (xml_response/'bucket').first[:href].wont_be_nil
+        (xml_response/'content_length').wont_be_empty
+        (xml_response/'content_length').size.must_equal 1
+        (xml_response/'content_length').first.text.must_match /^(\d+)$/
+        (xml_response/'content_type').wont_be_empty
+        (xml_response/'content_type').size.must_equal 1
+        (xml_response/'content_type').first.text.wont_be_empty
+        (xml_response/'last_modified').wont_be_empty
+        (xml_response/'last_modified').size.must_equal 1
+        (xml_response/'last_modified').first.text.wont_be_empty
+        (xml_response/'content').wont_be_empty
+        (xml_response/'content').size.must_equal 1
+        (xml_response/'content').first[:rel].wont_be_nil
+        (xml_response/'content').first[:rel].must_equal 'blob_content'
+        (xml_response/'content').first[:href].wont_be_nil
+        (xml_response/'content').first[:href].must_match /^http/
+        (xml_response/'content').first[:href].must_match /\/content$/
+      end
+    end
+  end
+
+end
diff --git a/server/tests/drivers/api/common.rb b/server/tests/drivers/api/common.rb
new file mode 100644
index 0000000..a45c1f4
--- /dev/null
+++ b/server/tests/drivers/api/common.rb
@@ -0,0 +1,58 @@
+unless Kernel.respond_to?(:require_relative)
+  module Kernel
+    def require_relative(path)
+      require File.join(File.dirname(caller[0]), path.to_str)
+    end
+  end
+end
+
+API_ROOT_URL = "/api" unless defined?(API_ROOT_URL)
+API_VERSION = "1.0.0" unless defined?(API_VERSION)
+ENV['API_DRIVER'] ||= 'mock'
+
+ENV['API_USERNAME'] ||= 'mockuser'
+ENV['API_PASSWORD'] ||= 'mockpassword'
+
+require_relative '../../lib/deltacloud/server.rb'
+
+require 'minitest/autorun'
+require 'rack/test'
+require 'nokogiri'
+require 'json'
+
+require 'pp'
+
+module Deltacloud
+  module Test
+    include Rack::Test::Methods
+
+    def included?(sub)
+      sub.class_eval do
+        before do
+          header 'Accept', 'application/xml'
+        end
+      end
+    end
+
+    def xml_response
+      Nokogiri::XML(last_response.body)
+    end
+
+    def auth_as_mock
+      authorize ENV['API_USERNAME'], ENV['API_PASSWORD']
+    end
+
+    def collection_url(collection)
+      [API_ROOT_URL, collection.to_s].join('/')
+    end
+
+    def app
+      Rack::Builder.new {
+        map '/' do
+          use Rack::Static, :urls => ["/stylesheets", "/javascripts"], :root => "public"
+          run Rack::Cascade.new([Deltacloud::API])
+        end
+      }
+    end
+  end
+end
diff --git a/server/tests/drivers/api/drivers_test.rb b/server/tests/drivers/api/drivers_test.rb
new file mode 100644
index 0000000..41c2e66
--- /dev/null
+++ b/server/tests/drivers/api/drivers_test.rb
@@ -0,0 +1,120 @@
+describe 'Deltacloud API drivers' do
+  include Deltacloud::Test
+
+  it 'must advertise have the drivers collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link[@rel=drivers]').wont_be_empty
+  end
+
+  it 'must not require authentication to access the "driver" collection' do
+    get collection_url(:drivers)
+    last_response.status.must_equal 200
+  end
+
+  it 'should respond with HTTP_OK when accessing the :drivers collection with authentication' do
+    get collection_url(:drivers)
+    last_response.status.must_equal 200
+  end
+
+  it 'should support the JSON media type' do
+    header 'Accept', 'application/json'
+    get collection_url(:drivers)
+    last_response.status.must_equal 200
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    get collection_url(:drivers)
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'must have the "drivers" element on top level' do
+    get collection_url(:drivers)
+    xml_response.root.name.must_equal 'drivers'
+  end
+
+  it 'must have some "driver" elements inside "drivers"' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver').wont_be_empty
+  end
+
+  it 'must provide the :id attribute for each driver in collection' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "driver" element in collection' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the driver' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must return the list of valid parameters for the :index action' do
+    options collection_url(:drivers) + '/index'
+    last_response.headers['Allow'].wont_be_nil
+  end
+
+  it 'must have the "name" element defined for each driver in collection' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver').each do |r|
+      (r/'name').wont_be_nil
+    end
+  end
+
+
+  it 'must return the full "driver" when following the URL in driver element' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver').each do |r|
+      get collection_url(:drivers) + '/' + r[:id]
+      last_response.status.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the driver and it should match with the one in collection' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver').each do |r|
+      get collection_url(:drivers) + '/' + r[:id]
+      (xml_response/'name').wont_be_empty
+      (xml_response/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+  it 'should advertise available providers for some drivers' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver/provider').each do |p|
+      p[:id].wont_be_nil
+    end
+  end
+
+  it 'should expose entrypoints for each provider if driver has providers defined' do
+    get collection_url(:drivers)
+    (xml_response/'drivers/driver/provider').each do |p|
+      get collection_url(:drivers) + '/' + p.parent[:id]
+      (xml_response/"driver/provider[@id=#{p[:id]}]").wont_be_empty
+      (xml_response/"driver/provider[@id=#{p[:id]}]").size.must_equal 1
+      (xml_response/"driver/provider[@id=#{p[:id]}]/entrypoint").wont_be_empty
+      (xml_response/"driver/provider[@id=#{p[:id]}]/entrypoint").each do |e|
+        e[:kind].wont_be_nil
+        e.text.wont_be_empty
+      end
+    end
+  end
+
+end
diff --git a/server/tests/drivers/api/hardware_profiles_test.rb b/server/tests/drivers/api/hardware_profiles_test.rb
new file mode 100644
index 0000000..3dad5a8
--- /dev/null
+++ b/server/tests/drivers/api/hardware_profiles_test.rb
@@ -0,0 +1,221 @@
+describe 'Deltacloud API Hardware Profiles' do
+  include Deltacloud::Test
+
+  it 'must advertise have the hardware_profiles collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link[@rel=hardware_profiles]').wont_be_empty
+  end
+
+  it 'should respond with HTTP_OK when accessing the :hardware_profiles collection with authentication' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    last_response.status.must_equal 200
+  end
+
+  it 'should support the JSON media type' do
+    auth_as_mock
+    header 'Accept', 'application/json'
+    get collection_url(:hardware_profiles)
+    last_response.status.must_equal 200
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'must have the "hardware_profiles" element on top level' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    xml_response.root.name.must_equal 'hardware_profiles'
+  end
+
+  it 'must have some "hardware_profile" elements inside "hardware_profiles"' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').wont_be_empty
+  end
+
+  it 'must provide the :id attribute for each hardware_profile in collection' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "hardware_profile" element in collection' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the hardware_profile' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must return the list of valid parameters for the :index action' do
+    auth_as_mock
+    options collection_url(:hardware_profiles) + '/index'
+    last_response.headers['Allow'].wont_be_nil
+  end
+
+  it 'must have the "name" element defined for each hardware_profile in collection' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      (r/'name').wont_be_empty
+    end
+  end
+
+  it 'should have the "property" element defined if not the opaque hardware_profile' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').wont_be_empty
+    end
+  end
+
+  it 'must define the :kind attribute for each "property" ' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').each { |p| p[:kind].wont_be_nil }
+    end
+  end
+
+  it 'must define the :name attribute for each "property" ' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').each { |p| p[:name].wont_be_nil }
+    end
+  end
+
+  it 'must define the :unit attribute for each "property" ' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').each { |p| p[:unit].wont_be_nil }
+    end
+  end
+
+  it 'must define the :value attribute for each "property" ' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').each { |p| p[:value].wont_be_nil }
+    end
+  end
+
+  it 'must define the "param" element if property kind is not "fixed"' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').each do |p|
+        next if p[:kind] == 'fixed'
+        (p/'param').wont_be_empty
+        (p/'param').size.must_equal 1
+        (p/'param').first[:href].wont_be_nil
+        (p/'param').first[:href].must_match /^http/
+        (p/'param').first[:method].wont_be_nil
+        (p/'param').first[:name].wont_be_nil
+        (p/'param').first[:operation].wont_be_nil
+      end
+    end
+  end
+
+  it 'must provide the list of valid values when the property is defined as "enum"' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').each do |p|
+        next if p[:kind] != 'enum'
+        (p/'enum/entry').wont_be_empty
+        (p/'enum/entry').each { |e| e[:value].wont_be_nil }
+      end
+    end
+  end
+
+  it 'must provide the range of valid values when the property is defined as "range"' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').each do |p|
+        next if p[:kind] != 'range'
+        (p/'range').wont_be_empty
+        (p/'range').size.must_equal 1
+        (p/'range').first[:first].wont_be_nil
+        (p/'range').first[:last].wont_be_nil
+      end
+    end
+  end
+
+  it 'must provide the default value within the range if property defined as "range"' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').each do |p|
+        next if p[:kind] != 'range'
+        ((p/'range').first[:first].to_i..(p/'range').first[:last].to_i).include?(p[:value].to_i).must_equal true
+      end
+    end
+  end
+
+  it 'must provide the default value that is included in enum list if property defined as "enum"' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      next if r[:id] == 'opaque'
+      (r/'property').each do |p|
+        next if p[:kind] != 'enum'
+        (p/'enum/entry').map { |e| e[:value] }.include?(p[:value]).must_equal true
+      end
+    end
+  end
+
+  it 'must return the full "hardware_profile" when following the URL in hardware_profile element' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      get collection_url(:hardware_profiles) + '/' + r[:id]
+      last_response.status.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the hardware_profile and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:hardware_profiles)
+    (xml_response/'hardware_profiles/hardware_profile').each do |r|
+      get collection_url(:hardware_profiles) + '/' + r[:id]
+      (xml_response/'name').wont_be_empty
+      (xml_response/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+end
diff --git a/server/tests/drivers/api/images_test.rb b/server/tests/drivers/api/images_test.rb
new file mode 100644
index 0000000..3faf752
--- /dev/null
+++ b/server/tests/drivers/api/images_test.rb
@@ -0,0 +1,194 @@
+describe 'Deltacloud API Images' do
+  include Deltacloud::Test
+
+  it 'must advertise have the images collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link[@rel=images]').wont_be_empty
+  end
+
+  it 'must require authentication to access the "image" collection' do
+    get collection_url(:images)
+    last_response.status.must_equal 401
+  end
+
+  it 'should respond with HTTP_OK when accessing the :images collection with authentication' do
+    auth_as_mock
+    get collection_url(:images)
+    last_response.status.must_equal 200
+  end
+
+  it 'should support the JSON media type' do
+    auth_as_mock
+    header 'Accept', 'application/json'
+    get collection_url(:images)
+    last_response.status.must_equal 200
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    auth_as_mock
+    get collection_url(:images)
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'must have the "images" element on top level' do
+    auth_as_mock
+    get collection_url(:images)
+    xml_response.root.name.must_equal 'images'
+  end
+
+  it 'must have some "image" elements inside "images"' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').wont_be_empty
+  end
+
+  it 'must provide the :id attribute for each image in collection' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "image" element in collection' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the image' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must return the list of valid parameters for the :index action' do
+    auth_as_mock
+    options collection_url(:images) + '/index'
+    last_response.headers['Allow'].wont_be_nil
+  end
+
+  it 'must have the "name" element defined for each image in collection' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      (r/'name').wont_be_empty
+    end
+  end
+
+  it 'must have the "state" element defined for each image in collection' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      (r/'state').wont_be_empty
+    end
+  end
+
+  it 'must return the full "image" when following the URL in image element' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      get collection_url(:images) + '/' + r[:id]
+      last_response.status.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the image and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      get collection_url(:images) + '/' + r[:id]
+      (xml_response/'name').wont_be_empty
+      (xml_response/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+  it 'must have the "name" element for the image and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      get collection_url(:images) + '/' + r[:id]
+      (xml_response/'state').wont_be_empty
+      (xml_response/'state').first.text.must_equal((r/'state').first.text)
+    end
+  end
+
+  it 'should have the "owner_id" element for each image' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      get collection_url(:images) + '/' + r[:id]
+      (xml_response/'owner_id').wont_be_empty
+    end
+  end
+
+  it 'should have the "description" element for each image' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      get collection_url(:images) + '/' + r[:id]
+      (xml_response/'description').wont_be_empty
+    end
+  end
+
+  it 'should have the "architecture" element for each image' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      get collection_url(:images) + '/' + r[:id]
+      (xml_response/'architecture').wont_be_empty
+    end
+  end
+
+  it 'should include the list of compatible hardware_profiles for each image' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      get collection_url(:images) + '/' + r[:id]
+      (xml_response/'hardware_profiles/hardware_profile').wont_be_empty
+      (xml_response/'hardware_profiles/hardware_profile').each do |hwp|
+        hwp[:href].wont_be_nil
+        hwp[:href].must_match /^http/
+        hwp[:id].wont_be_nil
+        hwp[:href].must_match /\/#{hwp[:id]}$/
+        hwp[:rel].must_equal 'hardware_profile'
+      end
+    end
+  end
+
+  it 'should advertise the list of actions that can be executed for each image' do
+    auth_as_mock
+    get collection_url(:images)
+    (xml_response/'images/image').each do |r|
+      get collection_url(:images) + '/' + r[:id]
+      (xml_response/'actions/link').wont_be_empty
+      (xml_response/'actions/link').each do |l|
+        l[:href].wont_be_nil
+        l[:href].must_match /^http/
+        l[:method].wont_be_nil
+        l[:rel].wont_be_nil
+      end
+    end
+  end
+
+  it 'should give client HTML form to create new image' do
+    auth_as_mock
+    header 'Accept', 'text/html'
+    get collection_url(:images) + '/new'
+    last_response.status.must_equal 200
+  end
+
+end
diff --git a/server/tests/drivers/api/instances_test.rb b/server/tests/drivers/api/instances_test.rb
new file mode 100644
index 0000000..c601a6f
--- /dev/null
+++ b/server/tests/drivers/api/instances_test.rb
@@ -0,0 +1,340 @@
+describe 'Deltacloud API instances' do
+  include Deltacloud::Test
+
+  it 'must advertise have the instances collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link[@rel=instances]').wont_be_empty
+  end
+
+  it 'must require authentication to access the "instance" collection' do
+    get collection_url(:instances)
+    last_response.status.must_equal 401
+  end
+
+  it 'should respond with HTTP_OK when accessing the :instances collection with authentication' do
+    auth_as_mock
+    get collection_url(:instances)
+    last_response.status.must_equal 200
+  end
+
+  it 'should support the JSON media type' do
+    auth_as_mock
+    header 'Accept', 'application/json'
+    get collection_url(:instances)
+    last_response.status.must_equal 200
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    auth_as_mock
+    get collection_url(:instances)
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'must have the "instances" element on top level' do
+    auth_as_mock
+    get collection_url(:instances)
+    xml_response.root.name.must_equal 'instances'
+  end
+
+  it 'must have some "instance" elements inside "instances"' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').wont_be_empty
+  end
+
+  it 'must provide the :id attribute for each instance in collection' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "instance" element in collection' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the instance' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must return the list of valid parameters for the :index action' do
+    auth_as_mock
+    options collection_url(:instances) + '/index'
+    last_response.headers['Allow'].wont_be_nil
+  end
+
+  it 'must have the "name" element defined for each instance in collection' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      (r/'name').wont_be_empty
+    end
+  end
+
+  it 'must have the "state" element defined for each instance in collection' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      (r/'state').wont_be_empty
+      (r/'state').first.must_match /(RUNNING|STOPPED|PENDING)/
+    end
+  end
+
+  it 'must return the full "instance" when following the URL in instance element' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      last_response.status.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the instance and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      (xml_response/'name').wont_be_empty
+      (xml_response/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+  it 'must have the "name" element for the instance and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      (xml_response/'state').wont_be_empty
+      (xml_response/'state').first.text.must_equal((r/'state').first.text)
+    end
+  end
+
+  it 'must have the "owner_id" element for the instance and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      (xml_response/'owner_id').wont_be_empty
+      (xml_response/'owner_id').first.text.must_equal((r/'owner_id').first.text)
+    end
+  end
+
+  it 'must link to the realm that was used to during instance creation' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      (xml_response/'realm').wont_be_empty
+      (xml_response/'realm').size.must_equal 1
+      (xml_response/'realm').first[:id].wont_be_nil
+      (xml_response/'realm').first[:href].wont_be_nil
+      (xml_response/'realm').first[:href].must_match /\/#{(xml_response/'realm').first[:id]}$/
+    end
+  end
+
+  it 'must link to the image that was used to during instance creation' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      (xml_response/'image').wont_be_empty
+      (xml_response/'image').size.must_equal 1
+      (xml_response/'image').first[:id].wont_be_nil
+      (xml_response/'image').first[:href].wont_be_nil
+      (xml_response/'image').first[:href].must_match /\/#{(xml_response/'image').first[:id]}$/
+    end
+  end
+
+  it 'must link to the hardware_profile that was used to during instance creation' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      (xml_response/'hardware_profile').wont_be_empty
+      (xml_response/'hardware_profile').size.must_equal 1
+      (xml_response/'hardware_profile').first[:id].wont_be_nil
+      (xml_response/'hardware_profile').first[:href].wont_be_nil
+      (xml_response/'hardware_profile').first[:href].must_match /\/#{(xml_response/'hardware_profile').first[:id]}$/
+    end
+  end
+
+  it 'should advertise the public and private addresses of the instance' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      (xml_response/'public_addresses').wont_be_empty
+      (xml_response/'public_addresses').size.must_equal 1
+      (xml_response/'public_addresses/address').each do |a|
+        a[:type].wont_be_nil
+        a.text.strip.wont_be_empty
+      end
+      (xml_response/'private_addresses').wont_be_empty
+      (xml_response/'private_addresses').size.must_equal 1
+      (xml_response/'private_addresses/address').each do |a|
+        a[:type].wont_be_nil
+        a.text.strip.wont_be_empty
+      end
+    end
+  end
+
+  it 'should advertise the storage volumes used by the instance' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      (xml_response/'storage_volumes').wont_be_empty
+    end
+  end
+
+  it 'should advertise the list of actions that can be executed for each instance' do
+    auth_as_mock
+    get collection_url(:instances)
+    (xml_response/'instances/instance').each do |r|
+      get collection_url(:instances) + '/' + r[:id]
+      (xml_response/'actions/link').wont_be_empty
+      (xml_response/'actions/link').each do |l|
+        l[:href].wont_be_nil
+        l[:href].must_match /^http/
+        l[:method].wont_be_nil
+        l[:rel].wont_be_nil
+      end
+    end
+  end
+
+  it 'should allow to create and destroy new instance using the first available image without realm' do
+    auth_as_mock
+    get collection_url(:images)
+    image_id = (xml_response/'images/image').first[:id]
+    image_id.wont_be_nil
+    post collection_url(:instances), {
+      :image_id => image_id
+    }
+    last_response.status.must_equal 201 # HTTP_CREATED
+    last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance
+    instance_id = last_response.headers['Location'].split('/').last
+    # Get the instance and check if ID and image is set correctly
+    get collection_url(:instances) + '/' + instance_id
+    last_response.status.must_equal 200 # HTTP_OK
+    (xml_response/'instance').first[:id].must_equal instance_id
+    (xml_response/'instance/image').first[:id].must_equal image_id
+    # If instance is RUNNING then stop it
+    if (xml_response/'instance/state').first.text == 'RUNNING'
+      post collection_url(:instances) + '/' + instance_id + '/stop'
+      last_response.status.must_equal 202 # HTTP_NO_CONTENT
+    end
+    # Delete created instance
+    delete collection_url(:instances) + '/' + instance_id
+    last_response.status.must_equal 204 # HTTP_NO_CONTENT
+  end
+
+  it 'should allow to create and destroy new instance using the first available image within first realm' do
+    auth_as_mock
+    get collection_url(:images)
+    image_id = (xml_response/'images/image').first[:id]
+    get collection_url(:realms)
+    realm_id = (xml_response/'realms/realm').first[:id]
+    image_id.wont_be_nil
+    realm_id.wont_be_nil
+    post collection_url(:instances), {
+      :image_id => image_id,
+      :realm_id => realm_id,
+    }
+    last_response.status.must_equal 201 # HTTP_CREATED
+    last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance
+    instance_id = last_response.headers['Location'].split('/').last
+    # Get the instance and check if ID and image is set correctly
+    get collection_url(:instances) + '/' + instance_id
+    last_response.status.must_equal 200 # HTTP_OK
+    (xml_response/'instance').first[:id].must_equal instance_id
+    (xml_response/'instance/image').first[:id].must_equal image_id
+    (xml_response/'instance/realm').first[:id].must_equal realm_id
+    # If instance is RUNNING then stop it
+    if (xml_response/'instance/state').first.text == 'RUNNING'
+      post collection_url(:instances) + '/' + instance_id + '/stop'
+      last_response.status.must_equal 202 # HTTP_NO_CONTENT
+    end
+    # Delete created instance
+    delete collection_url(:instances) + '/' + instance_id
+    last_response.status.must_equal 204 # HTTP_NO_CONTENT
+  end
+
+  it 'should allow to create and destroy new instance using the first available image with user defined name' do
+    auth_as_mock
+    get collection_url(:images)
+    image_id = (xml_response/'images/image').first[:id]
+    image_id.wont_be_nil
+    name = "i#{Time.now.to_i}"
+    post collection_url(:instances), {
+      :image_id => image_id,
+      :name => name
+    }
+    last_response.status.must_equal 201 # HTTP_CREATED
+    last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance
+    instance_id = last_response.headers['Location'].split('/').last
+    # Get the instance and check if ID and image is set correctly
+    get collection_url(:instances) + '/' + instance_id
+    last_response.status.must_equal 200 # HTTP_OK
+    (xml_response/'instance').first[:id].must_equal instance_id
+    (xml_response/'instance/image').first[:id].must_equal image_id
+    (xml_response/'instance/name').first.text.must_equal name
+    # If instance is RUNNING then stop it
+    if (xml_response/'instance/state').first.text == 'RUNNING'
+      post collection_url(:instances) + '/' + instance_id + '/stop'
+      last_response.status.must_equal 202 # HTTP_NO_CONTENT
+    end
+    # Delete created instance
+    delete collection_url(:instances) + '/' + instance_id
+    last_response.status.must_equal 204 # HTTP_NO_CONTENT
+  end
+
+  it 'should allow to create and destroy new instance using the first available image and first hardware_profile' do
+    auth_as_mock
+    get collection_url(:images)
+    image_id = (xml_response/'images/image').first[:id]
+    get collection_url(:hardware_profiles)
+    hwp_id = (xml_response/'hardware_profiles/hardware_profile').first[:id]
+    image_id.wont_be_nil
+    name = "i#{Time.now.to_i}"
+    post collection_url(:instances), {
+      :image_id => image_id,
+      :hwp_id => hwp_id
+    }
+    last_response.status.must_equal 201 # HTTP_CREATED
+    last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance
+    instance_id = last_response.headers['Location'].split('/').last
+    # Get the instance and check if ID and image is set correctly
+    get collection_url(:instances) + '/' + instance_id
+    last_response.status.must_equal 200 # HTTP_OK
+    (xml_response/'instance').first[:id].must_equal instance_id
+    (xml_response/'instance/image').first[:id].must_equal image_id
+    (xml_response/'instance/hardware_profile').first[:id].must_equal hwp_id
+    # If instance is RUNNING then stop it
+    if (xml_response/'instance/state').first.text == 'RUNNING'
+      post collection_url(:instances) + '/' + instance_id + '/stop'
+      last_response.status.must_equal 202 # HTTP_NO_CONTENT
+    end
+    # Delete created instance
+    delete collection_url(:instances) + '/' + instance_id
+    last_response.status.must_equal 204 # HTTP_NO_CONTENT
+  end
+
+end
diff --git a/server/tests/drivers/api/keys_test.rb b/server/tests/drivers/api/keys_test.rb
new file mode 100644
index 0000000..9267b5a
--- /dev/null
+++ b/server/tests/drivers/api/keys_test.rb
@@ -0,0 +1,158 @@
+describe 'Deltacloud API Keys' do
+  include Deltacloud::Test
+
+  it 'must advertise have the keys collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link[@rel=keys]').wont_be_empty
+  end
+
+  it 'must require authentication to access the "key" collection' do
+    get collection_url(:keys)
+    last_response.status.must_equal 401
+  end
+
+  it 'should respond with HTTP_OK when accessing the :keys collection with authentication' do
+    auth_as_mock
+    get collection_url(:keys)
+    last_response.status.must_equal 200
+  end
+
+  it 'should support the JSON media type' do
+    auth_as_mock
+    header 'Accept', 'application/json'
+    get collection_url(:keys)
+    last_response.status.must_equal 200
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    auth_as_mock
+    get collection_url(:keys)
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'must have the "keys" element on top level' do
+    auth_as_mock
+    get collection_url(:keys)
+    xml_response.root.name.must_equal 'keys'
+  end
+
+  it 'must have some "key" elements inside "keys"' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').wont_be_empty
+  end
+
+  it 'must tell the kind of "key" elements inside "keys"' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |k|
+      k[:type].must_match /(key|password)/
+    end
+  end
+
+  it 'must provide the :id attribute for each key in collection' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "key" element in collection' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the key' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must return the list of valid parameters for the :index action' do
+    auth_as_mock
+    options collection_url(:keys) + '/index'
+    last_response.headers['Allow'].wont_be_nil
+  end
+
+  it 'must have the "name" element defined for each key in collection' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |r|
+      (r/'name').wont_be_empty
+    end
+  end
+
+
+  it 'must return the full "key" when following the URL in key element' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |r|
+      get collection_url(:keys) + '/' + r[:id]
+      last_response.status.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the key and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |r|
+      get collection_url(:keys) + '/' + r[:id]
+      (xml_response/'name').wont_be_empty
+      (xml_response/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+  it 'must have the "name" element for the key and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |r|
+      get collection_url(:keys) + '/' + r[:id]
+      (xml_response/'state').wont_be_empty
+      (xml_response/'state').first.text.must_equal((r/'state').first.text)
+    end
+  end
+
+  it 'should advertise the list of actions that can be executed for each key' do
+    auth_as_mock
+    get collection_url(:keys)
+    (xml_response/'keys/key').each do |r|
+      get collection_url(:keys) + '/' + r[:id]
+      (xml_response/'actions/link').wont_be_empty
+      (xml_response/'actions/link').each do |l|
+        l[:href].wont_be_nil
+        l[:href].must_match /^http/
+        l[:method].wont_be_nil
+        l[:rel].wont_be_nil
+      end
+    end
+  end
+
+  it 'should allow to create a new key and then remove it' do
+    auth_as_mock
+    key_name = Time.now.to_i.to_s
+    post collection_url(:keys), {
+      :name => 'test_key_'+key_name
+    }
+    last_response.status.must_equal 201 # HTTP_CREATED
+    get collection_url(:keys) + '/' + 'test_key_'+key_name
+    last_response.status.must_equal 200 # HTTP_OK
+    delete collection_url(:keys) + '/' + 'test_key_'+key_name
+    last_response.status.must_equal 204 # HTTP_NO_CONTENT
+  end
+
+end
diff --git a/server/tests/drivers/api/realms_test.rb b/server/tests/drivers/api/realms_test.rb
new file mode 100644
index 0000000..6bc9101
--- /dev/null
+++ b/server/tests/drivers/api/realms_test.rb
@@ -0,0 +1,129 @@
+describe 'Deltacloud API Realms' do
+  include Deltacloud::Test
+
+  it 'must advertise have the realms collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link[@rel=realms]').wont_be_empty
+  end
+
+  it 'must require authentication to access the "realm" collection' do
+    get collection_url(:realms)
+    last_response.status.must_equal 401
+  end
+
+  it 'should respond with HTTP_OK when accessing the :realms collection with authentication' do
+    auth_as_mock
+    get collection_url(:realms)
+    last_response.status.must_equal 200
+  end
+
+  it 'should support the JSON media type' do
+    auth_as_mock
+    header 'Accept', 'application/json'
+    get collection_url(:realms)
+    last_response.status.must_equal 200
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    auth_as_mock
+    get collection_url(:realms)
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'must have the "realms" element on top level' do
+    auth_as_mock
+    get collection_url(:realms)
+    xml_response.root.name.must_equal 'realms'
+  end
+
+  it 'must have some "realm" elements inside "realms"' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').wont_be_empty
+  end
+
+  it 'must provide the :id attribute for each realm in collection' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "realm" element in collection' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the realm' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must return the list of valid parameters for the :index action' do
+    auth_as_mock
+    options collection_url(:realms) + '/index'
+    last_response.headers['Allow'].wont_be_nil
+  end
+
+  it 'must have the "name" element defined for each realm in collection' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').each do |r|
+      (r/'name').wont_be_empty
+    end
+  end
+
+  it 'must have the "state" element defined for each realm in collection' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').each do |r|
+      (r/'state').wont_be_empty
+    end
+  end
+
+  it 'must return the full "realm" when following the URL in realm element' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').each do |r|
+      get collection_url(:realms) + '/' + r[:id]
+      last_response.status.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the realm and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').each do |r|
+      get collection_url(:realms) + '/' + r[:id]
+      (xml_response/'name').wont_be_empty
+      (xml_response/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+  it 'must have the "state" element for the realm and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:realms)
+    (xml_response/'realms/realm').each do |r|
+      get collection_url(:realms) + '/' + r[:id]
+      (xml_response/'state').wont_be_empty
+      (xml_response/'state').first.text.must_equal((r/'state').first.text)
+    end
+  end
+
+end
diff --git a/server/tests/drivers/api/storage_snapshots_test.rb b/server/tests/drivers/api/storage_snapshots_test.rb
new file mode 100644
index 0000000..52ea847
--- /dev/null
+++ b/server/tests/drivers/api/storage_snapshots_test.rb
@@ -0,0 +1,111 @@
+describe 'Deltacloud API storage_snapshots' do
+  include Deltacloud::Test
+
+  it 'must advertise have the storage_snapshots collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link[@rel=storage_snapshots]').wont_be_empty
+  end
+
+  it 'must require authentication to access the "storage_snapshot" collection' do
+    get collection_url(:storage_snapshots)
+    last_response.status.must_equal 401
+  end
+
+  it 'should respond with HTTP_OK when accessing the :storage_snapshots collection with authentication' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    last_response.status.must_equal 200
+  end
+
+  it 'should support the JSON media type' do
+    auth_as_mock
+    header 'Accept', 'application/json'
+    get collection_url(:storage_snapshots)
+    last_response.status.must_equal 200
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'must have the "storage_snapshots" element on top level' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    xml_response.root.name.must_equal 'storage_snapshots'
+  end
+
+  it 'must have some "storage_snapshot" elements inside "storage_snapshots"' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    (xml_response/'storage_snapshots/storage_snapshot').wont_be_empty
+  end
+
+  it 'must provide the :id attribute for each storage_snapshot in collection' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "storage_snapshot" element in collection' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the storage_snapshot' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must return the list of valid parameters for the :index action' do
+    auth_as_mock
+    options collection_url(:storage_snapshots) + '/index'
+    last_response.headers['Allow'].wont_be_nil
+  end
+
+  it 'must have the "name" element defined for each storage_snapshot in collection' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+      (r/'name').wont_be_empty
+    end
+  end
+
+  it 'must return the full "storage_snapshot" when following the URL in storage_snapshot element' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+      get collection_url(:storage_snapshots) + '/' + r[:id]
+      last_response.status.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the storage_snapshot and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:storage_snapshots)
+    (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+      get collection_url(:storage_snapshots) + '/' + r[:id]
+      (xml_response/'name').wont_be_empty
+      (xml_response/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+end
diff --git a/server/tests/drivers/api/storage_volumes_test.rb b/server/tests/drivers/api/storage_volumes_test.rb
new file mode 100644
index 0000000..cbafd5d
--- /dev/null
+++ b/server/tests/drivers/api/storage_volumes_test.rb
@@ -0,0 +1,119 @@
+describe 'Deltacloud API storage_volumes' do
+  include Deltacloud::Test
+
+  it 'must advertise have the storage_volumes collection in API entrypoint' do
+    get API_ROOT_URL
+    (xml_response/'api/link[@rel=storage_volumes]').wont_be_empty
+  end
+
+  it 'must require authentication to access the "storage_volume" collection' do
+    get collection_url(:storage_volumes)
+    last_response.status.must_equal 401
+  end
+
+  it 'should respond with HTTP_OK when accessing the :storage_volumes collection with authentication' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    last_response.status.must_equal 200
+  end
+
+  it 'should support the JSON media type' do
+    auth_as_mock
+    header 'Accept', 'application/json'
+    get collection_url(:storage_volumes)
+    last_response.status.must_equal 200
+    last_response.headers['Content-Type'].must_equal 'application/json'
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    last_response.headers['ETag'].wont_be_nil
+  end
+
+  it 'must have the "storage_volumes" element on top level' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    xml_response.root.name.must_equal 'storage_volumes'
+  end
+
+  it 'must have some "storage_volume" elements inside "storage_volumes"' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    (xml_response/'storage_volumes/storage_volume').wont_be_empty
+  end
+
+  it 'must provide the :id attribute for each storage_volume in collection' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    (xml_response/'storage_volumes/storage_volume').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "storage_volume" element in collection' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    (xml_response/'storage_volumes/storage_volume').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    (xml_response/'storage_volumes/storage_volume').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the storage_volume' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    (xml_response/'storage_volumes/storage_volume').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must return the list of valid parameters for the :index action' do
+    auth_as_mock
+    options collection_url(:storage_volumes) + '/index'
+    last_response.headers['Allow'].wont_be_nil
+  end
+
+  it 'must have the "name" element defined for each storage_volume in collection' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    (xml_response/'storage_volumes/storage_volume').each do |r|
+      (r/'name').wont_be_empty
+    end
+  end
+
+  it 'must have the "state" element defined for each storage_volume in collection' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    (xml_response/'storage_volumes/storage_volume').each do |r|
+      (r/'state').wont_be_empty
+    end
+  end
+
+  it 'must return the full "storage_volume" when following the URL in storage_volume element' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    (xml_response/'storage_volumes/storage_volume').each do |r|
+      get collection_url(:storage_volumes) + '/' + r[:id]
+      last_response.status.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the storage_volume and it should match with the one in collection' do
+    auth_as_mock
+    get collection_url(:storage_volumes)
+    (xml_response/'storage_volumes/storage_volume').each do |r|
+      get collection_url(:storage_volumes) + '/' + r[:id]
+      (xml_response/'name').wont_be_empty
+      (xml_response/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+end
-- 
1.7.10