You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by ma...@redhat.com on 2012/07/20 18:55:46 UTC

Initial API tests rewrite

Initial API tests rewrite - these are intended as 'black box' tests,
i.e. to be pointed at a deltacloud API server running somewhere and
interacting only with the API and not with deltacloud internals 
(as described in more detail http://piratepad.net/deltacloud-testing
). Uses RestClient to do the talking.

Things to notice in Patch 1:

* Edit /deltacloud/config.yaml with location of deltacloud server to
be tested and add credentials for the driver you expect to be running
there

* invoke tests with rake test:deltacloud (idea is that a rake test:ec2
and rake test:cimi rake tasks will be added in due course)

* test_setup.rb talks to the URL you configure in config.yaml and
'discovers' the current driver, it's collections (adding the test_files
accordingly, so if only 'buckets' is supported then only buckets_test.rb
will run in the TestTask) and features (so for example if buckets 
collection doesn't have the 'bucket_location' feature, then that specific
test will be skipped).

marios

Re: Initial API tests rewrite

Posted by "marios@redhat.com" <ma...@redhat.com>.
I got a "ezmlm-reject: fatal: Sorry, I don't accept messages larger than
1000000 bytes (#5.2.3)"

PATCH 2/2 is @
http://mariosandreou.com/documents/0002-Deprecates-old-driver-specific-cucumber-tests-and-fe.patch

(just deprecates old cuke tests into /deltacloud/tests/deprecated directory)


On 20/07/12 19:55, marios@redhat.com wrote:
> Initial API tests rewrite - these are intended as 'black box' tests,
> i.e. to be pointed at a deltacloud API server running somewhere and
> interacting only with the API and not with deltacloud internals 
> (as described in more detail http://piratepad.net/deltacloud-testing
> ). Uses RestClient to do the talking.
> 
> Things to notice in Patch 1:
> 
> * Edit /deltacloud/config.yaml with location of deltacloud server to
> be tested and add credentials for the driver you expect to be running
> there
> 
> * invoke tests with rake test:deltacloud (idea is that a rake test:ec2
> and rake test:cimi rake tasks will be added in due course)
> 
> * test_setup.rb talks to the URL you configure in config.yaml and
> 'discovers' the current driver, it's collections (adding the test_files
> accordingly, so if only 'buckets' is supported then only buckets_test.rb
> will run in the TestTask) and features (so for example if buckets 
> collection doesn't have the 'bucket_location' feature, then that specific
> test will be skipped).
> 
> marios
> 



Re: Initial API tests rewrite

Posted by "marios@redhat.com" <ma...@redhat.com>.
On 21/07/12 04:12, David Lutterkort wrote:
> Hi Marios,
> 
> On Fri, 2012-07-20 at 19:55 +0300, marios@redhat.com wrote:
>> Initial API tests rewrite - these are intended as 'black box' tests,
>> i.e. to be pointed at a deltacloud API server running somewhere and
>> interacting only with the API and not with deltacloud internals 
>> (as described in more detail http://piratepad.net/deltacloud-testing
>> ). Uses RestClient to do the talking.
> 
> Looks good. I have a whole bunch of comments, which I put in the form of
> patches[1] ;)
> 
> The patch series applies on top of your 2/2 and should just be squashed
> into it. There's a few more changes I'd like to make, but that will have
> to wait until Monday.
> 

many thanks for the comments - great tidying up - including a proper LOL
moment - even after 2.5 yrs of working with ruby I'm still impressed by
monkey patching and editing classes on the fly:

> -    driver = xml_response(res).root[:driver]
> +    driver = res.xml.root[:driver]

/me opens http://rdoc.info/github/archiloque/rest-client/index - looks
for the .xml and .json methods I missed...  until I see

> -def xml_response(xml)
> -  Nokogiri::XML(xml)
> -end

> +module RestClient::Response
> +  def xml
> +    @xml ||= Nokogiri::XML(body)
> +  end
>  
> -def json_response(json)
> -  JSON.parse(json)

> +  def json
> +    @json ||= JSON.parse(body)
> +  end
>  end

:)

marios


> David
> 
> [1] http://people.apache.org/~lutter/patches/tests-rewrite/
> 
> 



Re: Initial API tests rewrite

Posted by David Lutterkort <lu...@redhat.com>.
On Fri, 2012-07-20 at 18:12 -0700, David Lutterkort wrote:
> On Fri, 2012-07-20 at 19:55 +0300, marios@redhat.com wrote:
> > Initial API tests rewrite - these are intended as 'black box' tests,
> > i.e. to be pointed at a deltacloud API server running somewhere and
> > interacting only with the API and not with deltacloud internals 
> > (as described in more detail http://piratepad.net/deltacloud-testing
> > ). Uses RestClient to do the talking.
> 
> Looks good. I have a whole bunch of comments, which I put in the form of
> patches[1] ;)
> 
> The patch series applies on top of your 2/2 and should just be squashed
> into it. There's a few more changes I'd like to make, but that will have
> to wait until Monday.

I've added a few more patches to my patch series at [1] - in particular,
patches 10/14 and higher are new.

Besides shuffling code around, there's now a couple helpers to suppress
test runs when collections or features are missing:

  describe "collection frob" do
        need_collection :frobs
        need_feature :frobs, :user_name
        
        it "lists all frobs" do
          ...
        end
  end

The one thing I am not too happy about is that it leads to skipped
tests, which is kinda ugly when you run 'rake test:deltacloud
TESTOPT="-v"' - I couldn't find a good way to suppress tests quietly.

David

[1] http://people.apache.org/~lutter/patches/tests-rewrite/



Re: Initial API tests rewrite

Posted by David Lutterkort <lu...@redhat.com>.
Hi Marios,

On Fri, 2012-07-20 at 19:55 +0300, marios@redhat.com wrote:
> Initial API tests rewrite - these are intended as 'black box' tests,
> i.e. to be pointed at a deltacloud API server running somewhere and
> interacting only with the API and not with deltacloud internals 
> (as described in more detail http://piratepad.net/deltacloud-testing
> ). Uses RestClient to do the talking.

Looks good. I have a whole bunch of comments, which I put in the form of
patches[1] ;)

The patch series applies on top of your 2/2 and should just be squashed
into it. There's a few more changes I'd like to make, but that will have
to wait until Monday.

David

[1] http://people.apache.org/~lutter/patches/tests-rewrite/



[PATCH 1/2] Adds initial Deltacloud API tests

Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>


Signed-off-by: marios <ma...@redhat.com>
---
 tests/Rakefile                             |   28 ++---
 tests/deltacloud/base_api_test.rb          |  111 ++++++++++++++++
 tests/deltacloud/buckets_test.rb           |  187 ++++++++++++++++++++++++++++
 tests/deltacloud/config.yaml               |   18 +++
 tests/deltacloud/test_setup.rb             |  119 ++++++++++++++++++
 5 files changed, 443 insertions(+), 20 deletions(-)
 create mode 100644 tests/deltacloud/base_api_test.rb
 create mode 100644 tests/deltacloud/buckets_test.rb
 create mode 100644 tests/deltacloud/config.yaml
 create mode 100644 tests/deltacloud/hardware_profiles_test.rb
 create mode 100644 tests/deltacloud/images_test.rb
 create mode 100644 tests/deltacloud/instance_states_test.rb
 create mode 100644 tests/deltacloud/instances_test.rb
 create mode 100644 tests/deltacloud/keys_test.rb
 create mode 100644 tests/deltacloud/realms_test.rb
 create mode 100644 tests/deltacloud/storage_snapshots_test.rb
 create mode 100644 tests/deltacloud/storage_volumes_test.rb
 create mode 100644 tests/deltacloud/test_setup.rb

diff --git a/tests/Rakefile b/tests/Rakefile
index dfdb209..0475ae5 100644
--- a/tests/Rakefile
+++ b/tests/Rakefile
@@ -19,28 +19,16 @@
 # Add your own tasks in files placed in lib/tasks ending in .rake,
 # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
 
-require 'rake'
-require 'cucumber'
-require 'cucumber/rake/task'
+require 'rake/testtask'
+require 'deltacloud/test_setup.rb'
 
-DRIVER=ENV['API_DRIVER'] || 'mock'
+namespace :test do
 
-Cucumber::Rake::Task.new(:features) do |t|
-  t.cucumber_opts = "../tests/#{DRIVER} --format html --out ../tests/tmp/cucumber_#{DRIVER}.html"
-  t.rcov = false
-end
-
-Cucumber::Rake::Task.new(:cucumber) do |t|
-  t.cucumber_opts = "../tests/#{DRIVER} --format pretty"
-  t.rcov = false
-end
+    file_list = Rake::FileList.new(deltacloud_test_file_names)
+    Rake::TestTask.new(:deltacloud) do |t|
+      t.test_files = file_list << "deltacloud/base_api_test.rb"
+      t.options = "-v"
+    end
 
-Cucumber::Rake::Task.new(:rcov) do |t|
-  t.cucumber_opts = "../tests/#{DRIVER} --format pretty"
-  t.rcov = true
-  t.rcov_opts << %[-o "../tests/tmp/coverage_#{DRIVER}"]
 end
 
-Cucumber::Rake::Task.new(:junit) do |t|
-  t.cucumber_opts = "../tests/#{DRIVER} --format junit --out #{File.join(File.dirname(__FILE__), "tmp", "junit_#{DRIVER}")}"
-end
diff --git a/tests/deltacloud/base_api_test.rb b/tests/deltacloud/base_api_test.rb
new file mode 100644
index 0000000..2035b8f
--- /dev/null
+++ b/tests/deltacloud/base_api_test.rb
@@ -0,0 +1,111 @@
+$:.unshift File.join(File.dirname(__FILE__), '..')
+require "deltacloud/test_setup.rb"
+
+describe "Deltacloud API Entry Point" do
+
+  it 'return status 200 OK when accessing API entrypoint' do
+    res = get
+    res.code.must_equal 200
+  end
+
+  it 'advertise the current driver in API entrypoint' do
+    res = get({:accept => :xml})
+    driver = xml_response(res).root[:driver]
+    driver.wont_be_nil
+    DRIVERS.include?(driver).must_equal true
+  end
+
+  it 'advertise the current API version in API entrypoint' do
+    res = get({:accept => :xml})
+    version = xml_response(res).root[:version]
+    version.wont_be_nil
+    version.must_equal API_VERSION
+  end
+
+  it 'advertise the current API version in HTTP headers' do
+    res = get
+    res.headers[:server].must_equal "Apache-Deltacloud/#{API_VERSION}"
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    res = get
+    res.headers[:etag].wont_be_nil
+  end
+
+  it 'advertise collections in API entrypoint' do
+    res = get({:accept => :xml})
+    xml_response(res).xpath('//api/link').wont_be_empty
+  end
+
+  it 'include the :href and :rel attribute for each collection in API entrypoint' do
+    xml_response(get({:accept => :xml})).xpath("//api/link").each do |collection|
+      collection[:href].wont_be_nil
+      collection[:rel].wont_be_nil
+    end
+  end
+
+  it 'uses the absolute URI in the :href attribute for each collection in API entrypoint' do
+    xml_response(get({:accept => :xml})).xpath("//api/link").each do |collection|
+      collection[:href].must_match /^http/
+    end
+  end
+
+  it 'advertise features for some collections in API entrypoint' do
+    xml_doc = xml_response(get({:accept => :xml}))
+    xml_doc.xpath("//api/link/feature").wont_be_empty
+  end
+
+  it 'advertise the name of the feature for some collections in API entrypoint' do
+    xml_response(get({:accept => :xml})).xpath("//api/link/feature").each do |feature|
+      feature[:name].wont_be_nil
+    end
+  end
+
+  it 'must change the media type from XML to JSON using Accept headers' do
+    res = get({:accept => :json})
+    res.headers[:content_type].must_equal 'application/json'
+  end
+
+  it 'must change the media type to JSON using the "?format" parameter in URL' do
+    res = get({}, "?format=json")
+    res.headers[:content_type].must_equal 'application/json'
+  end
+
+  it 'must change the driver when using X-Deltacloud-Driver HTTP header' do
+    res = xml_response(get({'X-Deltacloud-Driver'=> 'ec2', :accept=> :xml}))
+    res.root[:driver].must_equal 'ec2'
+    res = xml_response(get({'X-Deltacloud-Driver'=> 'mock', :accept=> :xml}))
+    res.root[:driver].must_equal 'mock'
+  end
+
+  it 'must change the features when driver is swapped using HTTP headers' do
+    res = xml_response(get({'X-Deltacloud-Driver'=> 'ec2', :accept=> :xml}))
+    # The 'user_name' feature is not supported currently for the EC2 driver
+    (res/'api/link/feature').map { |f| f[:name] }.wont_include 'user_name'
+    res = xml_response(get({'X-Deltacloud-Driver'=> 'mock', :accept=> :xml}))
+    # But it's supported in Mock driver
+    (res/'api/link/feature').map { |f| f[:name] }.must_include 'user_name'
+  end
+
+  it 'must re-validate the driver credentials when using "?force_auth" parameter in URL' do
+    proc {get({ :params => {:force_auth => '1'} })}.must_raise RestClient::Request::Unauthorized
+    res = get({ "X-Deltacloud-Driver"=>"mock", :params=>{:force_auth => '1'}, :Authorization=>"Basic #{Base64.encode64('mockuser:mockpassword')}" })
+    res.code.must_equal 200
+  end
+
+  it 'must change the API PROVIDER using the /api;provider matrix parameter in URI' do
+    res = xml_response(get({}, ';provider=test1'))
+    res.root[:provider].wont_be_nil
+    res.root[:provider].must_equal 'test1'
+    res = xml_response(get({}, ';provider=test2'))
+    res.root[:provider].must_equal 'test2'
+  end
+
+  it 'must change the API DRIVER using the /api;driver matrix parameter in URI' do
+    res = xml_response(get({}, ';driver=ec2'))
+    res.root[:driver].must_equal 'ec2'
+    res = xml_response(get({}, ';driver=mock'))
+    res.root[:driver].must_equal 'mock'
+  end
+
+end
diff --git a/tests/deltacloud/buckets_test.rb b/tests/deltacloud/buckets_test.rb
new file mode 100644
index 0000000..3a525ed
--- /dev/null
+++ b/tests/deltacloud/buckets_test.rb
@@ -0,0 +1,187 @@
+$:.unshift File.join(File.dirname(__FILE__), '..')
+require "deltacloud/test_setup.rb"
+
+
+require 'ruby-debug'
+BUCKETS = "/buckets"
+
+#make sure we have at least one bucket and blob to test
+bucket, blob = create_a_bucket_and_blob
+
+features_hash = discover_features
+
+describe 'Deltacloud API buckets collection' do
+
+MiniTest::Unit.after_tests{
+  #finally delete the bucket/blob we created for the tests:
+  delete_bucket_and_blob(bucket, blob)
+}
+
+  it 'must advertise the buckets collection in API entrypoint' do
+    res = xml_response(get)
+    (res/'api/link[@rel=buckets]').wont_be_empty
+  end
+
+  it 'must require authentication to access the "bucket" collection' do
+    proc {  get({},BUCKETS) }.must_raise RestClient::Request::Unauthorized
+  end
+
+  it 'should respond with HTTP_OK when accessing the :buckets collection with authentication' do
+    res = get({}, BUCKETS, true)
+    res.code.must_equal 200
+  end
+
+  it 'should be possible to create bucket with POST /api/buckets and delete it with DELETE /api/buckets/:id' do
+    bucket_name = random_name
+    #create bucket
+    res = post({:name=>bucket_name}, BUCKETS, {}, true)
+    #check response
+    res.code.must_equal 201
+    xml_res = xml_response(res)
+    xml_res.xpath("//bucket/name").text.must_equal bucket_name
+    xml_res.xpath("//bucket").size.must_equal 1
+    xml_res.xpath("//bucket")[0][:id].must_equal bucket_name
+    #GET bucket
+    res = get({}, BUCKETS+"/"+bucket_name, true)
+    res.code.must_equal 200
+    #DELETE bucket
+    res = delete({}, BUCKETS+"/"+bucket_name)
+    res.code.must_equal 204
+  end
+
+  it 'should be possible to specify location for POST /api/buckets if bucket_location feature' do
+    skip("No bucket_location feature specified for driver #{API_DRIVER} running at #{API_URL}... skipping test") unless features_hash["buckets"].include?("bucket_location")
+    bucket_name = random_name
+#    res = post({:name=>bucket_name, :bucket_location=>
+  end
+
+
+  it 'should support the JSON media type' do
+    res = get({:accept=>:json}, BUCKETS, true)
+    res.code.must_equal 200
+    res.headers[:content_type].must_equal 'application/json'
+    assert_silent {JSON.parse(res)}
+  end
+
+  it 'must include the ETag in HTTP headers' do
+    res = get({}, BUCKETS, true)
+    res.headers[:etag].wont_be_nil
+  end
+
+  it 'must have the "buckets" element on top level' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    xml_res.root.name.must_equal 'buckets'
+  end
+
+  it 'must have some "bucket" elements inside "buckets"' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').wont_be_empty
+  end
+
+  it 'must provide the :id attribute for each bucket in collection' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      r[:id].wont_be_nil
+    end
+  end
+
+  it 'must include the :href attribute for each "bucket" element in collection' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      r[:href].wont_be_nil
+    end
+  end
+
+  it 'must use the absolute URL in each :href attribute' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      r[:href].must_match /^http/
+    end
+  end
+
+  it 'must have the URL ending with the :id of the bucket' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      r[:href].must_match /#{r[:id]}$/
+    end
+  end
+
+  it 'must have the "name" element defined for each bucket in collection' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      (r/'name').wont_be_nil
+      (r/'name').wont_be_empty
+    end
+  end
+
+  it 'must have the "size" element defined for each bucket in collection' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      (r/'size').wont_be_nil
+      (r/'size').wont_be_empty
+    end
+  end
+
+  it 'must return 200 OK when following the URL in bucket element' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      bucket_res = RestClient.get(r[:href], {:Authorization=>BASIC_AUTH})
+      bucket_res.code.must_equal 200
+    end
+  end
+
+  it 'must have the "name" element for the bucket and it should match with the one in collection' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      bucket_xml = xml_response(get({:accept=>:xml}, BUCKETS+"/#{r[:id]}", true))
+      (bucket_xml/'name').wont_be_empty
+      (bucket_xml/'name').first.text.must_equal((r/'name').first.text)
+    end
+  end
+
+  it 'all "blob" elements for the bucket should match the ones in collection' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      bucket_xml = xml_response(get({:accept=>:xml}, BUCKETS+"/#{r[:id]}", true))
+      (bucket_xml/'bucket/blob').each do |b|
+        b[:id].wont_be_nil
+        b[:href].wont_be_nil
+        b[:href].must_match /^http/
+        b[:href].must_match /#{r[:id]}\/#{b[:id]}$/
+      end
+    end
+  end
+
+  it 'must allow to get all blobs details and the details should be set correctly' do
+    xml_res = xml_response(get({:accept=>:xml}, BUCKETS, true))
+    (xml_res/'buckets/bucket').each do |r|
+      bucket_xml = xml_response(get({:accept=>:xml}, BUCKETS+"/#{r[:id]}", true))
+      (bucket_xml/'bucket/blob').each do |b|
+        blob_xml = xml_response(get({:accept=>:xml}, BUCKETS+"/#{r[:id]}/#{b[:id]}", true))
+        blob_xml.root.name.must_equal 'blob'
+        blob_xml.root[:id].must_equal b[:id]
+        (blob_xml/'bucket').wont_be_empty
+        (blob_xml/'bucket').size.must_equal 1
+        (blob_xml/'bucket').first.text.wont_be_nil
+        (blob_xml/'bucket').first.text.must_equal r[:id]
+        (blob_xml/'content_length').wont_be_empty
+        (blob_xml/'content_length').size.must_equal 1
+        (blob_xml/'content_length').first.text.must_match /^(\d+)$/
+        (blob_xml/'content_type').wont_be_empty
+        (blob_xml/'content_type').size.must_equal 1
+        (blob_xml/'content_type').first.text.wont_be_nil
+        (blob_xml/'last_modified').wont_be_empty
+        (blob_xml/'last_modified').size.must_equal 1
+        (blob_xml/'last_modified').first.text.wont_be_empty
+        (blob_xml/'content').wont_be_empty
+        (blob_xml/'content').size.must_equal 1
+        (blob_xml/'content').first[:rel].wont_be_nil
+        (blob_xml/'content').first[:rel].must_equal 'blob_content'
+        (blob_xml/'content').first[:href].wont_be_nil
+        (blob_xml/'content').first[:href].must_match /^http/
+        (blob_xml/'content').first[:href].must_match /\/content$/
+      end
+    end
+  end
+
+end
diff --git a/tests/deltacloud/config.yaml b/tests/deltacloud/config.yaml
new file mode 100644
index 0000000..e94a29e
--- /dev/null
+++ b/tests/deltacloud/config.yaml
@@ -0,0 +1,18 @@
+---
+api_url: "http://localhost:3001/api"
+#MOCK DRIVER CONFIG:
+mock:
+  user: "mockuser"
+  password: "mockpassword"
+#EC2 DRIVER CONFIG:
+ec2:
+  user: #your EC2 KEY HERE
+  password: #your EC2 SECRET_KEY HERE
+  bucket_locations:
+  #location constraint : provider
+    "EU" : "eu-west-1"
+    "sa-east-1" : "sa-east-1"
+    "us-west-1" : "us-west-1"
+    "us-west-2" : "us-west-2"
+    "ap-southeast-1" : "ap-southeast-1"
+    "ap-northeast-1" : "ap-northeast-1"
diff --git a/tests/deltacloud/hardware_profiles_test.rb b/tests/deltacloud/hardware_profiles_test.rb
new file mode 100644
index 0000000..e69de29
diff --git a/tests/deltacloud/images_test.rb b/tests/deltacloud/images_test.rb
new file mode 100644
index 0000000..e69de29
diff --git a/tests/deltacloud/instance_states_test.rb b/tests/deltacloud/instance_states_test.rb
new file mode 100644
index 0000000..e69de29
diff --git a/tests/deltacloud/instances_test.rb b/tests/deltacloud/instances_test.rb
new file mode 100644
index 0000000..e69de29
diff --git a/tests/deltacloud/keys_test.rb b/tests/deltacloud/keys_test.rb
new file mode 100644
index 0000000..e69de29
diff --git a/tests/deltacloud/realms_test.rb b/tests/deltacloud/realms_test.rb
new file mode 100644
index 0000000..e69de29
diff --git a/tests/deltacloud/storage_snapshots_test.rb b/tests/deltacloud/storage_snapshots_test.rb
new file mode 100644
index 0000000..e69de29
diff --git a/tests/deltacloud/storage_volumes_test.rb b/tests/deltacloud/storage_volumes_test.rb
new file mode 100644
index 0000000..e69de29
diff --git a/tests/deltacloud/test_setup.rb b/tests/deltacloud/test_setup.rb
new file mode 100644
index 0000000..0c7fe6b
--- /dev/null
+++ b/tests/deltacloud/test_setup.rb
@@ -0,0 +1,119 @@
+require 'rubygems'
+require 'minitest/autorun'
+require 'rest_client'
+require 'nokogiri'
+require 'json'
+require 'base64'
+require 'yaml'
+
+#SETUP
+$:.unshift File.join(File.dirname(__FILE__), '..')
+CONFIG = YAML.load(File.open("deltacloud/config.yaml"))
+API_URL = CONFIG["api_url"]
+API_DRIVER = RestClient.get(API_URL) do |response, request, result|
+  Nokogiri::XML(response).root[:driver]
+end
+raise Exception.new("Can't find config for driver: #{API_DRIVER} currently running at #{API_URL} in config.yaml file") unless CONFIG[API_DRIVER] and CONFIG[API_DRIVER]["user"] and CONFIG[API_DRIVER]["password"]
+USER    = CONFIG[API_DRIVER]["user"]
+PASSWORD= CONFIG[API_DRIVER]["password"]
+BASIC_AUTH="Basic #{Base64.encode64(USER+":"+PASSWORD)}"
+
+DRIVERS = RestClient.get(API_URL+"/drivers") do |response, request, result|
+  Nokogiri::XML(response).xpath("//driver/name").inject([]){|res, c| res << c.text.downcase; res}
+end
+API_VERSION = Nokogiri::XML(RestClient.get API_URL).root[:version]
+#SETUP
+
+def xml_response(xml)
+  Nokogiri::XML(xml)
+end
+
+def json_response(json)
+  JSON.parse(json)
+end
+
+def get(params={}, path="", authenticate = false)
+  if authenticate
+    params.merge!({:Authorization=>BASIC_AUTH})
+  end
+  RestClient.get API_URL+path, params
+end
+
+def post(post_body = "", path= "", params={}, authenticate = false)
+  if authenticate
+    params.merge!({:Authorization=>BASIC_AUTH})
+  end
+  RestClient.post API_URL+path, post_body, params
+end
+
+def delete(params={}, path = "", authenticate = true)
+  if authenticate
+    params.merge!({:Authorization=>BASIC_AUTH})
+  end
+  RestClient.delete API_URL+path, params
+end
+
+def options(params={}, path="", authenticate = false)
+  if authenticate
+    params.merge!({:Authorization=>BASIC_AUTH})
+  end
+  RestClient.options API_URL+path, params
+end
+
+#the TEST_FILES hash and deltacloud_test_file_names method
+#which follows is used in the Rakefile for the rake test:deltacloud task
+TEST_FILES =  { :images             => "images_test.rb",
+                :realms             => "realms_test.rb",
+                :hardware_profiles  => "hardware_profiles_test.rb",
+                :instance_states    => "instance_states_test.rb",
+                :instances          => "instances_test.rb",
+                :keys               => "keys_test.rb",
+                :firewalls          => "firewalls_test.rb",
+                :addresses          => "addresses_test.rb",
+                :load_balancers     => "load_balancers_test.rb",
+                :storage_volumes    => "storage_volumes_test.rb",
+                :storage_snapshots  => "storage_snapshots_test.rb",
+                :buckets            => "buckets_test.rb"
+              }
+#gets the list of collections from the server running at API_URL and translates those into file names accoring to TEST_FILES
+def deltacloud_test_file_names
+  driver_collections = xml_response(RestClient.get API_URL, {:accept=>:xml}).xpath("//api/link").inject([]){|res, current| res<<current[:rel].to_sym ;res}
+  driver_collections.inject([]){|res, current| res << "deltacloud/#{TEST_FILES[current]}" if TEST_FILES[current] ;res}
+end
+
+def create_a_bucket_and_blob
+  #random bucket and blob name - make sure starts with letter:
+  bucket_name = random_name
+  blob_name = random_name
+  #create bucket:
+#  res = RestClient.post "#{API_URL}/buckets", {:name=>bucket_name}, {:Authorization=>BASIC_AUTH}
+  res = post({:name=>bucket_name}, "/buckets", {}, true)
+  raise Exception.new("unable to create bucket with name #{bucket_name} for bucket_test.rb") unless res.code == 201
+  #create blob:
+  res = RestClient.put "#{API_URL}/buckets/#{bucket_name}/#{blob_name}", "This is the test blob content", {:Authorization=>BASIC_AUTH, :content_type=>"text/plain", "X-Deltacloud-Blobmeta-Version"=>"1.0", "X-Deltacloud-Blobmeta-Author"=>"herpyderp"}
+  raise Exception.new("unable to create blob with name #{blob_name} for bucket_test.rb") unless res.code == 200
+  return [bucket_name, blob_name]
+end
+
+def random_name
+  name = rand(36**10).to_s(36)
+  name.insert(0, "apitest")
+end
+
+def delete_bucket_and_blob(bucket, blob)
+#  res = RestClient.delete "#{API_URL}/buckets/#{bucket}/#{blob}", {:Authorization=>BASIC_AUTH}
+  res = delete({}, "/buckets/#{bucket}/#{blob}")
+  raise Exception.new("unable to delete blob with name #{blob} for bucket_test.rb") unless res.code == 204
+#  res = RestClient.delete "#{API_URL}/buckets/#{bucket}", {:Authorization=>BASIC_AUTH}
+  res = delete({}, "/buckets/#{bucket}")
+  raise Exception.new("unable to delete bucket with name #{bucket} for bucket_test.rb") unless res.code == 204
+end
+
+def discover_features
+  res = xml_response(get)
+  features_hash = res.xpath("//api/link").inject({}) do |result, collection|
+    result.merge!({collection[:rel] => []})
+    collection.children.inject([]){|features, current_child| result[collection[:rel]] << current_child[:name] if current_child.name == "feature"}
+    result
+  end
+end
-- 
1.7.6.5