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/07/31 14:33:24 UTC

Initial support for CIMI minitest (rev 2)

Hi,

This patch will add support for CIMI collection testing.

  -- Michal


[PATCH core 2/5] Core: Fixed url_for helper when used different frontend

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

* url_for by default check the ENV and then fallback to Deltacloud
  This patch makes it look into Sinatra 'settings' for the correct
  root_url

Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/lib/deltacloud/helpers/url_helper.rb |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/server/lib/deltacloud/helpers/url_helper.rb b/server/lib/deltacloud/helpers/url_helper.rb
index 1822057..bf82867 100644
--- a/server/lib/deltacloud/helpers/url_helper.rb
+++ b/server/lib/deltacloud/helpers/url_helper.rb
@@ -96,7 +96,7 @@ module Sinatra
         else
           port = ":#{port}"
         end
-        base = "#{scheme}://#{request_host}#{port}#{request.script_name.empty? ? Deltacloud[ENV['API_FRONTEND'] || :deltacloud].root_url : request.script_name}"
+        base = "#{scheme}://#{request_host}#{port}#{request.script_name.empty? ? settings.config.root_url : request.script_name}"
       else
         raise TypeError, "Unknown url_for mode #{mode}"
       end
-- 
1.7.10.2


[PATCH core 3/5] CIMI: Replaced return by 'halt' in force_auth

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

* Use the Sinatra 'halt' helper instead of return
  to make sure the correct HTTP status code is being
  delivered to the client.

Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/lib/cimi/collections/cloud_entry_point.rb |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/server/lib/cimi/collections/cloud_entry_point.rb b/server/lib/cimi/collections/cloud_entry_point.rb
index 5aa2630..6371a06 100644
--- a/server/lib/cimi/collections/cloud_entry_point.rb
+++ b/server/lib/cimi/collections/cloud_entry_point.rb
@@ -22,7 +22,7 @@ module CIMI::Collections
         description "list all resources of the cloud"
         control do
           if params[:force_auth]
-            return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials)
+            halt 401 unless driver.valid_credentials?(credentials)
           end
           entry_point = CIMI::Model::CloudEntryPoint.create(self)
           respond_to do |format|
-- 
1.7.10.2


Re: [PATCH core 4/5] Core: Minor tweak to deltacloud test helper

Posted by David Lutterkort <lu...@redhat.com>.
On Tue, 2012-07-31 at 14:33 +0200, mfojtik@redhat.com wrote:
> From: Michal Fojtik <mf...@redhat.com>
> 
> * The 'root_url' can be now used in this way:
> 
>   get root_url '/url'
> 
> Signed-off-by: Michal fojtik <mf...@redhat.com>
> ---
>  server/tests/deltacloud/common.rb |    4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/server/tests/deltacloud/common.rb b/server/tests/deltacloud/common.rb
> index d2f59e5..98b9884 100644
> --- a/server/tests/deltacloud/common.rb
> +++ b/server/tests/deltacloud/common.rb
> @@ -1,3 +1,5 @@
> +ENV['API_FRONTEND'] = 'cimi'
> +

Is this really what we want here ? I thought tests/deltacloud was for
the DC frontend, not for CIMI. We might need some config.ru-like setup
for each of the frontend test suites.

David



[PATCH core 4/5] Core: Minor tweak to deltacloud test helper

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

* The 'root_url' can be now used in this way:

  get root_url '/url'

Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/tests/deltacloud/common.rb |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/server/tests/deltacloud/common.rb b/server/tests/deltacloud/common.rb
index d2f59e5..98b9884 100644
--- a/server/tests/deltacloud/common.rb
+++ b/server/tests/deltacloud/common.rb
@@ -1,3 +1,5 @@
+ENV['API_FRONTEND'] = 'cimi'
+
 require_relative File.join('..', '..', 'lib', 'deltacloud_rack.rb')
 
 # Set the default driver used for server API tests
@@ -17,4 +19,4 @@ unless Deltacloud::config[:deltacloud]
   Deltacloud.require_frontend!
 end
 
-def root_url; Deltacloud.config[:deltacloud].root_url; end
+def root_url; Deltacloud.config[:cimi].root_url; end
-- 
1.7.10.2


Re: Initial support for CIMI minitest (rev 2)

Posted by David Lutterkort <lu...@redhat.com>.
On Thu, 2012-08-02 at 12:24 +0200, Michal Fojtik wrote:
> Hi,
> 
> The problem is that 'require_relative' is actually a 'rubygem' too,
> and so it require 'rubygems' to be required.
> 
> If you put 'require "rubygems"' into spec_helper.rb, then 'spec_helper'
> will need to be loaded by 'load' method to make it work.
> 
> I'm more fun of adding "require 'rubygems'" into each test file, in that
> case everything will work and we don't need to pollute RUBYOPT variable.

Yes, of course, you're right; so the standard test file header would be

  require 'rubygems'
  require 'require_relative'
  require_relative './common'

and then chain up to some toplevel test boot file.

> Another option is to execute single test file using:
> 
> bundle exec ./tests/cimi/...
> 
> Bundler will then take care of loading rubygems and everything runs smoothly.

I'd prefer doing Bundler.setup in some toplevel test helper, but it
would be preferrable to requiring env vars - though I'd much prefer the
boilerplate above for each test file.

David



Re: Initial support for CIMI minitest (rev 2)

Posted by Michal Fojtik <mf...@redhat.com>.
Hi,

The problem is that 'require_relative' is actually a 'rubygem' too,
and so it require 'rubygems' to be required.

If you put 'require "rubygems"' into spec_helper.rb, then 'spec_helper'
will need to be loaded by 'load' method to make it work.

I'm more fun of adding "require 'rubygems'" into each test file, in that
case everything will work and we don't need to pollute RUBYOPT variable.

Another option is to execute single test file using:

bundle exec ./tests/cimi/...

Bundler will then take care of loading rubygems and everything runs smoothly.

What do you think?

Michal Fojtik
http://deltacloud.org
mfojtik@redhat.com



On Aug 2, 2012, at 4:26 AM, David Lutterkort <lu...@redhat.com> wrote:

> On Wed, 2012-08-01 at 14:55 +0200, Michal Fojtik wrote:
>> Hi,
>> 
>> Seems like a missing 'require "rubygems"' for me.
>> 
>> Do you want me to add this into all files or we are good with:
>> 
>> export RUBYOPT="rubygems" ?
> 
> I don't want to rely on env vars for running tests. I am thinking that
> each test file should start with
> 
>        require 'require_relative'
>        require_relative './common'
> 
> and then each common.rb with
> 
>        require 'require_relative'
>        require_relative './test_helper'
> 
> and then require rubygems first thing in test_helper.
> 
> That sort of setup should avoid these issues.
> 
> David
> 
> 
> 


Re: Initial support for CIMI minitest (rev 2)

Posted by David Lutterkort <lu...@redhat.com>.
On Wed, 2012-08-01 at 14:55 +0200, Michal Fojtik wrote:
> Hi,
> 
> Seems like a missing 'require "rubygems"' for me.
> 
> Do you want me to add this into all files or we are good with:
> 
> export RUBYOPT="rubygems" ?

I don't want to rely on env vars for running tests. I am thinking that
each test file should start with

        require 'require_relative'
        require_relative './common'

and then each common.rb with

        require 'require_relative'
        require_relative './test_helper'

and then require rubygems first thing in test_helper.

That sort of setup should avoid these issues.

David

                


Re: Initial support for CIMI minitest (rev 2)

Posted by Michal Fojtik <mf...@redhat.com>.
Hi,

Seems like a missing 'require "rubygems"' for me.

Do you want me to add this into all files or we are good with:

export RUBYOPT="rubygems" ?

 -- Michal

Michal Fojtik
http://deltacloud.org
mfojtik@redhat.com



On Aug 1, 2012, at 12:35 AM, David Lutterkort <lu...@redhat.com> wrote:

> On Tue, 2012-07-31 at 14:33 +0200, mfojtik@redhat.com wrote:
>> This patch will add support for CIMI collection testing.
> 
> More ruby 1.8 pain:
> 
>> ruby tests/cimi/spec/cimi/model/machine_spec.rb
>         tests/cimi/spec/cimi/model/machine_spec.rb:17:in `require': no such file to load -- minitest/autorun (LoadError)
>                from tests/cimi/spec/cimi/model/machine_spec.rb:17
> 
> and
> 
>> ruby -rubygems tests/cimi/spec/cimi/model/machine_spec.rb 
>        tests/cimi/spec/cimi/model/machine_spec.rb:17: undefined method `require_relative' for main:Object (NoMethodError)
> 
> and
> 
>> bundle eexec ruby tests/cimi/spec/cimi/model/machine_spec.rb
>         tests/cimi/spec/cimi/model/machine_spec.rb:17: undefined method `require_relative' for main:Object (NoMethodError)
> 
> as well as
> 
>> rake test:cimi:models
>        ./tests/cimi/spec/spec_helper.rb:17:in `require': no such file to load -- minitest/autorun (LoadError)
>                from ./tests/cimi/spec/spec_helper.rb:17
>        rake aborted!
>        Command failed with status (1): [/usr/bin/ruby -I"lib" -r./tests/cimi/spec/...]
> 
>        Tasks: TOP => test:cimi:models
>        (See full trace by running task with --trace)
> 
> But the code looks great ;)
> 
> David
> 
> 


Re: Initial support for CIMI minitest (rev 2)

Posted by David Lutterkort <lu...@redhat.com>.
On Tue, 2012-07-31 at 14:33 +0200, mfojtik@redhat.com wrote:
> This patch will add support for CIMI collection testing.

More ruby 1.8 pain:

        >ruby tests/cimi/spec/cimi/model/machine_spec.rb
         tests/cimi/spec/cimi/model/machine_spec.rb:17:in `require': no such file to load -- minitest/autorun (LoadError)
                from tests/cimi/spec/cimi/model/machine_spec.rb:17
        
and

        >ruby -rubygems tests/cimi/spec/cimi/model/machine_spec.rb 
        tests/cimi/spec/cimi/model/machine_spec.rb:17: undefined method `require_relative' for main:Object (NoMethodError)

and

        >bundle eexec ruby tests/cimi/spec/cimi/model/machine_spec.rb
         tests/cimi/spec/cimi/model/machine_spec.rb:17: undefined method `require_relative' for main:Object (NoMethodError)
        
as well as

        >rake test:cimi:models
        ./tests/cimi/spec/spec_helper.rb:17:in `require': no such file to load -- minitest/autorun (LoadError)
                from ./tests/cimi/spec/spec_helper.rb:17
        rake aborted!
        Command failed with status (1): [/usr/bin/ruby -I"lib" -r./tests/cimi/spec/...]
        
        Tasks: TOP => test:cimi:models
        (See full trace by running task with --trace)
        
But the code looks great ;)

David



[PATCH core 5/5] CIMI: Added initial support for CIMI collections tests

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


Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/Rakefile                                    |    2 +
 .../cimi/collections/cloud_entry_point_test.rb     |   43 ++++++++++++++++++++
 server/tests/cimi/collections/common.rb            |   22 ++++++++++
 .../tests/cimi/collections/machine_images_test.rb  |   42 +++++++++++++++++++
 server/tests/cimi/collections/machines_test.rb     |   39 ++++++++++++++++++
 5 files changed, 148 insertions(+)
 create mode 100644 server/tests/cimi/collections/cloud_entry_point_test.rb
 create mode 100644 server/tests/cimi/collections/common.rb
 create mode 100644 server/tests/cimi/collections/machine_images_test.rb
 create mode 100644 server/tests/cimi/collections/machines_test.rb

diff --git a/server/Rakefile b/server/Rakefile
index ec3984b..184fe6c 100644
--- a/server/Rakefile
+++ b/server/Rakefile
@@ -185,11 +185,13 @@ namespace :test do
   namespace :cimi do
     Rake::TestTask.new(:models) do |t|
       t.ruby_opts << '-r./tests/cimi/spec/spec_helper.rb'    # Load SimpleCov when COVERAGE=1 is set
+      t.ruby_opts << '-r./tests/test_helper.rb'    # Load SimpleCov when COVERAGE=1 is set
       unless RUBY_VERSION < '1.9.0'
         t.loader = :testrb
       end
       t.test_files = FileList[
         'tests/cimi/spec/cimi/model/*spec.rb',        # CIMI frontend serialization API tests
+        'tests/cimi/collections/*test.rb',        # CIMI frontend API tests
       ]
     end
   end
diff --git a/server/tests/cimi/collections/cloud_entry_point_test.rb b/server/tests/cimi/collections/cloud_entry_point_test.rb
new file mode 100644
index 0000000..7603a3d
--- /dev/null
+++ b/server/tests/cimi/collections/cloud_entry_point_test.rb
@@ -0,0 +1,43 @@
+require 'minitest/autorun'
+require_relative './common.rb'
+
+describe CIMI::Collections::CloudEntryPoint do
+
+  before do
+    def app; CIMI::API; end
+    @collection = CIMI::Collections.collection(:cloudEntryPoint)
+  end
+
+  it 'has index operation' do
+    @collection.operation(:index).must_equal Sinatra::Rabbit::CloudentrypointCollection::IndexOperation
+  end
+
+  it 'set the CIMI-Version header' do
+    get root_url
+    headers['X-CIMI-Specification-Version'].wont_be_nil
+    headers['X-CIMI-Specification-Version'].must_equal '1.0.0'
+  end
+
+  it 'advertise CIMI collections in XML format' do
+    get root_url + '/cloudEntryPoint'
+    xml.root.name.must_equal 'CloudEntryPoint'
+    (xml.root/'description').first.text.wont_be_empty
+    (xml.root/'id').first.text.wont_be_empty
+  end
+
+  it 'advertise CIMI collections in JSON format' do
+    get root_url + '/cloudEntryPoint?format=json'
+    json.wont_be_empty
+    json['description'].wont_be_empty
+    json['id'].wont_be_empty
+  end
+
+  it 'allow to force authentication using force_auth parameter in URI' do
+    get root_url + '/cloudEntryPoint?force_auth=1'
+    status.must_equal 401
+    authorize 'mockuser', 'mockpassword'
+    get root_url + '/cloudEntryPoint?force_auth=1'
+    status.must_equal 200
+  end
+
+end
diff --git a/server/tests/cimi/collections/common.rb b/server/tests/cimi/collections/common.rb
new file mode 100644
index 0000000..f24e7b1
--- /dev/null
+++ b/server/tests/cimi/collections/common.rb
@@ -0,0 +1,22 @@
+require_relative File.join('..', '..', '..', 'lib', 'deltacloud_rack.rb')
+
+# Set the default driver used for server API tests
+#
+ENV['API_DRIVER'] = 'mock'
+
+# Setup Deltacloud::API Sinatra instance
+#
+unless Deltacloud::config[:cimi]
+  Deltacloud::configure(:cimi) do |server|
+    server.root_url '/cimi'
+    server.version '1.0.0'
+    server.klass 'CIMI::API'
+    server.logger Rack::DeltacloudLogger.setup(ENV['API_LOG'], ENV['API_VERBOSE'])
+  end
+
+  Deltacloud.require_frontend!(:cimi)
+end
+
+def root_url(url=''); Deltacloud.config[:cimi].root_url + url; end
+def formats; [ 'application/xml', 'application/json' ]; end
+def json; JSON::parse(response_body); end
diff --git a/server/tests/cimi/collections/machine_images_test.rb b/server/tests/cimi/collections/machine_images_test.rb
new file mode 100644
index 0000000..f8df8b5
--- /dev/null
+++ b/server/tests/cimi/collections/machine_images_test.rb
@@ -0,0 +1,42 @@
+require 'minitest/autorun'
+require_relative './common.rb'
+
+describe CIMI::Collections::MachineImages do
+
+  before do
+    def app; CIMI::API; end
+    authorize 'mockuser', 'mockpassword'
+    @collection = CIMI::Collections.collection(:machine_images)
+  end
+
+  it 'has index operation' do
+    @collection.operation(:index).must_equal Sinatra::Rabbit::MachineImagesCollection::IndexOperation
+  end
+
+  it 'has show operation' do
+    @collection.operation(:show).must_equal Sinatra::Rabbit::MachineImagesCollection::ShowOperation
+  end
+
+  it 'returns list of images in various formats with index operation' do
+    formats.each do |format|
+      header 'Accept', format
+      get root_url + '/machine_images'
+      status.must_equal 200
+    end
+  end
+
+  it 'should allow to retrieve the single image' do
+    get root_url '/machine_images/img1'
+    status.must_equal 200
+    xml.root.name.must_equal 'MachineImage'
+  end
+
+  it 'should allow to filter using CIMISelect' do
+    get root_url '/machine_images?CIMISelect=description'
+    status.must_equal 200
+    xml.root.name.must_equal 'MachineImageCollection'
+    (xml/'description').wont_be_empty
+    (xml/'id').must_be_empty
+  end
+
+end
diff --git a/server/tests/cimi/collections/machines_test.rb b/server/tests/cimi/collections/machines_test.rb
new file mode 100644
index 0000000..aa3360e
--- /dev/null
+++ b/server/tests/cimi/collections/machines_test.rb
@@ -0,0 +1,39 @@
+require 'minitest/autorun'
+require_relative './common.rb'
+
+describe CIMI::Collections::Machines do
+
+  before do
+    def app; CIMI::API; end
+    authorize 'mockuser', 'mockpassword'
+    @collection = CIMI::Collections.collection(:machines)
+  end
+
+  it 'has index operation' do
+    @collection.operation(:index).must_equal Sinatra::Rabbit::MachinesCollection::IndexOperation
+  end
+
+  it 'has show operation' do
+    @collection.operation(:show).must_equal Sinatra::Rabbit::MachinesCollection::ShowOperation
+  end
+
+  it 'returns list of machines in various formats with index operation' do
+    formats.each do |format|
+      header 'Accept', format
+      get root_url + '/machines'
+      status.must_equal 200
+    end
+  end
+
+  it 'should allow to retrieve the single machine' do
+    get root_url '/machines/inst1'
+    status.must_equal 200
+    xml.root.name.must_equal 'Machine'
+  end
+
+  it 'should not return non-existing machine' do
+    get root_url '/machines/unknown-machine'
+    status.must_equal 404
+  end
+
+end
-- 
1.7.10.2


[PATCH core 1/5] CIMI: Converted rspec tests to minitest

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

* Added new Rake task: rake test:cimi:models
* All tests can be now run standalone

Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/Rakefile                                    |   16 +-
 server/spec/cimi/data/machine.json                 |   43 ----
 server/spec/cimi/data/machine.xml                  |   33 ---
 server/spec/cimi/data/machine_admin.json           |   14 --
 server/spec/cimi/data/machine_admin.xml            |   10 -
 server/spec/cimi/data/machine_configuration.json   |   17 --
 server/spec/cimi/data/machine_configuration.xml    |   14 --
 server/spec/cimi/data/machine_image.json           |   14 --
 server/spec/cimi/data/machine_image.xml            |   11 -
 server/spec/cimi/data/machine_template.json        |   30 ---
 server/spec/cimi/data/machine_template.xml         |   24 --
 server/spec/cimi/data/volume.json                  |   16 --
 server/spec/cimi/data/volume.xml                   |   12 -
 server/spec/cimi/data/volume_configuration.json    |   16 --
 server/spec/cimi/data/volume_configuration.xml     |   12 -
 server/spec/cimi/data/volume_image.json            |   14 --
 server/spec/cimi/data/volume_image.xml             |   10 -
 server/spec/cimi/data/volume_template.json         |   14 --
 server/spec/cimi/data/volume_template.xml          |   10 -
 server/spec/cimi/model/machine_admin_spec.rb       |   28 ---
 .../spec/cimi/model/machine_configuration_spec.rb  |   27 ---
 server/spec/cimi/model/machine_image_spec.rb       |   29 ---
 server/spec/cimi/model/machine_spec.rb             |   27 ---
 server/spec/cimi/model/machine_template_spec.rb    |   27 ---
 server/spec/cimi/model/schema_spec.rb              |  241 -------------------
 .../spec/cimi/model/volume_configuration_spec.rb   |   30 ---
 server/spec/cimi/model/volume_image_spec.rb        |   29 ---
 server/spec/cimi/model/volume_spec.rb              |   28 ---
 server/spec/cimi/model/volume_template_spec.rb     |   28 ---
 server/spec/spec_helper.rb                         |  133 -----------
 server/tests/cimi/spec/cimi/data/machine.json      |   43 ++++
 server/tests/cimi/spec/cimi/data/machine.xml       |   33 +++
 .../tests/cimi/spec/cimi/data/machine_admin.json   |   14 ++
 server/tests/cimi/spec/cimi/data/machine_admin.xml |   10 +
 .../cimi/spec/cimi/data/machine_configuration.json |   17 ++
 .../cimi/spec/cimi/data/machine_configuration.xml  |   14 ++
 .../tests/cimi/spec/cimi/data/machine_image.json   |   14 ++
 server/tests/cimi/spec/cimi/data/machine_image.xml |   11 +
 .../cimi/spec/cimi/data/machine_template.json      |   30 +++
 .../tests/cimi/spec/cimi/data/machine_template.xml |   24 ++
 server/tests/cimi/spec/cimi/data/volume.json       |   16 ++
 server/tests/cimi/spec/cimi/data/volume.xml        |   12 +
 .../cimi/spec/cimi/data/volume_configuration.json  |   16 ++
 .../cimi/spec/cimi/data/volume_configuration.xml   |   12 +
 server/tests/cimi/spec/cimi/data/volume_image.json |   14 ++
 server/tests/cimi/spec/cimi/data/volume_image.xml  |   10 +
 .../tests/cimi/spec/cimi/data/volume_template.json |   14 ++
 .../tests/cimi/spec/cimi/data/volume_template.xml  |   10 +
 .../cimi/spec/cimi/model/machine_admin_spec.rb     |   32 +++
 .../spec/cimi/model/machine_configuration_spec.rb  |   30 +++
 .../cimi/spec/cimi/model/machine_image_spec.rb     |   31 +++
 server/tests/cimi/spec/cimi/model/machine_spec.rb  |   30 +++
 .../cimi/spec/cimi/model/machine_template_spec.rb  |   30 +++
 server/tests/cimi/spec/cimi/model/schema_spec.rb   |  243 ++++++++++++++++++++
 .../spec/cimi/model/volume_configuration_spec.rb   |   32 +++
 .../cimi/spec/cimi/model/volume_image_spec.rb      |   31 +++
 server/tests/cimi/spec/cimi/model/volume_spec.rb   |   30 +++
 .../cimi/spec/cimi/model/volume_template_spec.rb   |   30 +++
 server/tests/cimi/spec/spec_helper.rb              |  127 ++++++++++
 59 files changed, 974 insertions(+), 943 deletions(-)
 delete mode 100644 server/spec/cimi/data/machine.json
 delete mode 100644 server/spec/cimi/data/machine.xml
 delete mode 100644 server/spec/cimi/data/machine_admin.json
 delete mode 100644 server/spec/cimi/data/machine_admin.xml
 delete mode 100644 server/spec/cimi/data/machine_configuration.json
 delete mode 100644 server/spec/cimi/data/machine_configuration.xml
 delete mode 100644 server/spec/cimi/data/machine_image.json
 delete mode 100644 server/spec/cimi/data/machine_image.xml
 delete mode 100644 server/spec/cimi/data/machine_template.json
 delete mode 100644 server/spec/cimi/data/machine_template.xml
 delete mode 100644 server/spec/cimi/data/volume.json
 delete mode 100644 server/spec/cimi/data/volume.xml
 delete mode 100644 server/spec/cimi/data/volume_configuration.json
 delete mode 100644 server/spec/cimi/data/volume_configuration.xml
 delete mode 100644 server/spec/cimi/data/volume_image.json
 delete mode 100644 server/spec/cimi/data/volume_image.xml
 delete mode 100644 server/spec/cimi/data/volume_template.json
 delete mode 100644 server/spec/cimi/data/volume_template.xml
 delete mode 100644 server/spec/cimi/model/machine_admin_spec.rb
 delete mode 100644 server/spec/cimi/model/machine_configuration_spec.rb
 delete mode 100644 server/spec/cimi/model/machine_image_spec.rb
 delete mode 100644 server/spec/cimi/model/machine_spec.rb
 delete mode 100644 server/spec/cimi/model/machine_template_spec.rb
 delete mode 100644 server/spec/cimi/model/schema_spec.rb
 delete mode 100644 server/spec/cimi/model/volume_configuration_spec.rb
 delete mode 100644 server/spec/cimi/model/volume_image_spec.rb
 delete mode 100644 server/spec/cimi/model/volume_spec.rb
 delete mode 100644 server/spec/cimi/model/volume_template_spec.rb
 delete mode 100644 server/spec/spec_helper.rb
 create mode 100644 server/tests/cimi/spec/cimi/data/machine.json
 create mode 100644 server/tests/cimi/spec/cimi/data/machine.xml
 create mode 100644 server/tests/cimi/spec/cimi/data/machine_admin.json
 create mode 100644 server/tests/cimi/spec/cimi/data/machine_admin.xml
 create mode 100644 server/tests/cimi/spec/cimi/data/machine_configuration.json
 create mode 100644 server/tests/cimi/spec/cimi/data/machine_configuration.xml
 create mode 100644 server/tests/cimi/spec/cimi/data/machine_image.json
 create mode 100644 server/tests/cimi/spec/cimi/data/machine_image.xml
 create mode 100644 server/tests/cimi/spec/cimi/data/machine_template.json
 create mode 100644 server/tests/cimi/spec/cimi/data/machine_template.xml
 create mode 100644 server/tests/cimi/spec/cimi/data/volume.json
 create mode 100644 server/tests/cimi/spec/cimi/data/volume.xml
 create mode 100644 server/tests/cimi/spec/cimi/data/volume_configuration.json
 create mode 100644 server/tests/cimi/spec/cimi/data/volume_configuration.xml
 create mode 100644 server/tests/cimi/spec/cimi/data/volume_image.json
 create mode 100644 server/tests/cimi/spec/cimi/data/volume_image.xml
 create mode 100644 server/tests/cimi/spec/cimi/data/volume_template.json
 create mode 100644 server/tests/cimi/spec/cimi/data/volume_template.xml
 create mode 100644 server/tests/cimi/spec/cimi/model/machine_admin_spec.rb
 create mode 100644 server/tests/cimi/spec/cimi/model/machine_configuration_spec.rb
 create mode 100644 server/tests/cimi/spec/cimi/model/machine_image_spec.rb
 create mode 100644 server/tests/cimi/spec/cimi/model/machine_spec.rb
 create mode 100644 server/tests/cimi/spec/cimi/model/machine_template_spec.rb
 create mode 100644 server/tests/cimi/spec/cimi/model/schema_spec.rb
 create mode 100644 server/tests/cimi/spec/cimi/model/volume_configuration_spec.rb
 create mode 100644 server/tests/cimi/spec/cimi/model/volume_image_spec.rb
 create mode 100644 server/tests/cimi/spec/cimi/model/volume_spec.rb
 create mode 100644 server/tests/cimi/spec/cimi/model/volume_template_spec.rb
 create mode 100644 server/tests/cimi/spec/spec_helper.rb

diff --git a/server/Rakefile b/server/Rakefile
index 80589c4..ec3984b 100644
--- a/server/Rakefile
+++ b/server/Rakefile
@@ -127,6 +127,8 @@ task :test do
   Rake::Task["mock:fixtures:reset"].invoke
   puts "\n[ \033[1;37;mrake test:ec2\33[0m ]\n"
   Rake::Task["test:ec2"].invoke
+  puts "\n[ \033[1;37;mrake test:cimi:models\33[0m ]\n"
+  Rake::Task["test:cimi:models"].invoke
   DRIVERS.each do |driver|
     puts "\n[ \033[1;37;mrake drivers:#{driver}\33[0m ]\n"
     Rake::Task["test:drivers:#{driver}"].invoke
@@ -180,6 +182,16 @@ namespace :test do
     ]
   end
 
-end
-
+  namespace :cimi do
+    Rake::TestTask.new(:models) do |t|
+      t.ruby_opts << '-r./tests/cimi/spec/spec_helper.rb'    # Load SimpleCov when COVERAGE=1 is set
+      unless RUBY_VERSION < '1.9.0'
+        t.loader = :testrb
+      end
+      t.test_files = FileList[
+        'tests/cimi/spec/cimi/model/*spec.rb',        # CIMI frontend serialization API tests
+      ]
+    end
+  end
 
+end
diff --git a/server/spec/cimi/data/machine.json b/server/spec/cimi/data/machine.json
deleted file mode 100644
index 728ff5c..0000000
--- a/server/spec/cimi/data/machine.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{ "id": "http://cimi.example.org/machines/1",
-  "name": "machine1",
-  "description": "Machine one description",
-  "created": "2011-11-21",
-  "properties": { "owner_id": "mockuser" },
-  "state": "STARTED",
-  "cpu": "4",
-  "memory": { "quantity": "1", "units": "gibibyte" },
-  "disks" : [
-    { "capacity": { "quantity": "1", "units": "terabyte" } }
-  ],
-  "volumes": [{
-    "href": "http://cimi.example.org/volumes/1",
-    "attachmentPoint": "/dev/sda",
-    "protocol": "nfs"
-  }],
-  "networkInterfaces": [{
-    "vsp": { "href": "http://cimi.example.org/vsps/2" },
-    "hostname": "machine1.cimi.example.org",
-    "macAddress": "00:11:22:33:44:56",
-    "state": "UP",
-    "protocol": "TCP",
-    "allocation": "static",
-    "address": "192.168.0.18",
-    "defaultGateway": "192.168.0.1",
-    "dns": "192.168.0.1",
-    "maxTransmissionUnit": "1500"
-  }],
-  "meters": [
-    { "href": "http://cimi.example.org/meters/1" }
-  ],
-  "eventLog": { "href": "http://cimi.example.org/event_logs/1" },
-  "operations": [
-    { "rel": "edit",
-      "href": "http://cimi.example.org/machines/1/edit" },
-    { "rel": "delete",
-      "href": "http://cimi.example.org/machines/1/delete" },
-    { "rel": "http://www.dmtf.org/cimi/action/stop",
-      "href": "http://cimi.example.org/machines/1/stop" },
-    { "rel": "http://www.dmtf.org/cimi/action/restart",
-      "href": "http://cimi.example.org/machines/1/restart" }
-  ]
-}
diff --git a/server/spec/cimi/data/machine.xml b/server/spec/cimi/data/machine.xml
deleted file mode 100644
index 37b6582..0000000
--- a/server/spec/cimi/data/machine.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<Machine xmlns="http://www.dmtf.org/cimi">
-  <id>http://cimi.example.org/machines/1</id>
-  <name>machine1</name>
-  <description>Machine one description</description>
-  <created>2011-11-21</created>
-  <property name="owner_id">mockuser</property>
-  <cpu>4</cpu>
-  <memory quantity="1" units="gibibyte"/>
-  <state>STARTED</state>
-  <disk>
-    <capacity quantity="1" units="terabyte"/>
-  </disk>
-  <volume href="http://cimi.example.org/volumes/1"
-          attachmentPoint="/dev/sda" protocol="nfs" />
-  <networkInterface>
-    <vsp href="http://cimi.example.org/vsps/2"/>
-    <hostname>machine1.cimi.example.org</hostname>
-    <macAddress>00:11:22:33:44:56</macAddress>
-    <state>UP</state>
-    <protocol>TCP</protocol>
-    <allocation>static</allocation>
-    <address>192.168.0.18</address>
-    <defaultGateway>192.168.0.1</defaultGateway>
-    <dns>192.168.0.1</dns>
-    <maxTransmissionUnit>1500</maxTransmissionUnit>
-  </networkInterface>
-  <meter href="http://cimi.example.org/meters/1"/>
-  <eventLog href="http://cimi.example.org/event_logs/1"/>
-  <operation rel="edit" href="http://cimi.example.org/machines/1/edit"/>
-  <operation rel="delete" href="http://cimi.example.org/machines/1/delete"/>
-  <operation rel="http://www.dmtf.org/cimi/action/stop" href="http://cimi.example.org/machines/1/stop"/>
-  <operation rel="http://www.dmtf.org/cimi/action/restart" href="http://cimi.example.org/machines/1/restart"/>
-</Machine>
diff --git a/server/spec/cimi/data/machine_admin.json b/server/spec/cimi/data/machine_admin.json
deleted file mode 100644
index 35ab04b..0000000
--- a/server/spec/cimi/data/machine_admin.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "id": "http://cimi.example.org/machine_admins/1",
-  "name": "credentials1",
-  "description": "Machine Admin One",
-  "created": "2011-11-16",
-  "username": "mockuser",
-  "password": "mockpassword",
-  "operations": [
-  { "rel": "edit",
-    "href": "http://cimi.example.org/machine_admins/1/edit" },
-  { "rel": "delete",
-    "href": "http://cimi.example.org/machine_admins/1/delete" }
-  ]
-}
diff --git a/server/spec/cimi/data/machine_admin.xml b/server/spec/cimi/data/machine_admin.xml
deleted file mode 100644
index 1f395c3..0000000
--- a/server/spec/cimi/data/machine_admin.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<MachineAdmin xmlns="http://www.dmtf.org/cimi">
-  <id>http://cimi.example.org/machine_admins/1</id>
-  <name>credentials1</name>
-  <description>Machine Admin One</description>
-  <created>2011-11-16</created>
-  <username>mockuser</username>
-  <password>mockpassword</password>
-  <operation rel="edit" href="http://cimi.example.org/machine_admins/1/edit"/>
-  <operation rel="delete" href="http://cimi.example.org/machine_admins/1/delete"/>
-</MachineAdmin>
diff --git a/server/spec/cimi/data/machine_configuration.json b/server/spec/cimi/data/machine_configuration.json
deleted file mode 100644
index 40c77ad..0000000
--- a/server/spec/cimi/data/machine_configuration.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "id": "http://cimi.example.org/machine_configurations/1",
-  "name": "MachineConfiguration1",
-  "description": "Example MachineConfiguration One",
-  "created": "2011-11-14",
-  "cpu": "2",
-  "memory" : { "quantity": "1", "units": "gigabyte" },
-  "disks" : [
-    { "capacity": { "quantity": "1", "units": "terabyte" } }
-  ],
-  "properties": { "architecture": "i386" },
-  "operations": [
-    { "rel": "edit",
-      "href": "http://cimi.example.org/machine_configurations/1/edit" },
-    { "rel": "delete",
-      "href": "http://cimi.example.org/machine_configurations/1/delete" }]
-}
diff --git a/server/spec/cimi/data/machine_configuration.xml b/server/spec/cimi/data/machine_configuration.xml
deleted file mode 100644
index b3810a3..0000000
--- a/server/spec/cimi/data/machine_configuration.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<MachineConfiguration xmlns="http://www.dmtf.org/cimi">
-  <id>http://cimi.example.org/machine_configurations/1</id>
-  <name>MachineConfiguration1</name>
-  <description>Example MachineConfiguration One</description>
-  <created>2011-11-14</created>
-  <property name="architecture">i386</property>
-  <cpu>2</cpu>
-  <memory quantity="1" units="gigabyte"/>
-  <disk>
-    <capacity quantity="1" units="terabyte"/>
-  </disk>
-  <operation rel="edit" href="http://cimi.example.org/machine_configurations/1/edit"/>
-  <operation rel="delete" href="http://cimi.example.org/machine_configurations/1/delete"/>
-</MachineConfiguration>
diff --git a/server/spec/cimi/data/machine_image.json b/server/spec/cimi/data/machine_image.json
deleted file mode 100644
index cee0a52..0000000
--- a/server/spec/cimi/data/machine_image.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "id": "http://cimi.example.org/machine_images/1",
-  "name": "img1",
-  "description": "Machine Image One",
-  "created": "2011-11-14",
-  "imageLocation": { "href": "nfs://cimi.example.com/images/1.img" },
-  "properties": { "status": "BUILD", "locked": "true" },
-  "operations": [
-  { "rel": "edit",
-    "href": "http://cimi.example.org/machine_images/1/edit" },
-  { "rel": "delete",
-    "href": "http://cimi.example.org/machine_images/1/delete" }
-  ]
-}
diff --git a/server/spec/cimi/data/machine_image.xml b/server/spec/cimi/data/machine_image.xml
deleted file mode 100644
index e1d4452..0000000
--- a/server/spec/cimi/data/machine_image.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<MachineImage xmlns="http://www.dmtf.org/cimi">
-  <id>http://cimi.example.org/machine_images/1</id>
-  <name>img1</name>
-  <description>Machine Image One</description>
-  <created>2011-11-14</created>
-  <property name="status">BUILD</property>
-  <property name="locked">true</property>
-  <imageLocation href="nfs://cimi.example.com/images/1.img"/>
-  <operation rel="edit" href="http://cimi.example.org/machine_images/1/edit"/>
-  <operation rel="delete" href="http://cimi.example.org/machine_images/1/delete"/>
-</MachineImage>
diff --git a/server/spec/cimi/data/machine_template.json b/server/spec/cimi/data/machine_template.json
deleted file mode 100644
index 1be6592..0000000
--- a/server/spec/cimi/data/machine_template.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "id": "http://cimi.example.org/machine_templates/1",
-  "name": "My First Template",
-  "description": "A template for testing",
-  "created": "2011-11-01",
-  "machineConfig": { "href": "http://cimi.example.org/machine_configs/1" },
-  "machineImage": { "href": "http://cimi.example.org/machine_images/1" },
-  "volumes": [{
-    "href": "http://cimi.example.org/volumes/1",
-    "attachmentPoint": "/dev/sda",
-    "protocol": "nfs"
-  }],
-  "networkInterfaces": [{
-    "vsp": { "href": "http://cimi.example.org/vsps/1" },
-    "hostname": "host.cimi.example.org",
-    "macAddress": "00:11:22:33:44:55",
-    "state": "UP",
-    "protocol": "TCP",
-    "allocation": "static",
-    "address": "192.168.0.17",
-    "defaultGateway": "192.168.0.1",
-    "dns": "192.168.0.1",
-    "maxTransmissionUnit": "1500"
-  }],
-  "operations": [
-    { "rel": "edit",
-      "href": "http://cimi.example.org/machine_templates/1/edit" },
-    { "rel": "delete",
-      "href": "http://cimi.example.org/machine_templates/1/delete" }]
-}
diff --git a/server/spec/cimi/data/machine_template.xml b/server/spec/cimi/data/machine_template.xml
deleted file mode 100644
index 1386460..0000000
--- a/server/spec/cimi/data/machine_template.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<MachineTemplate xmlns="http://www.dmtf.org/cimi">
-  <id>http://cimi.example.org/machine_templates/1</id>
-  <name>My First Template</name>
-  <description>A template for testing</description>
-  <created>2011-11-01</created>
-  <machineConfig href="http://cimi.example.org/machine_configs/1"/>
-  <machineImage href="http://cimi.example.org/machine_images/1"/>
-  <volume href="http://cimi.example.org/volumes/1"
-          attachmentPoint="/dev/sda" protocol="nfs" />
-  <networkInterface>
-    <vsp href="http://cimi.example.org/vsps/1"/>
-    <hostname>host.cimi.example.org</hostname>
-    <macAddress>00:11:22:33:44:55</macAddress>
-    <state>UP</state>
-    <protocol>TCP</protocol>
-    <allocation>static</allocation>
-    <address>192.168.0.17</address>
-    <defaultGateway>192.168.0.1</defaultGateway>
-    <dns>192.168.0.1</dns>
-    <maxTransmissionUnit>1500</maxTransmissionUnit>
-  </networkInterface>
-  <operation rel="edit" href="http://cimi.example.org/machine_templates/1/edit"/>
-  <operation rel="delete" href="http://cimi.example.org/machine_templates/1/delete"/>
-</MachineTemplate>
diff --git a/server/spec/cimi/data/volume.json b/server/spec/cimi/data/volume.json
deleted file mode 100644
index e8364dd..0000000
--- a/server/spec/cimi/data/volume.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "id": "http://cimi.example.org/volumes/1",
-  "name": "volume1",
-  "description": "Volume One",
-  "created": "2011-11-17",
-  "capacity": { "quantity": "10", "units": "gigabyte" },
-  "bootable": "false",
-  "supportsSnapshots": "false",
-  "guestInterface": "NFS",
-  "operations": [
-    { "rel": "edit",
-      "href": "http://cimi.example.org/volumes/1/edit" },
-    { "rel": "delete",
-      "href": "http://cimi.example.org/volumes/1/delete" }
-  ]
-}
diff --git a/server/spec/cimi/data/volume.xml b/server/spec/cimi/data/volume.xml
deleted file mode 100644
index 65e6e06..0000000
--- a/server/spec/cimi/data/volume.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<Volume xmlns="http://www.dmtf.org/cimi">
-  <id>http://cimi.example.org/volumes/1</id>
-  <name>volume1</name>
-  <description>Volume One</description>
-  <created>2011-11-17</created>
-  <capacity quantity="10" units="gigabyte"/>
-  <bootable>false</bootable>
-  <supportsSnapshots>false</supportsSnapshots>
-  <guestInterface>NFS</guestInterface>
-  <operation rel="edit" href="http://cimi.example.org/volumes/1/edit"/>
-  <operation rel="delete" href="http://cimi.example.org/volumes/1/delete"/>
-</Volume>
diff --git a/server/spec/cimi/data/volume_configuration.json b/server/spec/cimi/data/volume_configuration.json
deleted file mode 100644
index 817e20c..0000000
--- a/server/spec/cimi/data/volume_configuration.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "id": "http://cimi.example.org/volume_configurations/1",
-  "name": "volume_config_1",
-  "description": "Volume Configuration One",
-  "created": "2011-11-21",
-  "format": "ext3",
-  "capacity": { "quantity": "10", "units": "gigabyte" },
-  "supportsSnapshots": "false",
-  "guestInterface": "NFS",
-  "operations": [
-    { "rel": "edit",
-      "href": "http://cimi.example.org/volume_configurations/1/edit" },
-    { "rel": "delete",
-      "href": "http://cimi.example.org/volume_configurations/1/delete" }
-  ]
-}
diff --git a/server/spec/cimi/data/volume_configuration.xml b/server/spec/cimi/data/volume_configuration.xml
deleted file mode 100644
index 120e8a6..0000000
--- a/server/spec/cimi/data/volume_configuration.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<VolumeConfiguration xmlns="http://www.dmtf.org/cimi">
-  <id>http://cimi.example.org/volume_configurations/1</id>
-  <name>volume_config_1</name>
-  <description>Volume Configuration One</description>
-  <created>2011-11-21</created>
-  <capacity quantity="10" units="gigabyte"/>
-  <supportsSnapshots>false</supportsSnapshots>
-  <guestInterface>NFS</guestInterface>
-  <format>ext3</format>
-  <operation rel="edit" href="http://cimi.example.org/volume_configurations/1/edit"/>
-  <operation rel="delete" href="http://cimi.example.org/volume_configurations/1/delete"/>
-</VolumeConfiguration>
diff --git a/server/spec/cimi/data/volume_image.json b/server/spec/cimi/data/volume_image.json
deleted file mode 100644
index 8c6ac04..0000000
--- a/server/spec/cimi/data/volume_image.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "id": "http://cimi.example.org/volume_images/1",
-  "name": "volume_image_1",
-  "description": "Volume Image One",
-  "created": "2011-11-21",
-  "bootable": "false",
-  "imageLocation":{ "href": "nfs://cimi.example.com/volume_images/vol_image_1.img" },
-  "operations": [
-    { "rel": "edit",
-      "href": "http://cimi.example.org/volume_images/1/edit" },
-    { "rel": "delete",
-      "href": "http://cimi.example.org/volume_images/1/delete" }
-  ]
-}
diff --git a/server/spec/cimi/data/volume_image.xml b/server/spec/cimi/data/volume_image.xml
deleted file mode 100644
index c6ba575..0000000
--- a/server/spec/cimi/data/volume_image.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<VolumeImage xmlns="http://www.dmtf.org/cimi">
-  <id>http://cimi.example.org/volume_images/1</id>
-  <name>volume_image_1</name>
-  <description>Volume Image One</description>
-  <created>2011-11-21</created>
-  <bootable>false</bootable>
-  <imageLocation href="nfs://cimi.example.com/volume_images/vol_image_1.img"/>
-  <operation rel="edit" href="http://cimi.example.org/volume_images/1/edit"/>
-  <operation rel="delete" href="http://cimi.example.org/volume_images/1/delete"/>
-</VolumeImage>
diff --git a/server/spec/cimi/data/volume_template.json b/server/spec/cimi/data/volume_template.json
deleted file mode 100644
index bddb5dc..0000000
--- a/server/spec/cimi/data/volume_template.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "id": "http://cimi.example.org/volume_templates/1",
-  "name": "volume_template_1",
-  "description": "Volume Template One",
-  "created": "2011-11-21",
-  "volumeConfig":{ "href": "http://cimi.example.com/volume_configurations/1" },
-  "volumeImage":{ "href": "http://cimi.example.com/volume_images/1" },
-  "operations": [
-    { "rel": "edit",
-      "href": "http://cimi.example.org/volume_templates/1/edit" },
-    { "rel": "delete",
-      "href": "http://cimi.example.org/volume_templates/1/delete" }
-  ]
-}
diff --git a/server/spec/cimi/data/volume_template.xml b/server/spec/cimi/data/volume_template.xml
deleted file mode 100644
index 47113b5..0000000
--- a/server/spec/cimi/data/volume_template.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<VolumeTemplate xmlns="http://www.dmtf.org/cimi">
-  <id>http://cimi.example.org/volume_templates/1</id>
-  <name>volume_template_1</name>
-  <description>Volume Template One</description>
-  <created>2011-11-21</created>
-  <volumeImage href="http://cimi.example.com/volume_images/1"/>
-  <volumeConfig href="http://cimi.example.com/volume_configurations/1"/>
-  <operation rel="edit" href="http://cimi.example.org/volume_templates/1/edit"/>
-  <operation rel="delete" href="http://cimi.example.org/volume_templates/1/delete"/>
-</VolumeTemplate>
diff --git a/server/spec/cimi/model/machine_admin_spec.rb b/server/spec/cimi/model/machine_admin_spec.rb
deleted file mode 100644
index 036a66d..0000000
--- a/server/spec/cimi/model/machine_admin_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-describe "MachineAdmin model" do
-
-  before(:all) do
-    @xml = IO::read(File::join(DATA_DIR, "machine_admin.xml"))
-    @json = IO::read(File::join(DATA_DIR, "machine_admin.json"))
-  end
-
-  it "can be constructed from XML and JSON" do
-    should_properly_serialize_model CIMI::Model::MachineAdmin, @xml, @json
-  end
-
-end
diff --git a/server/spec/cimi/model/machine_configuration_spec.rb b/server/spec/cimi/model/machine_configuration_spec.rb
deleted file mode 100644
index 2a57269..0000000
--- a/server/spec/cimi/model/machine_configuration_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-describe "MachineConfiguration model" do
-
-  before(:all) do
-    @xml = IO::read(File::join(DATA_DIR, "machine_configuration.xml"))
-    @json = IO::read(File::join(DATA_DIR, "machine_configuration.json"))
-  end
-
-  it "can be constructed from XML and JSON" do
-    should_properly_serialize_model CIMI::Model::MachineConfiguration, @xml, @json
-  end
-
-end
diff --git a/server/spec/cimi/model/machine_image_spec.rb b/server/spec/cimi/model/machine_image_spec.rb
deleted file mode 100644
index f284952..0000000
--- a/server/spec/cimi/model/machine_image_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-describe "MachineImage model" do
-
-  before(:all) do
-    @xml = IO::read(File::join(DATA_DIR, "machine_image.xml"))
-    @json = IO::read(File::join(DATA_DIR, "machine_image.json"))
-  end
-
-  it "can be constructed from XML and JSON" do
-    should_properly_serialize_model CIMI::Model::MachineImage, @xml, @json
-  end
-
-
-end
diff --git a/server/spec/cimi/model/machine_spec.rb b/server/spec/cimi/model/machine_spec.rb
deleted file mode 100644
index 59b5d6f..0000000
--- a/server/spec/cimi/model/machine_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-describe "Machine model" do
-
-  before(:all) do
-    @xml = IO::read(File::join(DATA_DIR, "machine.xml"))
-    @json = IO::read(File::join(DATA_DIR, "machine.json"))
-  end
-
-  it "can be constructed from XML and JSON" do
-    should_properly_serialize_model CIMI::Model::Machine, @xml, @json
-  end
-
-end
diff --git a/server/spec/cimi/model/machine_template_spec.rb b/server/spec/cimi/model/machine_template_spec.rb
deleted file mode 100644
index 3ca2c9a..0000000
--- a/server/spec/cimi/model/machine_template_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-describe "MachineTemplate model" do
-
-  before(:all) do
-    @xml = IO::read(File::join(DATA_DIR, "machine_template.xml"))
-    @json = IO::read(File::join(DATA_DIR, "machine_template.json"))
-  end
-
-  it "can be constructed from XML and JSON" do
-    should_properly_serialize_model CIMI::Model::MachineTemplate, @xml, @json
-  end
-
-end
diff --git a/server/spec/cimi/model/schema_spec.rb b/server/spec/cimi/model/schema_spec.rb
deleted file mode 100644
index 9fd4338..0000000
--- a/server/spec/cimi/model/schema_spec.rb
+++ /dev/null
@@ -1,241 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-describe "Schema" do
-  before(:each) do
-    @schema = CIMI::Model::Schema.new
-  end
-
-  it "does not allow adding attributes after being used for conversion" do
-    @schema.scalar(:before)
-    @schema.from_json({})
-    lambda { @schema.scalar(:after) }.should raise_error
-  end
-
-  describe "scalars" do
-    before(:each) do
-      @schema.scalar(:attr)
-      @schema.text(:camel_hump)
-
-      @schema.attribute_names.should == [:attr, :camel_hump]
-    end
-
-    let :sample_xml do
-      parse_xml("<camelHump>bumpy</camelHump>", :keep_root => true)
-    end
-
-    it "should camel case attribute names for JSON" do
-      obj = @schema.from_json("camelHump" => "bumpy")
-      obj.should_not be_nil
-      obj[:camel_hump].should == "bumpy"
-
-      json = @schema.to_json(obj)
-      json.should == { "camelHump" => "bumpy" }
-    end
-
-    it "should camel case attribute names for XML" do
-      obj = @schema.from_xml(sample_xml)
-
-      obj.should_not be_nil
-      obj[:camel_hump].should == "bumpy"
-
-      xml = @schema.to_xml(obj)
-
-      xml.should == { "camelHump" => [{ "content" => "bumpy" }] }
-    end
-
-    it "should allow aliasing the XML and JSON name" do
-      @schema.scalar :aliased, :xml_name => :xml, :json_name => :json
-      obj = @schema.from_xml({"aliased" => "no", "xml" => "yes"}, {})
-      obj[:aliased].should == "yes"
-
-      obj = @schema.from_json({"aliased" => "no", "json" => "yes"}, {})
-      obj[:aliased].should == "yes"
-    end
-  end
-
-  describe "hrefs" do
-    before(:each) do
-      @schema.href(:meter)
-    end
-
-    it "should extract the href attribute from XML" do
-      xml = parse_xml("<meter href='http://example.org/'/>")
-
-      obj = @schema.from_xml(xml)
-      check obj
-      @schema.to_xml(obj).should == xml
-    end
-
-    it "should extract the href attribute from JSON" do
-      json = { "meter" =>  { "href" => "http://example.org/" } }
-
-      obj = @schema.from_json(json)
-      check obj
-      @schema.to_json(obj).should == json
-    end
-
-    def check(obj)
-      obj.should_not be_nil
-      obj[:meter].href.should == 'http://example.org/'
-    end
-  end
-
-  describe "structs" do
-    before(:each) do
-      @schema.struct(:struct, :content => :scalar) do
-        scalar   :href
-      end
-      @schema.attribute_names.should == [:struct]
-    end
-
-    let(:sample_json) do
-      { "struct" => { "scalar" => "v1", "href" => "http://example.org/" } }
-    end
-
-    let (:sample_xml) do
-      parse_xml("<struct href='http://example.org/'>v1</struct>")
-    end
-
-    let (:sample_xml_no_href) do
-      parse_xml("<struct>v1</struct>")
-    end
-
-    describe "JSON conversion" do
-      it "should convert empty hash" do
-        model = @schema.from_json({ })
-        check_empty_struct model
-        @schema.to_json(model).should == {}
-      end
-
-      it "should convert empty body" do
-        model = @schema.from_json({ "struct" => { } })
-        check_empty_struct model
-        @schema.to_json(model).should == {}
-      end
-
-      it "should convert values" do
-        model = @schema.from_json(sample_json)
-        check_struct model
-        @schema.to_json(model).should == sample_json
-      end
-    end
-
-    describe "XML conversion" do
-      it "should convert empty hash" do
-        model = @schema.from_xml({ })
-        check_empty_struct model
-        @schema.to_xml(model).should == {}
-      end
-
-      it "should convert empty body" do
-        model = @schema.from_json({ "struct" => { } })
-        check_empty_struct model
-        @schema.to_xml(model).should == {}
-      end
-
-      it "should convert values" do
-        model = @schema.from_xml(sample_xml)
-        check_struct model
-        @schema.to_xml(model).should == sample_xml
-      end
-
-      it "should handle missing attributes" do
-        model = @schema.from_xml(sample_xml_no_href)
-        check_struct model, :nil_href => true
-        @schema.to_xml(model).should == sample_xml_no_href
-      end
-    end
-
-    def check_struct(obj, opts = {})
-      obj.should_not be_nil
-      obj[:struct].should_not be_nil
-      obj[:struct].scalar.should == "v1"
-      if opts[:nil_href]
-        obj[:struct].href.should be_nil
-      else
-        obj[:struct].href.should == "http://example.org/"
-      end
-    end
-
-    def check_empty_struct(obj)
-      obj.should_not be_nil
-      obj[:struct].should_not be_nil
-      obj[:struct].scalar.should be_nil
-      obj[:struct].href.should be_nil
-    end
-  end
-
-  describe "arrays" do
-    before(:each) do
-      @schema.array(:structs, :content => :scalar) do
-        scalar :href
-      end
-    end
-
-    let(:sample_json) do
-      { "structs" => [{ "scalar" => "v1", "href" => "http://example.org/1" },
-                      { "scalar" => "v2", "href" => "http://example.org/2" }] }
-    end
-
-    let (:sample_xml) do
-      parse_xml("<wrapper>
-  <struct href='http://example.org/1'>v1</struct>
-  <struct href='http://example.org/2'>v2</struct>
-</wrapper>", :keep_root => false)
-    end
-
-    it "should convert missing array from JSON" do
-      obj = @schema.from_json({})
-
-      obj.should_not be_nil
-      obj[:structs].should == []
-      @schema.to_json(obj).should == {}
-    end
-
-    it "should convert empty array from JSON" do
-      obj = @schema.from_json("structs" => [])
-
-      obj.should_not be_nil
-      obj[:structs].should == []
-      @schema.to_json(obj).should == {}
-    end
-
-    it "should convert arrays from JSON" do
-      obj = @schema.from_json(sample_json)
-
-      check_structs(obj)
-      @schema.to_json(obj).should == sample_json
-    end
-
-    it "should convert arrays from XML" do
-      obj = @schema.from_xml(sample_xml)
-
-      check_structs(obj)
-      @schema.to_xml(obj).should == sample_xml
-    end
-
-    def check_structs(obj)
-      obj.should_not be_nil
-      obj[:structs].size.should == 2
-      obj[:structs][0].scalar.should == "v1"
-      obj[:structs][0].href.should == "http://example.org/1"
-      obj[:structs][1].scalar.should == "v2"
-      obj[:structs][1].href.should == "http://example.org/2"
-    end
-  end
-
-end
diff --git a/server/spec/cimi/model/volume_configuration_spec.rb b/server/spec/cimi/model/volume_configuration_spec.rb
deleted file mode 100644
index 15be8ff..0000000
--- a/server/spec/cimi/model/volume_configuration_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-describe "Volume Configuration model" do
-
-  before(:all) do
-    @xml = IO::read(File::join(DATA_DIR, "volume_configuration.xml"))
-    @json = IO::read(File::join(DATA_DIR, "volume_configuration.json"))
-  end
-
-  it "can be constructed from XML and JSON" do
-    should_properly_serialize_model CIMI::Model::VolumeConfiguration, @xml, @json
-  end
-
-
-end
diff --git a/server/spec/cimi/model/volume_image_spec.rb b/server/spec/cimi/model/volume_image_spec.rb
deleted file mode 100644
index 99abdad..0000000
--- a/server/spec/cimi/model/volume_image_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-describe "Volume Image model" do
-
-  before(:all) do
-    @xml = IO::read(File::join(DATA_DIR, "volume_image.xml"))
-    @json = IO::read(File::join(DATA_DIR, "volume_image.json"))
-  end
-
-  it "can be constructed from XML and JSON" do
-    should_properly_serialize_model CIMI::Model::VolumeImage, @xml, @json
-  end
-
-
-end
diff --git a/server/spec/cimi/model/volume_spec.rb b/server/spec/cimi/model/volume_spec.rb
deleted file mode 100644
index d208e15..0000000
--- a/server/spec/cimi/model/volume_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-describe "Volume model" do
-
-  before(:all) do
-    @xml = IO::read(File::join(DATA_DIR, "volume.xml"))
-    @json = IO::read(File::join(DATA_DIR, "volume.json"))
-  end
-
-  it "can be constructed from XML and JSON" do
-    should_properly_serialize_model CIMI::Model::Volume, @xml, @json
-  end
-
-end
diff --git a/server/spec/cimi/model/volume_template_spec.rb b/server/spec/cimi/model/volume_template_spec.rb
deleted file mode 100644
index 12849e2..0000000
--- a/server/spec/cimi/model/volume_template_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-describe "Volume Template model" do
-
-  before(:all) do
-    @xml = IO::read(File::join(DATA_DIR, "volume_template.xml"))
-    @json = IO::read(File::join(DATA_DIR, "volume_template.json"))
-  end
-
-  it "can be constructed from XML and JSON" do
-    should_properly_serialize_model CIMI::Model::VolumeTemplate, @xml, @json
-  end
-
-end
diff --git a/server/spec/spec_helper.rb b/server/spec/spec_helper.rb
deleted file mode 100644
index 643873e..0000000
--- a/server/spec/spec_helper.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-require 'rubygems'
-require 'pp'
-require 'rspec/core'
-require 'xmlsimple'
-require 'require_relative'
-
-require_relative '../lib/deltacloud/core_ext.rb'
-require_relative '../lib/cimi/models.rb'
-
-DATA_DIR = File::join(File::expand_path(File::dirname(__FILE__)), 'cimi', 'data')
-
-def parse_xml(xml, opts = {})
-  opts[:force_content] = true
-  opts[:keep_root] = true unless opts.has_key?(:keep_root)
-  XmlSimple.xml_in(xml, opts)
-end
-
-class HashCmp
-  def initialize(exp, act)
-    @exp = exp
-    @act = act
-    @io = StringIO.new
-  end
-
-  def match?
-    @equal = true
-    compare_values(@exp, @act, [])
-    @equal
-  end
-
-  def errors
-    @io.string
-  end
-
-  private
-  def compare_values(exp, act, path)
-    if exp.is_a?(String)
-      mismatch("entries differ", exp, act, path) unless exp == act
-    elsif exp.is_a?(Array)
-      mismatch("expected array", exp, act, path) unless act.is_a?(Array)
-      unless act.size == exp.size
-        mismatch("different array lengths", exp, act, path)
-      end
-      name = path.pop
-      0.upto(exp.size-1) do |i|
-        compare_values(exp[i], act[i], path + [ "#{name}[#{i}]" ])
-      end
-    elsif exp.is_a?(Hash)
-      unless act.is_a?(Hash)
-        mismatch("expected Hash", exp, act, path)
-        return
-      end
-      unless (missing = exp.keys - act.keys).empty?
-        error "Missing key(s) at /#{path.join("/")}: #{missing.inspect}"
-      end
-      unless (excess = act.keys - exp.keys).empty?
-        error "Excess key(s) at /#{path.join("/")}: #{excess.inspect}"
-      end
-      (exp.keys - missing - excess).each do |k|
-        compare_values(exp[k], act[k], path + [ k ])
-      end
-    end
-  end
-
-  def mismatch(msg, exp, act, path)
-    error "#{msg}[#{fmt(path)}]: #{exp.inspect} != #{act.inspect}"
-  end
-
-  def error(msg)
-    @equal = false
-    @io.puts msg
-  end
-
-  def fmt(path)
-    "/#{path.join("/")}"
-  end
-end
-
-def should_properly_serialize_model(model_class, xml, json)
-  # Roundtrip in same format
-  model_class.from_xml(xml).should serialize_to xml, :fmt => :xml
-  model_class.from_json(json).should serialize_to json, :fmt => :json
-  # Roundtrip crossing format
-  model_class.from_xml(xml).should serialize_to json, :fmt => :json
-  model_class.from_json(json).should serialize_to xml, :fmt => :xml
-end
-
-RSpec::Matchers.define :serialize_to do |exp, opts|
-  match do |act|
-    matcher(exp, act, opts[:fmt]).match?
-  end
-
-  failure_message_for_should do |act|
-    m = matcher(exp, act, opts[:fmt])
-    m.match?
-    "#{opts[:fmt].to_s.upcase} documents do not match\n" + m.errors
-  end
-
-  def matcher(exp, act, fmt)
-    raise "missing format; use :fmt => [:xml || :json]" if fmt.nil?
-    exp, act = [exp, act].map { |x| convert(x, fmt) }
-    HashCmp.new(exp, act)
-  end
-
-  def convert(x, fmt)
-    if fmt == :json
-      x = x.to_json if x.is_a?(CIMI::Model::Base)
-      x = JSON.parse(x) if x.is_a?(String)
-    elsif fmt == :xml
-      x = x.to_xml if x.is_a?(CIMI::Model::Base)
-      x = parse_xml(x)  if x.is_a?(String)
-    else
-      raise "Invalid format #{fmt}"
-    end
-    x
-  end
-end
diff --git a/server/tests/cimi/spec/cimi/data/machine.json b/server/tests/cimi/spec/cimi/data/machine.json
new file mode 100644
index 0000000..728ff5c
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine.json
@@ -0,0 +1,43 @@
+{ "id": "http://cimi.example.org/machines/1",
+  "name": "machine1",
+  "description": "Machine one description",
+  "created": "2011-11-21",
+  "properties": { "owner_id": "mockuser" },
+  "state": "STARTED",
+  "cpu": "4",
+  "memory": { "quantity": "1", "units": "gibibyte" },
+  "disks" : [
+    { "capacity": { "quantity": "1", "units": "terabyte" } }
+  ],
+  "volumes": [{
+    "href": "http://cimi.example.org/volumes/1",
+    "attachmentPoint": "/dev/sda",
+    "protocol": "nfs"
+  }],
+  "networkInterfaces": [{
+    "vsp": { "href": "http://cimi.example.org/vsps/2" },
+    "hostname": "machine1.cimi.example.org",
+    "macAddress": "00:11:22:33:44:56",
+    "state": "UP",
+    "protocol": "TCP",
+    "allocation": "static",
+    "address": "192.168.0.18",
+    "defaultGateway": "192.168.0.1",
+    "dns": "192.168.0.1",
+    "maxTransmissionUnit": "1500"
+  }],
+  "meters": [
+    { "href": "http://cimi.example.org/meters/1" }
+  ],
+  "eventLog": { "href": "http://cimi.example.org/event_logs/1" },
+  "operations": [
+    { "rel": "edit",
+      "href": "http://cimi.example.org/machines/1/edit" },
+    { "rel": "delete",
+      "href": "http://cimi.example.org/machines/1/delete" },
+    { "rel": "http://www.dmtf.org/cimi/action/stop",
+      "href": "http://cimi.example.org/machines/1/stop" },
+    { "rel": "http://www.dmtf.org/cimi/action/restart",
+      "href": "http://cimi.example.org/machines/1/restart" }
+  ]
+}
diff --git a/server/tests/cimi/spec/cimi/data/machine.xml b/server/tests/cimi/spec/cimi/data/machine.xml
new file mode 100644
index 0000000..37b6582
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine.xml
@@ -0,0 +1,33 @@
+<Machine xmlns="http://www.dmtf.org/cimi">
+  <id>http://cimi.example.org/machines/1</id>
+  <name>machine1</name>
+  <description>Machine one description</description>
+  <created>2011-11-21</created>
+  <property name="owner_id">mockuser</property>
+  <cpu>4</cpu>
+  <memory quantity="1" units="gibibyte"/>
+  <state>STARTED</state>
+  <disk>
+    <capacity quantity="1" units="terabyte"/>
+  </disk>
+  <volume href="http://cimi.example.org/volumes/1"
+          attachmentPoint="/dev/sda" protocol="nfs" />
+  <networkInterface>
+    <vsp href="http://cimi.example.org/vsps/2"/>
+    <hostname>machine1.cimi.example.org</hostname>
+    <macAddress>00:11:22:33:44:56</macAddress>
+    <state>UP</state>
+    <protocol>TCP</protocol>
+    <allocation>static</allocation>
+    <address>192.168.0.18</address>
+    <defaultGateway>192.168.0.1</defaultGateway>
+    <dns>192.168.0.1</dns>
+    <maxTransmissionUnit>1500</maxTransmissionUnit>
+  </networkInterface>
+  <meter href="http://cimi.example.org/meters/1"/>
+  <eventLog href="http://cimi.example.org/event_logs/1"/>
+  <operation rel="edit" href="http://cimi.example.org/machines/1/edit"/>
+  <operation rel="delete" href="http://cimi.example.org/machines/1/delete"/>
+  <operation rel="http://www.dmtf.org/cimi/action/stop" href="http://cimi.example.org/machines/1/stop"/>
+  <operation rel="http://www.dmtf.org/cimi/action/restart" href="http://cimi.example.org/machines/1/restart"/>
+</Machine>
diff --git a/server/tests/cimi/spec/cimi/data/machine_admin.json b/server/tests/cimi/spec/cimi/data/machine_admin.json
new file mode 100644
index 0000000..35ab04b
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine_admin.json
@@ -0,0 +1,14 @@
+{
+  "id": "http://cimi.example.org/machine_admins/1",
+  "name": "credentials1",
+  "description": "Machine Admin One",
+  "created": "2011-11-16",
+  "username": "mockuser",
+  "password": "mockpassword",
+  "operations": [
+  { "rel": "edit",
+    "href": "http://cimi.example.org/machine_admins/1/edit" },
+  { "rel": "delete",
+    "href": "http://cimi.example.org/machine_admins/1/delete" }
+  ]
+}
diff --git a/server/tests/cimi/spec/cimi/data/machine_admin.xml b/server/tests/cimi/spec/cimi/data/machine_admin.xml
new file mode 100644
index 0000000..1f395c3
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine_admin.xml
@@ -0,0 +1,10 @@
+<MachineAdmin xmlns="http://www.dmtf.org/cimi">
+  <id>http://cimi.example.org/machine_admins/1</id>
+  <name>credentials1</name>
+  <description>Machine Admin One</description>
+  <created>2011-11-16</created>
+  <username>mockuser</username>
+  <password>mockpassword</password>
+  <operation rel="edit" href="http://cimi.example.org/machine_admins/1/edit"/>
+  <operation rel="delete" href="http://cimi.example.org/machine_admins/1/delete"/>
+</MachineAdmin>
diff --git a/server/tests/cimi/spec/cimi/data/machine_configuration.json b/server/tests/cimi/spec/cimi/data/machine_configuration.json
new file mode 100644
index 0000000..40c77ad
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine_configuration.json
@@ -0,0 +1,17 @@
+{
+  "id": "http://cimi.example.org/machine_configurations/1",
+  "name": "MachineConfiguration1",
+  "description": "Example MachineConfiguration One",
+  "created": "2011-11-14",
+  "cpu": "2",
+  "memory" : { "quantity": "1", "units": "gigabyte" },
+  "disks" : [
+    { "capacity": { "quantity": "1", "units": "terabyte" } }
+  ],
+  "properties": { "architecture": "i386" },
+  "operations": [
+    { "rel": "edit",
+      "href": "http://cimi.example.org/machine_configurations/1/edit" },
+    { "rel": "delete",
+      "href": "http://cimi.example.org/machine_configurations/1/delete" }]
+}
diff --git a/server/tests/cimi/spec/cimi/data/machine_configuration.xml b/server/tests/cimi/spec/cimi/data/machine_configuration.xml
new file mode 100644
index 0000000..b3810a3
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine_configuration.xml
@@ -0,0 +1,14 @@
+<MachineConfiguration xmlns="http://www.dmtf.org/cimi">
+  <id>http://cimi.example.org/machine_configurations/1</id>
+  <name>MachineConfiguration1</name>
+  <description>Example MachineConfiguration One</description>
+  <created>2011-11-14</created>
+  <property name="architecture">i386</property>
+  <cpu>2</cpu>
+  <memory quantity="1" units="gigabyte"/>
+  <disk>
+    <capacity quantity="1" units="terabyte"/>
+  </disk>
+  <operation rel="edit" href="http://cimi.example.org/machine_configurations/1/edit"/>
+  <operation rel="delete" href="http://cimi.example.org/machine_configurations/1/delete"/>
+</MachineConfiguration>
diff --git a/server/tests/cimi/spec/cimi/data/machine_image.json b/server/tests/cimi/spec/cimi/data/machine_image.json
new file mode 100644
index 0000000..cee0a52
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine_image.json
@@ -0,0 +1,14 @@
+{
+  "id": "http://cimi.example.org/machine_images/1",
+  "name": "img1",
+  "description": "Machine Image One",
+  "created": "2011-11-14",
+  "imageLocation": { "href": "nfs://cimi.example.com/images/1.img" },
+  "properties": { "status": "BUILD", "locked": "true" },
+  "operations": [
+  { "rel": "edit",
+    "href": "http://cimi.example.org/machine_images/1/edit" },
+  { "rel": "delete",
+    "href": "http://cimi.example.org/machine_images/1/delete" }
+  ]
+}
diff --git a/server/tests/cimi/spec/cimi/data/machine_image.xml b/server/tests/cimi/spec/cimi/data/machine_image.xml
new file mode 100644
index 0000000..e1d4452
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine_image.xml
@@ -0,0 +1,11 @@
+<MachineImage xmlns="http://www.dmtf.org/cimi">
+  <id>http://cimi.example.org/machine_images/1</id>
+  <name>img1</name>
+  <description>Machine Image One</description>
+  <created>2011-11-14</created>
+  <property name="status">BUILD</property>
+  <property name="locked">true</property>
+  <imageLocation href="nfs://cimi.example.com/images/1.img"/>
+  <operation rel="edit" href="http://cimi.example.org/machine_images/1/edit"/>
+  <operation rel="delete" href="http://cimi.example.org/machine_images/1/delete"/>
+</MachineImage>
diff --git a/server/tests/cimi/spec/cimi/data/machine_template.json b/server/tests/cimi/spec/cimi/data/machine_template.json
new file mode 100644
index 0000000..1be6592
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine_template.json
@@ -0,0 +1,30 @@
+{
+  "id": "http://cimi.example.org/machine_templates/1",
+  "name": "My First Template",
+  "description": "A template for testing",
+  "created": "2011-11-01",
+  "machineConfig": { "href": "http://cimi.example.org/machine_configs/1" },
+  "machineImage": { "href": "http://cimi.example.org/machine_images/1" },
+  "volumes": [{
+    "href": "http://cimi.example.org/volumes/1",
+    "attachmentPoint": "/dev/sda",
+    "protocol": "nfs"
+  }],
+  "networkInterfaces": [{
+    "vsp": { "href": "http://cimi.example.org/vsps/1" },
+    "hostname": "host.cimi.example.org",
+    "macAddress": "00:11:22:33:44:55",
+    "state": "UP",
+    "protocol": "TCP",
+    "allocation": "static",
+    "address": "192.168.0.17",
+    "defaultGateway": "192.168.0.1",
+    "dns": "192.168.0.1",
+    "maxTransmissionUnit": "1500"
+  }],
+  "operations": [
+    { "rel": "edit",
+      "href": "http://cimi.example.org/machine_templates/1/edit" },
+    { "rel": "delete",
+      "href": "http://cimi.example.org/machine_templates/1/delete" }]
+}
diff --git a/server/tests/cimi/spec/cimi/data/machine_template.xml b/server/tests/cimi/spec/cimi/data/machine_template.xml
new file mode 100644
index 0000000..1386460
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/machine_template.xml
@@ -0,0 +1,24 @@
+<MachineTemplate xmlns="http://www.dmtf.org/cimi">
+  <id>http://cimi.example.org/machine_templates/1</id>
+  <name>My First Template</name>
+  <description>A template for testing</description>
+  <created>2011-11-01</created>
+  <machineConfig href="http://cimi.example.org/machine_configs/1"/>
+  <machineImage href="http://cimi.example.org/machine_images/1"/>
+  <volume href="http://cimi.example.org/volumes/1"
+          attachmentPoint="/dev/sda" protocol="nfs" />
+  <networkInterface>
+    <vsp href="http://cimi.example.org/vsps/1"/>
+    <hostname>host.cimi.example.org</hostname>
+    <macAddress>00:11:22:33:44:55</macAddress>
+    <state>UP</state>
+    <protocol>TCP</protocol>
+    <allocation>static</allocation>
+    <address>192.168.0.17</address>
+    <defaultGateway>192.168.0.1</defaultGateway>
+    <dns>192.168.0.1</dns>
+    <maxTransmissionUnit>1500</maxTransmissionUnit>
+  </networkInterface>
+  <operation rel="edit" href="http://cimi.example.org/machine_templates/1/edit"/>
+  <operation rel="delete" href="http://cimi.example.org/machine_templates/1/delete"/>
+</MachineTemplate>
diff --git a/server/tests/cimi/spec/cimi/data/volume.json b/server/tests/cimi/spec/cimi/data/volume.json
new file mode 100644
index 0000000..e8364dd
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/volume.json
@@ -0,0 +1,16 @@
+{
+  "id": "http://cimi.example.org/volumes/1",
+  "name": "volume1",
+  "description": "Volume One",
+  "created": "2011-11-17",
+  "capacity": { "quantity": "10", "units": "gigabyte" },
+  "bootable": "false",
+  "supportsSnapshots": "false",
+  "guestInterface": "NFS",
+  "operations": [
+    { "rel": "edit",
+      "href": "http://cimi.example.org/volumes/1/edit" },
+    { "rel": "delete",
+      "href": "http://cimi.example.org/volumes/1/delete" }
+  ]
+}
diff --git a/server/tests/cimi/spec/cimi/data/volume.xml b/server/tests/cimi/spec/cimi/data/volume.xml
new file mode 100644
index 0000000..65e6e06
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/volume.xml
@@ -0,0 +1,12 @@
+<Volume xmlns="http://www.dmtf.org/cimi">
+  <id>http://cimi.example.org/volumes/1</id>
+  <name>volume1</name>
+  <description>Volume One</description>
+  <created>2011-11-17</created>
+  <capacity quantity="10" units="gigabyte"/>
+  <bootable>false</bootable>
+  <supportsSnapshots>false</supportsSnapshots>
+  <guestInterface>NFS</guestInterface>
+  <operation rel="edit" href="http://cimi.example.org/volumes/1/edit"/>
+  <operation rel="delete" href="http://cimi.example.org/volumes/1/delete"/>
+</Volume>
diff --git a/server/tests/cimi/spec/cimi/data/volume_configuration.json b/server/tests/cimi/spec/cimi/data/volume_configuration.json
new file mode 100644
index 0000000..817e20c
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/volume_configuration.json
@@ -0,0 +1,16 @@
+{
+  "id": "http://cimi.example.org/volume_configurations/1",
+  "name": "volume_config_1",
+  "description": "Volume Configuration One",
+  "created": "2011-11-21",
+  "format": "ext3",
+  "capacity": { "quantity": "10", "units": "gigabyte" },
+  "supportsSnapshots": "false",
+  "guestInterface": "NFS",
+  "operations": [
+    { "rel": "edit",
+      "href": "http://cimi.example.org/volume_configurations/1/edit" },
+    { "rel": "delete",
+      "href": "http://cimi.example.org/volume_configurations/1/delete" }
+  ]
+}
diff --git a/server/tests/cimi/spec/cimi/data/volume_configuration.xml b/server/tests/cimi/spec/cimi/data/volume_configuration.xml
new file mode 100644
index 0000000..120e8a6
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/volume_configuration.xml
@@ -0,0 +1,12 @@
+<VolumeConfiguration xmlns="http://www.dmtf.org/cimi">
+  <id>http://cimi.example.org/volume_configurations/1</id>
+  <name>volume_config_1</name>
+  <description>Volume Configuration One</description>
+  <created>2011-11-21</created>
+  <capacity quantity="10" units="gigabyte"/>
+  <supportsSnapshots>false</supportsSnapshots>
+  <guestInterface>NFS</guestInterface>
+  <format>ext3</format>
+  <operation rel="edit" href="http://cimi.example.org/volume_configurations/1/edit"/>
+  <operation rel="delete" href="http://cimi.example.org/volume_configurations/1/delete"/>
+</VolumeConfiguration>
diff --git a/server/tests/cimi/spec/cimi/data/volume_image.json b/server/tests/cimi/spec/cimi/data/volume_image.json
new file mode 100644
index 0000000..8c6ac04
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/volume_image.json
@@ -0,0 +1,14 @@
+{
+  "id": "http://cimi.example.org/volume_images/1",
+  "name": "volume_image_1",
+  "description": "Volume Image One",
+  "created": "2011-11-21",
+  "bootable": "false",
+  "imageLocation":{ "href": "nfs://cimi.example.com/volume_images/vol_image_1.img" },
+  "operations": [
+    { "rel": "edit",
+      "href": "http://cimi.example.org/volume_images/1/edit" },
+    { "rel": "delete",
+      "href": "http://cimi.example.org/volume_images/1/delete" }
+  ]
+}
diff --git a/server/tests/cimi/spec/cimi/data/volume_image.xml b/server/tests/cimi/spec/cimi/data/volume_image.xml
new file mode 100644
index 0000000..c6ba575
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/volume_image.xml
@@ -0,0 +1,10 @@
+<VolumeImage xmlns="http://www.dmtf.org/cimi">
+  <id>http://cimi.example.org/volume_images/1</id>
+  <name>volume_image_1</name>
+  <description>Volume Image One</description>
+  <created>2011-11-21</created>
+  <bootable>false</bootable>
+  <imageLocation href="nfs://cimi.example.com/volume_images/vol_image_1.img"/>
+  <operation rel="edit" href="http://cimi.example.org/volume_images/1/edit"/>
+  <operation rel="delete" href="http://cimi.example.org/volume_images/1/delete"/>
+</VolumeImage>
diff --git a/server/tests/cimi/spec/cimi/data/volume_template.json b/server/tests/cimi/spec/cimi/data/volume_template.json
new file mode 100644
index 0000000..bddb5dc
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/volume_template.json
@@ -0,0 +1,14 @@
+{
+  "id": "http://cimi.example.org/volume_templates/1",
+  "name": "volume_template_1",
+  "description": "Volume Template One",
+  "created": "2011-11-21",
+  "volumeConfig":{ "href": "http://cimi.example.com/volume_configurations/1" },
+  "volumeImage":{ "href": "http://cimi.example.com/volume_images/1" },
+  "operations": [
+    { "rel": "edit",
+      "href": "http://cimi.example.org/volume_templates/1/edit" },
+    { "rel": "delete",
+      "href": "http://cimi.example.org/volume_templates/1/delete" }
+  ]
+}
diff --git a/server/tests/cimi/spec/cimi/data/volume_template.xml b/server/tests/cimi/spec/cimi/data/volume_template.xml
new file mode 100644
index 0000000..47113b5
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/volume_template.xml
@@ -0,0 +1,10 @@
+<VolumeTemplate xmlns="http://www.dmtf.org/cimi">
+  <id>http://cimi.example.org/volume_templates/1</id>
+  <name>volume_template_1</name>
+  <description>Volume Template One</description>
+  <created>2011-11-21</created>
+  <volumeImage href="http://cimi.example.com/volume_images/1"/>
+  <volumeConfig href="http://cimi.example.com/volume_configurations/1"/>
+  <operation rel="edit" href="http://cimi.example.org/volume_templates/1/edit"/>
+  <operation rel="delete" href="http://cimi.example.org/volume_templates/1/delete"/>
+</VolumeTemplate>
diff --git a/server/tests/cimi/spec/cimi/model/machine_admin_spec.rb b/server/tests/cimi/spec/cimi/model/machine_admin_spec.rb
new file mode 100644
index 0000000..132272d
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/machine_admin_spec.rb
@@ -0,0 +1,32 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+if require 'minitest/autorun'
+  require_relative '../../spec_helper.rb'
+end
+
+describe "MachineAdmin model" do
+
+  before do
+    @xml = IO::read(File::join(DATA_DIR, "machine_admin.xml"))
+    @json = IO::read(File::join(DATA_DIR, "machine_admin.json"))
+  end
+
+  it "can be constructed from XML and JSON" do
+    should_properly_serialize_model CIMI::Model::MachineAdmin, @xml, @json
+  end
+
+end
diff --git a/server/tests/cimi/spec/cimi/model/machine_configuration_spec.rb b/server/tests/cimi/spec/cimi/model/machine_configuration_spec.rb
new file mode 100644
index 0000000..e59fcc0
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/machine_configuration_spec.rb
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+describe "MachineConfiguration model" do
+
+  before do
+    @xml = IO::read(File::join(DATA_DIR, "machine_configuration.xml"))
+    @json = IO::read(File::join(DATA_DIR, "machine_configuration.json"))
+  end
+
+  it "can be constructed from XML and JSON" do
+    should_properly_serialize_model CIMI::Model::MachineConfiguration, @xml, @json
+  end
+
+end
diff --git a/server/tests/cimi/spec/cimi/model/machine_image_spec.rb b/server/tests/cimi/spec/cimi/model/machine_image_spec.rb
new file mode 100644
index 0000000..7eef29a
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/machine_image_spec.rb
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+describe "MachineImage model" do
+
+  before do
+    @xml = IO::read(File::join(DATA_DIR, "machine_image.xml"))
+    @json = IO::read(File::join(DATA_DIR, "machine_image.json"))
+  end
+
+  it "can be constructed from XML and JSON" do
+    should_properly_serialize_model CIMI::Model::MachineImage, @xml, @json
+  end
+
+
+end
diff --git a/server/tests/cimi/spec/cimi/model/machine_spec.rb b/server/tests/cimi/spec/cimi/model/machine_spec.rb
new file mode 100644
index 0000000..b10ecfe
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/machine_spec.rb
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+describe "Machine model" do
+
+  before do
+    @xml = IO::read(File::join(DATA_DIR, "machine.xml"))
+    @json = IO::read(File::join(DATA_DIR, "machine.json"))
+  end
+
+  it "can be constructed from XML and JSON" do
+    should_properly_serialize_model CIMI::Model::Machine, @xml, @json
+  end
+
+end
diff --git a/server/tests/cimi/spec/cimi/model/machine_template_spec.rb b/server/tests/cimi/spec/cimi/model/machine_template_spec.rb
new file mode 100644
index 0000000..7210c92
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/machine_template_spec.rb
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+describe "MachineTemplate model" do
+
+  before do
+    @xml = IO::read(File::join(DATA_DIR, "machine_template.xml"))
+    @json = IO::read(File::join(DATA_DIR, "machine_template.json"))
+  end
+
+  it "can be constructed from XML and JSON" do
+    should_properly_serialize_model CIMI::Model::MachineTemplate, @xml, @json
+  end
+
+end
diff --git a/server/tests/cimi/spec/cimi/model/schema_spec.rb b/server/tests/cimi/spec/cimi/model/schema_spec.rb
new file mode 100644
index 0000000..c5619b1
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/schema_spec.rb
@@ -0,0 +1,243 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+describe "Schema" do
+  before do
+    @schema = CIMI::Model::Schema.new
+  end
+
+  it "does not allow adding attributes after being used for conversion" do
+    @schema.scalar(:before)
+    @schema.from_json({})
+    lambda { @schema.scalar(:after) }.must_raise RuntimeError, 'The schema has already been used to convert objects'
+  end
+
+  describe "scalars" do
+    before(:each) do
+      @schema.scalar(:attr)
+      @schema.text(:camel_hump)
+
+      @schema.attribute_names.must_equal [:attr, :camel_hump]
+    end
+
+    let :sample_xml do
+      parse_xml("<camelHump>bumpy</camelHump>", :keep_root => true)
+    end
+
+    it "should camel case attribute names for JSON" do
+      obj = @schema.from_json("camelHump" => "bumpy")
+      obj.wont_be_nil
+      obj[:camel_hump].must_equal "bumpy"
+
+      json = @schema.to_json(obj)
+      json['camelHump'].must_equal "bumpy"
+    end
+
+    it "should camel case attribute names for XML" do
+      obj = @schema.from_xml(sample_xml)
+
+      obj.wont_be_nil
+      obj[:camel_hump].must_equal "bumpy"
+
+      xml = @schema.to_xml(obj)
+
+      xml['camelHump'].must_equal [{ "content" => "bumpy" }]
+    end
+
+    it "should allow aliasing the XML and JSON name" do
+      @schema.scalar :aliased, :xml_name => :xml, :json_name => :json
+      obj = @schema.from_xml({"aliased" => "no", "xml" => "yes"}, {})
+      obj[:aliased].must_equal "yes"
+
+      obj = @schema.from_json({"aliased" => "no", "json" => "yes"}, {})
+      obj[:aliased].must_equal "yes"
+    end
+  end
+
+  describe "hrefs" do
+    before do
+      @schema.href(:meter)
+    end
+
+    it "should extract the href attribute from XML" do
+      xml = parse_xml("<meter href='http://example.org/'/>")
+
+      obj = @schema.from_xml(xml)
+      check obj
+      @schema.to_xml(obj).must_equal xml
+    end
+
+    it "should extract the href attribute from JSON" do
+      json = { "meter" =>  { "href" => "http://example.org/" } }
+
+      obj = @schema.from_json(json)
+      check obj
+      @schema.to_json(obj).must_equal json
+    end
+
+    def check(obj)
+      obj.wont_be_nil
+      obj[:meter].href.must_equal 'http://example.org/'
+    end
+  end
+
+  describe "structs" do
+    before do
+      @schema.struct(:struct, :content => :scalar) do
+        scalar   :href
+      end
+      @schema.attribute_names.must_equal [:struct]
+    end
+
+    let(:sample_json) do
+      { "struct" => { "scalar" => "v1", "href" => "http://example.org/" } }
+    end
+
+    let (:sample_xml) do
+      parse_xml("<struct href='http://example.org/'>v1</struct>")
+    end
+
+    let (:sample_xml_no_href) do
+      parse_xml("<struct>v1</struct>")
+    end
+
+    describe "JSON conversion" do
+      it "should convert empty hash" do
+        model = @schema.from_json({ })
+        check_empty_struct model
+        @schema.to_json(model).keys.must_be_empty
+      end
+
+      it "should convert empty body" do
+        model = @schema.from_json({ "struct" => { } })
+        check_empty_struct model
+        @schema.to_json(model).keys.must_be_empty
+      end
+
+      it "should convert values" do
+        model = @schema.from_json(sample_json)
+        check_struct model
+        @schema.to_json(model).must_equal sample_json
+      end
+    end
+
+    describe "XML conversion" do
+      it "should convert empty hash" do
+        model = @schema.from_xml({ })
+        check_empty_struct model
+        @schema.to_xml(model).keys.must_be_empty
+      end
+
+      it "should convert empty body" do
+        model = @schema.from_json({ "struct" => { } })
+        check_empty_struct model
+        @schema.to_xml(model).keys.must_be_empty
+      end
+
+      it "should convert values" do
+        model = @schema.from_xml(sample_xml)
+        check_struct model
+        @schema.to_xml(model).must_equal sample_xml
+      end
+
+      it "should handle missing attributes" do
+        model = @schema.from_xml(sample_xml_no_href)
+        check_struct model, :nil_href => true
+        @schema.to_xml(model).must_equal sample_xml_no_href
+      end
+    end
+
+    def check_struct(obj, opts = {})
+      obj.wont_be_nil
+      obj[:struct].wont_be_nil
+      obj[:struct].scalar.must_equal "v1"
+      if opts[:nil_href]
+        obj[:struct].href.must_be_nil
+      else
+        obj[:struct].href.must_equal "http://example.org/"
+      end
+    end
+
+    def check_empty_struct(obj)
+      obj.wont_be_nil
+      obj[:struct].wont_be_nil
+      obj[:struct].scalar.must_be_nil
+      obj[:struct].href.must_be_nil
+    end
+  end
+
+  describe "arrays" do
+    before do
+      @schema.array(:structs, :content => :scalar) do
+        scalar :href
+      end
+    end
+
+    let(:sample_json) do
+      { "structs" => [{ "scalar" => "v1", "href" => "http://example.org/1" },
+                      { "scalar" => "v2", "href" => "http://example.org/2" }] }
+    end
+
+    let (:sample_xml) do
+      parse_xml("<wrapper>
+  <struct href='http://example.org/1'>v1</struct>
+  <struct href='http://example.org/2'>v2</struct>
+</wrapper>", :keep_root => false)
+    end
+
+    it "should convert missing array from JSON" do
+      obj = @schema.from_json({})
+
+      obj.wont_be_nil
+      obj[:structs].must_be_empty
+      @schema.to_json(obj).keys.must_be_empty
+    end
+
+    it "should convert empty array from JSON" do
+      obj = @schema.from_json("structs" => [])
+
+      obj.wont_be_nil
+      obj[:structs].must_be_empty
+      @schema.to_json(obj).keys.must_be_empty
+    end
+
+    it "should convert arrays from JSON" do
+      obj = @schema.from_json(sample_json)
+
+      check_structs(obj)
+      @schema.to_json(obj).must_equal sample_json
+    end
+
+    it "should convert arrays from XML" do
+      obj = @schema.from_xml(sample_xml)
+
+      check_structs(obj)
+      @schema.to_xml(obj).must_equal sample_xml
+    end
+
+    def check_structs(obj)
+      obj.wont_be_nil
+      obj[:structs].size.must_equal 2
+      obj[:structs][0].scalar.must_equal "v1"
+      obj[:structs][0].href.must_equal "http://example.org/1"
+      obj[:structs][1].scalar.must_equal "v2"
+      obj[:structs][1].href.must_equal "http://example.org/2"
+    end
+  end
+
+end
diff --git a/server/tests/cimi/spec/cimi/model/volume_configuration_spec.rb b/server/tests/cimi/spec/cimi/model/volume_configuration_spec.rb
new file mode 100644
index 0000000..c12ac05
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/volume_configuration_spec.rb
@@ -0,0 +1,32 @@
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+describe "Volume Configuration model" do
+
+  before do
+    @xml = IO::read(File::join(DATA_DIR, "volume_configuration.xml"))
+    @json = IO::read(File::join(DATA_DIR, "volume_configuration.json"))
+  end
+
+  it "can be constructed from XML and JSON" do
+    should_properly_serialize_model CIMI::Model::VolumeConfiguration, @xml, @json
+  end
+
+
+end
diff --git a/server/tests/cimi/spec/cimi/model/volume_image_spec.rb b/server/tests/cimi/spec/cimi/model/volume_image_spec.rb
new file mode 100644
index 0000000..a6c4a20
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/volume_image_spec.rb
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+describe "Volume Image model" do
+
+  before do
+    @xml = IO::read(File::join(DATA_DIR, "volume_image.xml"))
+    @json = IO::read(File::join(DATA_DIR, "volume_image.json"))
+  end
+
+  it "can be constructed from XML and JSON" do
+    should_properly_serialize_model CIMI::Model::VolumeImage, @xml, @json
+  end
+
+
+end
diff --git a/server/tests/cimi/spec/cimi/model/volume_spec.rb b/server/tests/cimi/spec/cimi/model/volume_spec.rb
new file mode 100644
index 0000000..1176add
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/volume_spec.rb
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+describe "Volume model" do
+
+  before do
+    @xml = IO::read(File::join(DATA_DIR, "volume.xml"))
+    @json = IO::read(File::join(DATA_DIR, "volume.json"))
+  end
+
+  it "can be constructed from XML and JSON" do
+    should_properly_serialize_model CIMI::Model::Volume, @xml, @json
+  end
+
+end
diff --git a/server/tests/cimi/spec/cimi/model/volume_template_spec.rb b/server/tests/cimi/spec/cimi/model/volume_template_spec.rb
new file mode 100644
index 0000000..444baa7
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/volume_template_spec.rb
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+describe "Volume Template model" do
+
+  before do
+    @xml = IO::read(File::join(DATA_DIR, "volume_template.xml"))
+    @json = IO::read(File::join(DATA_DIR, "volume_template.json"))
+  end
+
+  it "can be constructed from XML and JSON" do
+    should_properly_serialize_model CIMI::Model::VolumeTemplate, @xml, @json
+  end
+
+end
diff --git a/server/tests/cimi/spec/spec_helper.rb b/server/tests/cimi/spec/spec_helper.rb
new file mode 100644
index 0000000..3cde6cf
--- /dev/null
+++ b/server/tests/cimi/spec/spec_helper.rb
@@ -0,0 +1,127 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require 'minitest/autorun'
+require 'minitest/spec'
+require 'xmlsimple'
+require 'require_relative'
+
+require_relative '../../../lib/deltacloud/core_ext.rb'
+require_relative '../../../lib/cimi/models.rb'
+
+DATA_DIR = File::join(File::expand_path(File::dirname(__FILE__)), 'cimi', 'data')
+
+def parse_xml(xml, opts = {})
+  opts[:force_content] = true
+  opts[:keep_root] = true unless opts.has_key?(:keep_root)
+  XmlSimple.xml_in(xml, opts)
+end
+
+class HashCmp
+  def initialize(exp, act)
+    @exp = exp
+    @act = act
+    @io = StringIO.new
+  end
+
+  def match?
+    @equal = true
+    compare_values(@exp, @act, [])
+    @equal
+  end
+
+  def errors
+    @io.string
+  end
+
+  private
+  def compare_values(exp, act, path)
+    if exp.is_a?(String)
+      mismatch("entries differ", exp, act, path) unless exp == act
+    elsif exp.is_a?(Array)
+      mismatch("expected array", exp, act, path) unless act.is_a?(Array)
+      unless act.size == exp.size
+        mismatch("different array lengths", exp, act, path)
+      end
+      name = path.pop
+      0.upto(exp.size-1) do |i|
+        compare_values(exp[i], act[i], path + [ "#{name}[#{i}]" ])
+      end
+    elsif exp.is_a?(Hash)
+      unless act.is_a?(Hash)
+        mismatch("expected Hash", exp, act, path)
+        return
+      end
+      unless (missing = exp.keys - act.keys).empty?
+        error "Missing key(s) at /#{path.join("/")}: #{missing.inspect}"
+      end
+      unless (excess = act.keys - exp.keys).empty?
+        error "Excess key(s) at /#{path.join("/")}: #{excess.inspect}"
+      end
+      (exp.keys - missing - excess).each do |k|
+        compare_values(exp[k], act[k], path + [ k ])
+      end
+    end
+  end
+
+  def mismatch(msg, exp, act, path)
+    error "#{msg}[#{fmt(path)}]: #{exp.inspect} != #{act.inspect}"
+  end
+
+  def error(msg)
+    @equal = false
+    @io.puts msg
+  end
+
+  def fmt(path)
+    "/#{path.join("/")}"
+  end
+end
+
+def should_properly_serialize_model(model_class, xml, json)
+  # Roundtrip in same format
+  model_class.from_xml(xml).must_serialize_to xml, :fmt => :xml
+  model_class.from_json(json).must_serialize_to json, :fmt => :json
+  # Roundtrip crossing format
+  model_class.from_xml(xml).must_serialize_to json, :fmt => :json
+  model_class.from_json(json).must_serialize_to xml, :fmt => :xml
+end
+
+module MiniTest::Assertions
+
+  def assert_serialize_to(exp, act, opts)
+    raise "missing format; use :fmt => [:xml || :json]" if opts[:fmt].nil?
+    exp, act = [exp, act].map { |x| convert(x, opts[:fmt]) }
+    m = HashCmp.new(exp, act)
+    assert m.match?,  "#{opts[:fmt].to_s.upcase} documents do not match\n" + m.errors
+  end
+
+  def convert(x, fmt)
+    if fmt == :json
+      x = x.to_json if x.is_a?(CIMI::Model::Base)
+      x = JSON.parse(x) if x.is_a?(String)
+    elsif fmt == :xml
+      x = x.to_xml if x.is_a?(CIMI::Model::Base)
+      x = parse_xml(x)  if x.is_a?(String)
+    else
+      raise "Invalid format #{fmt}"
+    end
+    x
+  end
+
+end
+
+CIMI::Model::Base.infect_an_assertion :assert_serialize_to, :must_serialize_to
-- 
1.7.10.2