Return-Path: X-Original-To: apmail-deltacloud-dev-archive@www.apache.org Delivered-To: apmail-deltacloud-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 12817C84B for ; Mon, 21 May 2012 10:07:51 +0000 (UTC) Received: (qmail 80174 invoked by uid 500); 21 May 2012 10:07:51 -0000 Delivered-To: apmail-deltacloud-dev-archive@deltacloud.apache.org Received: (qmail 80150 invoked by uid 500); 21 May 2012 10:07:50 -0000 Mailing-List: contact dev-help@deltacloud.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@deltacloud.apache.org Delivered-To: mailing list dev@deltacloud.apache.org Received: (qmail 80142 invoked by uid 99); 21 May 2012 10:07:50 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 May 2012 10:07:50 +0000 X-ASF-Spam-Status: No, hits=-5.0 required=5.0 tests=RCVD_IN_DNSWL_HI,SPF_HELO_PASS,SPF_PASS,T_FILL_THIS_FORM_SHORT,T_FRT_PROFIT2 X-Spam-Check-By: apache.org Received-SPF: pass (nike.apache.org: domain of mfojtik@redhat.com designates 209.132.183.28 as permitted sender) Received: from [209.132.183.28] (HELO mx1.redhat.com) (209.132.183.28) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 May 2012 10:07:35 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q4LA7Dhh016789 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 21 May 2012 06:07:13 -0400 Received: from dhcp-29-121.brq.redhat.com (dhcp-29-121.brq.redhat.com [10.34.29.121]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q4LA6sJv020488 for ; Mon, 21 May 2012 06:07:12 -0400 From: mfojtik@redhat.com To: dev@deltacloud.apache.org Subject: [PATCH core 17/51] Core: Replaced Test/Unit based tests for Mock with new minitest Date: Mon, 21 May 2012 12:06:58 +0200 Message-Id: <1337594852-42550-18-git-send-email-mfojtik@redhat.com> In-Reply-To: <1337594852-42550-1-git-send-email-mfojtik@redhat.com> References: <1337594852-42550-1-git-send-email-mfojtik@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 From: Michal Fojtik Signed-off-by: Michal fojtik --- server/Rakefile | 42 +- .../deltacloud/drivers/mock/data/keys/test-key.yml | 28 + server/tests/drivers/mock/api_test.rb | 210 ++++---- server/tests/drivers/mock/buckets_test.rb | 192 +++++++ server/tests/drivers/mock/common.rb | 58 +++ server/tests/drivers/mock/drivers_test.rb | 120 +++++ .../tests/drivers/mock/hardware_profiles_test.rb | 297 +++++++---- server/tests/drivers/mock/images_test.rb | 268 ++++++---- server/tests/drivers/mock/instance_states_test.rb | 67 --- server/tests/drivers/mock/instances_test.rb | 545 ++++++++++++-------- server/tests/drivers/mock/keys_test.rb | 158 ++++++ server/tests/drivers/mock/realms_test.rb | 180 ++++--- server/tests/drivers/mock/setup.rb | 3 - .../tests/drivers/mock/storage_snapshots_test.rb | 111 ++++ server/tests/drivers/mock/storage_volumes_test.rb | 119 +++++ server/tests/drivers/mock/url_for_test.rb | 67 --- 16 files changed, 1684 insertions(+), 781 deletions(-) create mode 100644 server/lib/deltacloud/drivers/mock/data/keys/test-key.yml create mode 100644 server/tests/drivers/mock/buckets_test.rb create mode 100644 server/tests/drivers/mock/common.rb create mode 100644 server/tests/drivers/mock/drivers_test.rb delete mode 100644 server/tests/drivers/mock/instance_states_test.rb create mode 100644 server/tests/drivers/mock/keys_test.rb delete mode 100644 server/tests/drivers/mock/setup.rb create mode 100644 server/tests/drivers/mock/storage_snapshots_test.rb create mode 100644 server/tests/drivers/mock/storage_volumes_test.rb delete mode 100644 server/tests/drivers/mock/url_for_test.rb diff --git a/server/Rakefile b/server/Rakefile index 5d205ce..50971f5 100644 --- a/server/Rakefile +++ b/server/Rakefile @@ -71,26 +71,6 @@ begin rescue LoadError end -namespace :test do - %w(mock rackspace rhevm openstack google fgcp).each do |driver| - desc "Run #{driver} unit tests" - Rake::TestTask.new(driver) { |t| - t.test_files = ['tests/common.rb', "tests/drivers/#{driver}/setup.rb"] + FileList.new("tests/drivers/#{driver}/*_test.rb") + FileList.new('tests/rabbit_test.rb') - t.options = "-v -v" - t.verbose = true - t.warning = false - } - end - - desc "Run CIMI frontend tests" - Rake::TestTask.new "cimi" do |t| - t.test_files = ["tests/cimi/cimi.rb", "tests/cimi/common/*_test.rb"] - t.options = "-v -v" - t.verbose = true - t.warning = false - end - -end desc "Call our Test::Unit suite" task :test do @@ -244,3 +224,25 @@ namespace :rabbit do end +namespace :test do + + %w(mock rackspace rhevm openstack google fgcp).each do |driver| + desc "Run #{driver} unit tests" + Rake::TestTask.new(driver) { |t| + Rake::Task["mock:fixtures:reset"].invoke + t.test_files = ['tests/drivers/'+driver+'/common.rb'] + FileList.new("tests/drivers/#{driver}/*_test.rb") + t.options = "-v -v" + t.verbose = true + t.warning = false + } + end + + desc "Run CIMI frontend tests" + Rake::TestTask.new "cimi" do |t| + t.test_files = ["tests/cimi/cimi.rb", "tests/cimi/common/*_test.rb"] + t.options = "-v -v" + t.verbose = true + t.warning = false + end + +end diff --git a/server/lib/deltacloud/drivers/mock/data/keys/test-key.yml b/server/lib/deltacloud/drivers/mock/data/keys/test-key.yml new file mode 100644 index 0000000..a643917 --- /dev/null +++ b/server/lib/deltacloud/drivers/mock/data/keys/test-key.yml @@ -0,0 +1,28 @@ +--- +:credential_type: :key +:fingerprint: 5e:ce:b6:dc:59:3b:5c:93:f8:2e:9d:20:ce:60:ca:f5:0b:8a:66:93 +:pem_rsa_key: |- + -----BEGIN RSA PRIVATE KEY----- + P9mRXOY7p2SmMzTGA6dwKxUp1NB8LNCIJ7sMGgAljsf=ToAi9qn9myx0EQJkE8FZ8FigUIMHS/T + 8EwP7Ayjztb8dczbC6sb/Ep2UWcegNUVHimyHstaEaO/3dCaFwLJ/kw=laAfLQAVj4sIr8EHDTg + /BFkgmwTAYlS/ybkEfO9J7AJlY6/agwYzDWp+VGAD9rMsl2EkkbkWdoTX4Aob9RqyHaFi2m1AAw + 2nhhqYpa1W4H=PJvyBcsXT3JynowSI8rTvo41oVwgSzv7YofGP0yV7BePm5pXZUUP2ZMByxbAUv + jvYRN/cMHbC6RW1ezR3uehCKdKFRXLTkoivoGj4ugrKgOwQP0HWI2orx/NW+6vYBxyCKiTJPZcK + x4BlRrlgvPST/7eaFv7/5Pqc3jWcp+bRC0qyYqQT9iq3gGNoc4ABFTI7zCeZ3p9tK8oje5fWo5m + 54P32hVGeBjfqT/MrEYbY5gbJU6LejCj7x6Ozlp4iHQtrYNhiZ0iP0W3nRhVFQHamKx9aoBXyeg + LLGxBOr+TfaeeBXRkXiaMuWoyPSzUQwWmaJhm0sjHf7e/iKiUggZkOHQ/eF9MWI4M+4wvyepfS0 + 5vl2Ql/2rXv+Mx+c4cx1fjBhRrMPcGKmHGjNMjPyamTrlqueFRJYP45AYABP2U2AsNxoPfEG0qu + ki3DJOeC5x/03nODd=hQLzfdiQ3Yyt0GMw1EQN96cPaRtnjr3U4/ngxt0Fi6o7Z8E2+Uh5t4n8D + h0exXCOlOi9BDsJJz677mga/=5Sin/4Cw8=D8O1FHrWoA4ZQbWFE71F=/29PM90RHJf2bjgk2WF + piltKwVfGAxPOTcpmf=J+V3NHgT/EawMPHuEmwgNvx6smDBUgJaw0QYX/XG5xuiQ7HTkffJN6Cm + 6D4WCJPZUvO1r+v=T9v7Qu4j9ue/l2WwVZuvQsVD67jpzq2R72EHna6rcwwyMcdAlwikP9nzJIL + Ale7hQAWHIEeAvAxtwxEMSfTkuLQcD=i0ORysmInDxdORw4ue2YThj2Id/jmUy6IiEqMYeVpiRq + 6spq2ukt=+HHn6aBcYWbsD=e8/wOk0X0=ixZ0HF+xqYgsiiAk==rA4QEgrf+5djbIRZk1wegeIO + po/HZdF4qk32cKBjrrel2AzxfZeGxWNX7ObAE4HACXi3eSdcnm1fIHsoSC+1eDqFkfAIve3Dj/a + afZxrda6zzp3g6IPcHAqleCn7XNcS0v5tk4Fag8Wr5Wq7IipRfixAs+GESGiyugeRvZWN2mtDOL + CGHGGAbpvplw2vjdryVyj7P6bVcwLNgl0t1ufZBaGRBpyontJ1/UQQMew7e2lW=EZr/GxHke8HN + X5vIw9ssx8=LL00fxAuX9SRdcrtVyTYGXORXe9NnldXjBXmLPgwqJAjoBTjTBQxzrQOtdla=/yw + MsDlFWumPz1HAFw7R5zS2VCHrwkLDm=h7k3y+fUvYOx6IYf+MmevANuJT+2qY6s/ilTBNDYq6jJ + 8LYpsBo4XpQm1ZleFCIyRldHfmaC5EMxkVQVqCV7X9I6JgzDEetUre25LQTpDa31M=ucVHNWlT+ + 6rjiLETNeMTWGcuIkLPe/PElmp4llKeFi6g2=E2AKeSDzNycr5eXHEnBuKfEnENXXo6n-----END RSA PRIVATE KEY----- +:id: test-key diff --git a/server/tests/drivers/mock/api_test.rb b/server/tests/drivers/mock/api_test.rb index cfb0921..3308365 100644 --- a/server/tests/drivers/mock/api_test.rb +++ b/server/tests/drivers/mock/api_test.rb @@ -1,133 +1,115 @@ -# 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. -# - -$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..') -require 'tests/common' - -module DeltacloudUnitTest - class ApiTest < Test::Unit::TestCase - include Rack::Test::Methods - - def app - Sinatra::Application - end +describe 'Deltacloud API' do + include Deltacloud::Test - def test_it_returns_entry_points - get_auth_url '/api' - (last_xml_response/'/api/link').length.should > 0 - end + it 'return HTTP_OK when accessing API entrypoint' do + get API_ROOT_URL + last_response.status.must_equal 200 + end - def test_it_has_correct_attributes_set - get_auth_url '/api' - (last_xml_response/'/api/link').each do |link| - link.attributes.keys.sort.should == [ 'href', 'rel' ] - end - end + it 'advertise the current driver in API entrypoint' do + get API_ROOT_URL + xml_response.root[:driver].must_equal ENV['API_DRIVER'] + end - def test_it_responses_to_html - get_url '/api', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' - end + it 'advertise the current API version in API entrypoint' do + get API_ROOT_URL + xml_response.root[:version].must_equal API_VERSION + end - def test_it_responses_to_json - get_url '/api', {}, { :format => :json } - last_response.status.should == 200 - JSON::parse(last_response.body).class.should == Hash - JSON::parse(last_response.body)['api'].class.should == Hash - end + it 'advertise the current API version in HTTP headers' do + get API_ROOT_URL + last_response.headers['Server'].must_equal "Apache-Deltacloud/#{API_VERSION}" + end - def test_it_switches_drivers - with_provider("") do - get_auth_url '/api' - (last_xml_response/"api/link[rel = 'instances']").first.should_not == nil - end - - # Switch to storage-only mock driver - with_provider("storage") do - get_auth_url '/api' - (last_xml_response/"api/link[rel = 'instances']").first.should == nil - end - end + it 'must include the ETag in HTTP headers' do + get API_ROOT_URL + last_response.headers['ETag'].wont_be_nil + end - def test_it_handles_unsupported_collections - get_auth_url '/api/no_such_collection' - last_response.status.should == 404 + it 'advertise collections in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link').wont_be_empty + end - with_provider("storage") do - get_auth_url '/api/instances' - last_response.status.should == 403 - end + it 'include the :href and :rel attribute for each collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link').each do |collection| + collection[:href].wont_be_nil + collection[:rel].wont_be_nil end + end - def test_it_allows_accessing_docs - get_url '/api/docs/instances' - last_response.status.should == 200 - - with_provider("storage") do - get_url '/api/docs/instances' - last_response.status.should == 403 - end + it 'uses the absolute URI in the :href attribute for each collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link').each do |collection| + collection[:href].must_match /^http/ end + end - def test_it_respond_to_head - head '/api/instances' - last_response.headers['Allow'].should_not == nil - last_response.headers['Allow'].split(',').include?('HEAD').should == true - end + it 'advertise features for some collections in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link/feature').wont_be_empty + end - def test_it_expose_available_drivers - get_auth_url '/api/drivers' - last_response.status.should == 200 - (last_xml_response/"drivers").length.should > 0 - (last_xml_response/'drivers/driver').length.should > 0 - (last_xml_response/"drivers/driver[@id = 'mock']").length.should == 1 + it 'advertise the name of the feature for some collections in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link/feature').each do |f| + f[:name].wont_be_nil end + end - def test_it_expose_ec2_driver_entrypoints - get_auth_url '/api/drivers' - last_response.status.should == 200 - ec2 = (last_xml_response/'drivers/driver[@id=ec2]').first - (ec2/"provider").length.should > 0 - (ec2/"provider[@id = 'eu-west-1']").length.should == 1 - get_auth_url ec2[:href] - eu_west = (last_xml_response/"provider[@id = 'eu-west-1']").first - (eu_west/"entrypoint").length.should > 0 - (eu_west/"entrypoint[@kind = 'ec2']").length.should == 1 - end + it 'must change the media type from XML to JSON using Accept headers' do + header 'Accept', 'application/json' + get API_ROOT_URL + last_response.headers['Content-Type'].must_equal 'application/json' + end - def test_it_supports_matrix_params - get_auth_url "/api;driver=ec2" - last_response.status.should == 200 - (last_xml_response/'api').first[:driver].should == 'ec2' - get_auth_url "/api;driver=mock" - (last_xml_response/'api').first[:driver].should == 'mock' - get_auth_url "/api;driver=ec2/hardware_profiles" - (last_xml_response/'hardware_profiles/hardware_profile/@id').map {|n| n.to_s}.include?('m1.small').should == true - last_response.status.should == 200 - end + it 'must change the media type to JSON using the "?format" parameter in URL' do + get API_ROOT_URL, { :format => 'json' } + last_response.headers['Content-Type'].must_equal 'application/json' + end - def test_it_change_features_after_driver_change - get_auth_url "/api;driver=ec2" - (last_xml_response/'api/link[@rel="instances"]/feature[@name="user_name"]').first.should == nil - (last_xml_response/'api/link[@rel="instances"]/feature[@name="user_data"]').first.should_not == nil - get_auth_url "/api;driver=mock" - (last_xml_response/'api/link[@rel="instances"]/feature[@name="user_name"]').first.should_not == nil - (last_xml_response/'api/link[@rel="instances"]/feature[@name="firewalls"]').first.should == nil - end + it 'must change the driver when using X-Deltacloud-Driver HTTP header' do + header 'X-Deltacloud-Driver', 'ec2' + get API_ROOT_URL + xml_response.root[:driver].must_equal 'ec2' + header 'X-Deltacloud-Driver', 'mock' + get API_ROOT_URL + xml_response.root[:driver].must_equal 'mock' + end + it 'must change the features when driver is swapped using HTTP headers' do + header 'X-Deltacloud-Driver', 'ec2' + get API_ROOT_URL + # The 'user_name' feature is not supported currently for the EC2 driver + (xml_response/'api/link/feature').map { |f| f[:name] }.wont_include 'user_name' + header 'X-Deltacloud-Driver', 'mock' + get API_ROOT_URL + # But it's supported in Mock driver + (xml_response/'api/link/feature').map { |f| f[:name] }.must_include 'user_name' end + + it 'must re-validate the driver credentials when using "?force_auth" parameter in URL' do + get API_ROOT_URL, { :force_auth => '1' } + last_response.status.must_equal 401 + auth_as_mock + get API_ROOT_URL, { :force_auth => '1' } + last_response.status.must_equal 200 + end + + it 'must change the API PROVIDER using the /api;provider matrix parameter in URI' do + get API_ROOT_URL + ';provider=test1' + xml_response.root[:provider].wont_be_nil + xml_response.root[:provider].must_equal 'test1' + get API_ROOT_URL + ';provider=test2' + xml_response.root[:provider].must_equal 'test2' + end + + it 'must change the API DRIVER using the /api;driver matrix parameter in URI' do + get API_ROOT_URL + ';driver=ec2' + xml_response.root[:driver].must_equal 'ec2' + get API_ROOT_URL + ';driver=mock' + xml_response.root[:driver].must_equal 'mock' + end + end diff --git a/server/tests/drivers/mock/buckets_test.rb b/server/tests/drivers/mock/buckets_test.rb new file mode 100644 index 0000000..f98ad24 --- /dev/null +++ b/server/tests/drivers/mock/buckets_test.rb @@ -0,0 +1,192 @@ +describe 'Deltacloud API buckets' do + include Deltacloud::Test + + it 'must advertise have the buckets collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link[@rel=buckets]').wont_be_empty + end + + it 'must require authentication to access the "bucket" collection' do + get collection_url(:buckets) + last_response.status.must_equal 401 + end + + it 'should respond with HTTP_OK when accessing the :buckets collection with authentication' do + auth_as_mock + get collection_url(:buckets) + last_response.status.must_equal 200 + end + + it 'should support the JSON media type' do + auth_as_mock + header 'Accept', 'application/json' + get collection_url(:buckets) + last_response.status.must_equal 200 + last_response.headers['Content-Type'].must_equal 'application/json' + end + + it 'must include the ETag in HTTP headers' do + auth_as_mock + get collection_url(:buckets) + last_response.headers['ETag'].wont_be_nil + end + + it 'must have the "buckets" element on top level' do + auth_as_mock + get collection_url(:buckets) + xml_response.root.name.must_equal 'buckets' + end + + it 'must have some "bucket" elements inside "buckets"' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').wont_be_empty + end + + it 'must provide the :id attribute for each bucket in collection' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + r[:id].wont_be_nil + end + end + + it 'must include the :href attribute for each "bucket" element in collection' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + r[:href].wont_be_nil + end + end + + it 'must use the absolute URL in each :href attribute' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + r[:href].must_match /^http/ + end + end + + it 'must have the URL ending with the :id of the bucket' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + r[:href].must_match /#{r[:id]}$/ + end + end + + it 'must return the list of valid parameters for the :index action' do + auth_as_mock + options collection_url(:buckets) + '/index' + last_response.headers['Allow'].wont_be_nil + end + + it 'must have the "name" element defined for each bucket in collection' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + (r/'name').wont_be_nil + end + end + + it 'must have the "state" element defined for each bucket in collection' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + (r/'state').wont_be_nil + end + end + + it 'must return the full "bucket" when following the URL in bucket element' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + get collection_url(:buckets) + '/' + r[:id] + last_response.status.must_equal 200 + end + end + + it 'must have the "name" element for the bucket and it should match with the one in collection' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + get collection_url(:buckets) + '/' + r[:id] + (xml_response/'name').wont_be_empty + (xml_response/'name').first.text.must_equal((r/'name').first.text) + end + end + + it 'must have the "size" element for the bucket and it should match with the one in collection' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + get collection_url(:buckets) + '/' + r[:id] + (xml_response/'size').wont_be_empty + (xml_response/'size').first.text.must_equal((r/'size').first.text) + end + end + + it 'must have the "blob" elements for the bucket and it should match with the ones in collection' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + get collection_url(:buckets) + '/' + r[:id] + (xml_response/'bucket/blob').wont_be_empty + (xml_response/'bucket/blob').each do |b| + b[:id].wont_be_nil + b[:href].wont_be_nil + b[:href].must_match /^http/ + b[:href].must_match /#{r[:id]}\/#{b[:id]}$/ + end + end + end + + it 'must have the "blob" elements for the bucket and it should match with the ones in collection' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + get collection_url(:buckets) + '/' + r[:id] + (xml_response/'bucket/blob').wont_be_empty + (xml_response/'bucket/blob').each do |b| + b[:id].wont_be_nil + b[:href].wont_be_nil + b[:href].must_match /^http/ + b[:href].must_match /#{r[:id]}\/#{b[:id]}$/ + end + end + end + + it 'must allow to get all blobs details and the details should be set correctly' do + auth_as_mock + get collection_url(:buckets) + (xml_response/'buckets/bucket').each do |r| + get collection_url(:buckets) + '/' + r[:id] + (xml_response/'bucket/blob').each do |b| + get collection_url(:buckets) + '/' + r[:id] + '/' + b[:id] + xml_response.root.name.must_equal 'blob' + xml_response.root[:id].must_equal b[:id] + (xml_response/'bucket').wont_be_empty + (xml_response/'bucket').size.must_equal 1 + (xml_response/'bucket').first[:id].wont_be_nil + (xml_response/'bucket').first[:href].wont_be_nil + (xml_response/'content_length').wont_be_empty + (xml_response/'content_length').size.must_equal 1 + (xml_response/'content_length').first.text.must_match /^(\d+)$/ + (xml_response/'content_type').wont_be_empty + (xml_response/'content_type').size.must_equal 1 + (xml_response/'content_type').first.text.wont_be_empty + (xml_response/'last_modified').wont_be_empty + (xml_response/'last_modified').size.must_equal 1 + (xml_response/'last_modified').first.text.wont_be_empty + (xml_response/'content').wont_be_empty + (xml_response/'content').size.must_equal 1 + (xml_response/'content').first[:rel].wont_be_nil + (xml_response/'content').first[:rel].must_equal 'blob_content' + (xml_response/'content').first[:href].wont_be_nil + (xml_response/'content').first[:href].must_match /^http/ + (xml_response/'content').first[:href].must_match /\/content$/ + end + end + end + +end diff --git a/server/tests/drivers/mock/common.rb b/server/tests/drivers/mock/common.rb new file mode 100644 index 0000000..08650a3 --- /dev/null +++ b/server/tests/drivers/mock/common.rb @@ -0,0 +1,58 @@ +unless Kernel.respond_to?(:require_relative) + module Kernel + def require_relative(path) + require File.join(File.dirname(caller[0]), path.to_str) + end + end +end + +API_ROOT_URL = "/api" unless defined?(API_ROOT_URL) +API_VERSION = "1.0.0" unless defined?(API_VERSION) +ENV['API_DRIVER'] ||= 'mock' + +ENV['API_USERNAME'] ||= 'mockuser' +ENV['API_PASSWORD'] ||= 'mockpassword' + +require_relative '../../../lib/deltacloud/server.rb' + +require 'minitest/autorun' +require 'rack/test' +require 'nokogiri' +require 'json' + +require 'pp' + +module Deltacloud + module Test + include Rack::Test::Methods + + def included?(sub) + sub.class_eval do + before do + header 'Accept', 'application/xml' + end + end + end + + def xml_response + Nokogiri::XML(last_response.body) + end + + def auth_as_mock + authorize ENV['API_USERNAME'], ENV['API_PASSWORD'] + end + + def collection_url(collection) + [API_ROOT_URL, collection.to_s].join('/') + end + + def app + Rack::Builder.new { + map '/' do + use Rack::Static, :urls => ["/stylesheets", "/javascripts"], :root => "public" + run Rack::Cascade.new([Deltacloud::API]) + end + } + end + end +end diff --git a/server/tests/drivers/mock/drivers_test.rb b/server/tests/drivers/mock/drivers_test.rb new file mode 100644 index 0000000..41c2e66 --- /dev/null +++ b/server/tests/drivers/mock/drivers_test.rb @@ -0,0 +1,120 @@ +describe 'Deltacloud API drivers' do + include Deltacloud::Test + + it 'must advertise have the drivers collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link[@rel=drivers]').wont_be_empty + end + + it 'must not require authentication to access the "driver" collection' do + get collection_url(:drivers) + last_response.status.must_equal 200 + end + + it 'should respond with HTTP_OK when accessing the :drivers collection with authentication' do + get collection_url(:drivers) + last_response.status.must_equal 200 + end + + it 'should support the JSON media type' do + header 'Accept', 'application/json' + get collection_url(:drivers) + last_response.status.must_equal 200 + last_response.headers['Content-Type'].must_equal 'application/json' + end + + it 'must include the ETag in HTTP headers' do + get collection_url(:drivers) + last_response.headers['ETag'].wont_be_nil + end + + it 'must have the "drivers" element on top level' do + get collection_url(:drivers) + xml_response.root.name.must_equal 'drivers' + end + + it 'must have some "driver" elements inside "drivers"' do + get collection_url(:drivers) + (xml_response/'drivers/driver').wont_be_empty + end + + it 'must provide the :id attribute for each driver in collection' do + get collection_url(:drivers) + (xml_response/'drivers/driver').each do |r| + r[:id].wont_be_nil + end + end + + it 'must include the :href attribute for each "driver" element in collection' do + get collection_url(:drivers) + (xml_response/'drivers/driver').each do |r| + r[:href].wont_be_nil + end + end + + it 'must use the absolute URL in each :href attribute' do + get collection_url(:drivers) + (xml_response/'drivers/driver').each do |r| + r[:href].must_match /^http/ + end + end + + it 'must have the URL ending with the :id of the driver' do + get collection_url(:drivers) + (xml_response/'drivers/driver').each do |r| + r[:href].must_match /#{r[:id]}$/ + end + end + + it 'must return the list of valid parameters for the :index action' do + options collection_url(:drivers) + '/index' + last_response.headers['Allow'].wont_be_nil + end + + it 'must have the "name" element defined for each driver in collection' do + get collection_url(:drivers) + (xml_response/'drivers/driver').each do |r| + (r/'name').wont_be_nil + end + end + + + it 'must return the full "driver" when following the URL in driver element' do + get collection_url(:drivers) + (xml_response/'drivers/driver').each do |r| + get collection_url(:drivers) + '/' + r[:id] + last_response.status.must_equal 200 + end + end + + it 'must have the "name" element for the driver and it should match with the one in collection' do + get collection_url(:drivers) + (xml_response/'drivers/driver').each do |r| + get collection_url(:drivers) + '/' + r[:id] + (xml_response/'name').wont_be_empty + (xml_response/'name').first.text.must_equal((r/'name').first.text) + end + end + + it 'should advertise available providers for some drivers' do + get collection_url(:drivers) + (xml_response/'drivers/driver/provider').each do |p| + p[:id].wont_be_nil + end + end + + it 'should expose entrypoints for each provider if driver has providers defined' do + get collection_url(:drivers) + (xml_response/'drivers/driver/provider').each do |p| + get collection_url(:drivers) + '/' + p.parent[:id] + (xml_response/"driver/provider[@id=#{p[:id]}]").wont_be_empty + (xml_response/"driver/provider[@id=#{p[:id]}]").size.must_equal 1 + (xml_response/"driver/provider[@id=#{p[:id]}]/entrypoint").wont_be_empty + (xml_response/"driver/provider[@id=#{p[:id]}]/entrypoint").each do |e| + e[:kind].wont_be_nil + e.text.wont_be_empty + end + end + end + +end diff --git a/server/tests/drivers/mock/hardware_profiles_test.rb b/server/tests/drivers/mock/hardware_profiles_test.rb index 47f7eb3..3dad5a8 100644 --- a/server/tests/drivers/mock/hardware_profiles_test.rb +++ b/server/tests/drivers/mock/hardware_profiles_test.rb @@ -1,134 +1,221 @@ -# 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. -# - -$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..') -require 'tests/common' - -module DeltacloudUnitTest - class HardwareProfilesTest < Test::Unit::TestCase - include Rack::Test::Methods - - def app - Sinatra::Application - end - - def test_it_returns_hardware_profiles - get_url '/api/hardware_profiles' - (last_xml_response/'hardware_profiles/hardware_profile').length.should > 0 - end - - def test_it_has_correct_attributes_set - get_auth_url '/api/hardware_profiles' - (last_xml_response/'hardware_profiles/hardware_profile').each do |profile| - profile.attributes.keys.sort.should == [ 'href', 'id' ] - end +describe 'Deltacloud API Hardware Profiles' do + include Deltacloud::Test + + it 'must advertise have the hardware_profiles collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link[@rel=hardware_profiles]').wont_be_empty + end + + it 'should respond with HTTP_OK when accessing the :hardware_profiles collection with authentication' do + auth_as_mock + get collection_url(:hardware_profiles) + last_response.status.must_equal 200 + end + + it 'should support the JSON media type' do + auth_as_mock + header 'Accept', 'application/json' + get collection_url(:hardware_profiles) + last_response.status.must_equal 200 + last_response.headers['Content-Type'].must_equal 'application/json' + end + + it 'must include the ETag in HTTP headers' do + auth_as_mock + get collection_url(:hardware_profiles) + last_response.headers['ETag'].wont_be_nil + end + + it 'must have the "hardware_profiles" element on top level' do + auth_as_mock + get collection_url(:hardware_profiles) + xml_response.root.name.must_equal 'hardware_profiles' + end + + it 'must have some "hardware_profile" elements inside "hardware_profiles"' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').wont_be_empty + end + + it 'must provide the :id attribute for each hardware_profile in collection' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + r[:id].wont_be_nil end + end - def test_hardware_profiles_have_name - get_auth_url '/api/hardware_profiles' - (last_xml_response/'hardware_profiles/hardware_profile').each do |profile| - (profile/'name').text.should_not == nil - end + it 'must include the :href attribute for each "hardware_profile" element in collection' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + r[:href].wont_be_nil end + end - def test_hardware_profiles_have_unique_name - get_auth_url '/api/hardware_profiles' - names = [] - (last_xml_response/'hardware_profiles/hardware_profile').each do |profile| - names << (profile/'name').text - end - names.should == names.uniq + it 'must use the absolute URL in each :href attribute' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + r[:href].must_match /^http/ end + end - def test_hardware_profiles_have_unique_id - get_auth_url '/api/hardware_profiles' - ids = [] - (last_xml_response/'hardware_profiles/hardware_profile').each do |profile| - ids << profile['id'] - end - ids.should == ids.uniq + it 'must have the URL ending with the :id of the hardware_profile' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + r[:href].must_match /#{r[:id]}$/ end + end - def test_m1_xlarge_profile_has_correct_attributes - get_auth_url '/api/hardware_profiles' - profile = (last_xml_response/'hardware_profiles/hardware_profile[@id="m1-xlarge"]') - test_profile_properties(profile) + it 'must return the list of valid parameters for the :index action' do + auth_as_mock + options collection_url(:hardware_profiles) + '/index' + last_response.headers['Allow'].wont_be_nil + end + + it 'must have the "name" element defined for each hardware_profile in collection' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + (r/'name').wont_be_empty end + end - def test_it_returns_valid_hardware_profile - get_auth_url '/api/hardware_profiles/m1-xlarge' - profile = (last_xml_response/'hardware_profile') - test_profile_properties(profile) + it 'should have the "property" element defined if not the opaque hardware_profile' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').wont_be_empty end + end - def test_it_responses_to_json - get_url '/api/hardware_profiles', {}, { :format => :json } - JSON::parse(last_response.body).class.should == Hash - JSON::parse(last_response.body)['hardware_profiles'].class.should == Array - get_url '/api/hardware_profiles/m1-xlarge', {}, { :format => :json } - last_response.status.should == 200 - JSON::parse(last_response.body).class.should == Hash - JSON::parse(last_response.body)['hardware_profile'].class.should == Hash + it 'must define the :kind attribute for each "property" ' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').each { |p| p[:kind].wont_be_nil } end + end - def test_it_responses_to_html - get_url '/api/hardware_profiles', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' - get_url '/api/hardware_profiles/m1-xlarge', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' + it 'must define the :name attribute for each "property" ' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').each { |p| p[:name].wont_be_nil } end + end - def test_it_returns_error_on_wrong_name - get_url '/api/hardware_profiles/m1-unknown-wrongname', {}, { :format => :html } - last_response.status.should == 404 - get_auth_url '/api/hardware_profiles/m1-unknown-wrongname' - last_response.status.should == 404 - get_url '/api/hardware_profiles/m1-unknown-wrongname', {}, { :format => :json } - last_response.status.should == 404 + it 'must define the :unit attribute for each "property" ' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').each { |p| p[:unit].wont_be_nil } end + end - private + it 'must define the :value attribute for each "property" ' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').each { |p| p[:value].wont_be_nil } + end + end - def test_profile_properties(profile) - (profile/'property').each do |properties| - properties.attributes.keys.sort.should == [ 'kind', 'name', 'unit', 'value' ] + it 'must define the "param" element if property kind is not "fixed"' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').each do |p| + next if p[:kind] == 'fixed' + (p/'param').wont_be_empty + (p/'param').size.must_equal 1 + (p/'param').first[:href].wont_be_nil + (p/'param').first[:href].must_match /^http/ + (p/'param').first[:method].wont_be_nil + (p/'param').first[:name].wont_be_nil + (p/'param').first[:operation].wont_be_nil end + end + end - (profile/'property[@name="architecture"]').first['kind'].should == 'fixed' - (profile/'property[@name="architecture"]').first['unit'].should == 'label' + it 'must provide the list of valid values when the property is defined as "enum"' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').each do |p| + next if p[:kind] != 'enum' + (p/'enum/entry').wont_be_empty + (p/'enum/entry').each { |e| e[:value].wont_be_nil } + end + end + end - (profile/'property[@name="memory"]').first['kind'].should == 'range' - (profile/'property[@name="memory"]').first['unit'].should == 'MB' - (profile/'property[@name="memory"]/range').length.should == 1 - (profile/'property[@name="memory"]/range').first.attributes.keys.sort.should == [ 'first', 'last' ] + it 'must provide the range of valid values when the property is defined as "range"' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').each do |p| + next if p[:kind] != 'range' + (p/'range').wont_be_empty + (p/'range').size.must_equal 1 + (p/'range').first[:first].wont_be_nil + (p/'range').first[:last].wont_be_nil + end + end + end - (profile/'property[@name="cpu"]').first['kind'].should == 'fixed' - (profile/'property[@name="cpu"]').first['unit'].should == 'count' + it 'must provide the default value within the range if property defined as "range"' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').each do |p| + next if p[:kind] != 'range' + ((p/'range').first[:first].to_i..(p/'range').first[:last].to_i).include?(p[:value].to_i).must_equal true + end + end + end - (profile/'property[@name="storage"]').first['kind'].should == 'enum' - (profile/'property[@name="storage"]').first['unit'].should == 'GB' - (profile/'property[@name="storage"]/enum').length.should == 1 - (profile/'property[@name="storage"]/enum/entry').length.should == 3 - (profile/'property[@name="storage"]/enum/entry').each do |entry| - entry.attributes.keys.should == [ 'value' ] - entry['value'].should_not == nil + it 'must provide the default value that is included in enum list if property defined as "enum"' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + next if r[:id] == 'opaque' + (r/'property').each do |p| + next if p[:kind] != 'enum' + (p/'enum/entry').map { |e| e[:value] }.include?(p[:value]).must_equal true end end + end + it 'must return the full "hardware_profile" when following the URL in hardware_profile element' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + get collection_url(:hardware_profiles) + '/' + r[:id] + last_response.status.must_equal 200 + end end + + it 'must have the "name" element for the hardware_profile and it should match with the one in collection' do + auth_as_mock + get collection_url(:hardware_profiles) + (xml_response/'hardware_profiles/hardware_profile').each do |r| + get collection_url(:hardware_profiles) + '/' + r[:id] + (xml_response/'name').wont_be_empty + (xml_response/'name').first.text.must_equal((r/'name').first.text) + end + end + end diff --git a/server/tests/drivers/mock/images_test.rb b/server/tests/drivers/mock/images_test.rb index 47fb690..3faf752 100644 --- a/server/tests/drivers/mock/images_test.rb +++ b/server/tests/drivers/mock/images_test.rb @@ -1,138 +1,194 @@ -# 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. -# - -$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..') -require 'tests/common' - -module DeltacloudUnitTest - class HardwareProfilesTest < Test::Unit::TestCase - include Rack::Test::Methods - - def app - Sinatra::Application - end +describe 'Deltacloud API Images' do + include Deltacloud::Test - def test_it_require_authentication - require_authentication?('/api/images').should == true - end + it 'must advertise have the images collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link[@rel=images]').wont_be_empty + end - def test_it_returns_images - get_auth_url '/api/images', {} - (last_xml_response/'images/image').length.should > 0 - end + it 'must require authentication to access the "image" collection' do + get collection_url(:images) + last_response.status.must_equal 401 + end - def test_it_has_correct_attributes_set - get_auth_url '/api/images', {} - (last_xml_response/'images/image').each do |image| - image.attributes.keys.sort.should == [ 'href', 'id' ] - end + it 'should respond with HTTP_OK when accessing the :images collection with authentication' do + auth_as_mock + get collection_url(:images) + last_response.status.must_equal 200 + end + + it 'should support the JSON media type' do + auth_as_mock + header 'Accept', 'application/json' + get collection_url(:images) + last_response.status.must_equal 200 + last_response.headers['Content-Type'].must_equal 'application/json' + end + + it 'must include the ETag in HTTP headers' do + auth_as_mock + get collection_url(:images) + last_response.headers['ETag'].wont_be_nil + end + + it 'must have the "images" element on top level' do + auth_as_mock + get collection_url(:images) + xml_response.root.name.must_equal 'images' + end + + it 'must have some "image" elements inside "images"' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').wont_be_empty + end + + it 'must provide the :id attribute for each image in collection' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + r[:id].wont_be_nil end + end - def test_img1_has_correct_attributes - get_auth_url '/api/images', {} - image = (last_xml_response/'images/image[@id="img1"]') - test_image_attributes(image) + it 'must include the :href attribute for each "image" element in collection' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + r[:href].wont_be_nil end + end - def test_it_returns_valid_image - get_auth_url '/api/images/img1', {} - image = (last_xml_response/'image') - test_image_attributes(image) + it 'must use the absolute URL in each :href attribute' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + r[:href].must_match /^http/ end + end - def test_it_has_unique_ids - get_auth_url '/api/images', {} - ids = [] - (last_xml_response/'images/image').each do |image| - ids << image['id'].to_s - end - ids.sort.should == ids.sort.uniq + it 'must have the URL ending with the :id of the image' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + r[:href].must_match /#{r[:id]}$/ end + end - def test_it_has_valid_urls - get_auth_url '/api/images', {} - ids = [] - images = (last_xml_response/'images/image') - images.each do |image| - get_auth_url image['href'].to_s, {} - (last_xml_response/'image').first['href'].should == image['href'].to_s - end + it 'must return the list of valid parameters for the :index action' do + auth_as_mock + options collection_url(:images) + '/index' + last_response.headers['Allow'].wont_be_nil + end + + it 'must have the "name" element defined for each image in collection' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + (r/'name').wont_be_empty end + end - def test_it_can_filter_using_owner_id - get_auth_url '/api/images', { :owner_id => 'mockuser' } - (last_xml_response/'images/image').length.should == 1 - (last_xml_response/'images/image/owner_id').first.text.should == 'mockuser' + it 'must have the "state" element defined for each image in collection' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + (r/'state').wont_be_empty end + end - def test_it_can_filter_using_unknown_owner_id - get_auth_url '/api/images', { :architecture => 'unknown_user' } - (last_xml_response/'images/image').length.should == 0 + it 'must return the full "image" when following the URL in image element' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + get collection_url(:images) + '/' + r[:id] + last_response.status.must_equal 200 end + end - def test_it_can_filter_using_architecture - get_auth_url '/api/images', { :architecture => 'x86_64' } - (last_xml_response/'images/image').length.should == 1 - (last_xml_response/'images/image/architecture').first.text.should == 'x86_64' + it 'must have the "name" element for the image and it should match with the one in collection' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + get collection_url(:images) + '/' + r[:id] + (xml_response/'name').wont_be_empty + (xml_response/'name').first.text.must_equal((r/'name').first.text) end + end - def test_it_can_filter_using_unknown_architecture - get_auth_url '/api/images', { :architecture => 'unknown_arch' } - (last_xml_response/'images/image').length.should == 0 + it 'must have the "name" element for the image and it should match with the one in collection' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + get collection_url(:images) + '/' + r[:id] + (xml_response/'state').wont_be_empty + (xml_response/'state').first.text.must_equal((r/'state').first.text) end + end - def test_it_responses_to_json - get_auth_url '/api/images', {}, { :format => :json } - JSON::parse(last_response.body).class.should == Hash - JSON::parse(last_response.body)['images'].class.should == Array - get_auth_url '/api/images/img1', {}, { :format => :json } - last_response.status.should == 200 - JSON::parse(last_response.body).class.should == Hash - JSON::parse(last_response.body)['image'].class.should == Hash + it 'should have the "owner_id" element for each image' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + get collection_url(:images) + '/' + r[:id] + (xml_response/'owner_id').wont_be_empty end + end - def test_it_responses_to_html - get_auth_url '/api/images', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' - get_auth_url '/api/images/img1', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' + it 'should have the "description" element for each image' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + get collection_url(:images) + '/' + r[:id] + (xml_response/'description').wont_be_empty end + end - def test_it_creates_and_destroys_image_from_instance - post_url "/api/images", { :name => "img4", :description => "Test::Unit image", :instance_id => "inst1"} - last_response.status.should == 201 - last_response.headers['Location'].should_not == nil - get_auth_url last_response.headers['Location'], {} - (last_xml_response/'instance/name').should_not == nil - delete_url "/api/images/img4", {} - last_response.status.should == 204 - get_auth_url "/api/images/img4", {} - last_response.status.should == 404 + it 'should have the "architecture" element for each image' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + get collection_url(:images) + '/' + r[:id] + (xml_response/'architecture').wont_be_empty end + end - private + it 'should include the list of compatible hardware_profiles for each image' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + get collection_url(:images) + '/' + r[:id] + (xml_response/'hardware_profiles/hardware_profile').wont_be_empty + (xml_response/'hardware_profiles/hardware_profile').each do |hwp| + hwp[:href].wont_be_nil + hwp[:href].must_match /^http/ + hwp[:id].wont_be_nil + hwp[:href].must_match /\/#{hwp[:id]}$/ + hwp[:rel].must_equal 'hardware_profile' + end + end + end - def test_image_attributes(image) - (image/'name').text.should_not nil - (image/'owner_id').text.should_not nil - (image/'description').text.should_not nil - (image/'architecture').text.should_not nil + it 'should advertise the list of actions that can be executed for each image' do + auth_as_mock + get collection_url(:images) + (xml_response/'images/image').each do |r| + get collection_url(:images) + '/' + r[:id] + (xml_response/'actions/link').wont_be_empty + (xml_response/'actions/link').each do |l| + l[:href].wont_be_nil + l[:href].must_match /^http/ + l[:method].wont_be_nil + l[:rel].wont_be_nil + end end + end + it 'should give client HTML form to create new image' do + auth_as_mock + header 'Accept', 'text/html' + get collection_url(:images) + '/new' + last_response.status.must_equal 200 end + end diff --git a/server/tests/drivers/mock/instance_states_test.rb b/server/tests/drivers/mock/instance_states_test.rb deleted file mode 100644 index 905cff3..0000000 --- a/server/tests/drivers/mock/instance_states_test.rb +++ /dev/null @@ -1,67 +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. -# - -$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..') -require 'tests/common' - -module DeltacloudUnitTest - class RealmsTest < Test::Unit::TestCase - include Rack::Test::Methods - - def app - Sinatra::Application - end - - def test_it_returns_instance_states - get_auth_url '/api/instance_states', {} - (last_xml_response/'states/state').length.should > 0 - end - - def test_each_state_has_transition - get_auth_url '/api/instance_states', {} - (last_xml_response/'states/state').each do |state| - next if state['name'].eql?('finish') # Finnish state doesn't have transitions - (state/'transition').length.should > 0 - (state/'transition').each do |transition| - transition['to'].should_not == nil - end - end - end - - def test_it_responses_to_json - # FIXME: This test is suffering from conflict between JSON gem and Activesupport - # gem in EC2. - # - #do_request '/api/instance_states', {}, false, { :format => :json } - #JSON::parse(last_response.body).class.should == Array - #JSON::parse(last_response.body).first['transitions'].class.should == Array - #JSON::parse(last_response.body).first['name'].should == 'start' - end - - def test_it_responses_to_html - get_url '/api/instance_states', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' - end - - def test_it_responses_to_png - get_url '/api/instance_states', { :format => 'png' } - last_response.status.should == 200 - last_response.headers['Content-Type'].should =~ /^image\/png/ - end - - end -end diff --git a/server/tests/drivers/mock/instances_test.rb b/server/tests/drivers/mock/instances_test.rb index 45bb4b8..c601a6f 100644 --- a/server/tests/drivers/mock/instances_test.rb +++ b/server/tests/drivers/mock/instances_test.rb @@ -1,253 +1,340 @@ -# 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. -# - -$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..') -require 'tests/common' - -module DeltacloudUnitTest - class InstancesTest < Test::Unit::TestCase - include Rack::Test::Methods - - def app - Sinatra::Application - end - - def test_it_require_authentication - require_authentication?('/api/instances').should == true - end - - def test_it_returns_instances - get_auth_url '/api/instances', {} - (last_xml_response/'instances/instance').length.should > 0 - end - - def test_it_has_correct_attributes_set - get_auth_url '/api/images', {} - (last_xml_response/'images/image').each do |image| - image.attributes.keys.sort.should == [ 'href', 'id' ] - end +describe 'Deltacloud API instances' do + include Deltacloud::Test + + it 'must advertise have the instances collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link[@rel=instances]').wont_be_empty + end + + it 'must require authentication to access the "instance" collection' do + get collection_url(:instances) + last_response.status.must_equal 401 + end + + it 'should respond with HTTP_OK when accessing the :instances collection with authentication' do + auth_as_mock + get collection_url(:instances) + last_response.status.must_equal 200 + end + + it 'should support the JSON media type' do + auth_as_mock + header 'Accept', 'application/json' + get collection_url(:instances) + last_response.status.must_equal 200 + last_response.headers['Content-Type'].must_equal 'application/json' + end + + it 'must include the ETag in HTTP headers' do + auth_as_mock + get collection_url(:instances) + last_response.headers['ETag'].wont_be_nil + end + + it 'must have the "instances" element on top level' do + auth_as_mock + get collection_url(:instances) + xml_response.root.name.must_equal 'instances' + end + + it 'must have some "instance" elements inside "instances"' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').wont_be_empty + end + + it 'must provide the :id attribute for each instance in collection' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + r[:id].wont_be_nil end + end - def test_it_has_unique_ids - get_auth_url '/api/instances', {} - ids = [] - (last_xml_response/'instances/instance').each do |image| - ids << image['id'].to_s - end - ids.sort.should == ids.sort.uniq - end - - def test_inst1_has_correct_attributes - get_auth_url '/api/instances', {} - instance = (last_xml_response/'instances/instance[@id="inst1"]') - test_instance_attributes(instance) - end - - def test_it_returns_valid_realm - get_auth_url '/api/instances/inst1', {} - instance = (last_xml_response/'instance') - test_instance_attributes(instance) - end - - def test_it_responses_to_json - get_auth_url '/api/instances', {}, { :format => :json } - JSON::parse(last_response.body).class.should == Hash - JSON::parse(last_response.body)['instances'].class.should == Array - - get_auth_url '/api/instances/inst1', {}, { :format => :json } - last_response.status.should == 200 - JSON::parse(last_response.body).class.should == Hash - JSON::parse(last_response.body)['instance'].class.should == Hash - end - - def test_it_responses_to_html - get_auth_url '/api/instances', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' - get_auth_url '/api/instances/inst1', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' - end - - def test_it_create_a_new_instance_using_image_id - params = { - :image_id => 'img1' - } - post_url '/api/instances', params - last_response.status.should == 201 - last_response.headers['Location'].should_not == nil - get_auth_url last_response.headers['Location'], {} - (last_xml_response/'instance/name').should_not == nil - add_created_instance (last_xml_response/'instance').first['id'] - test_instance_attributes(last_xml_response/'instance') - end - - def test_it_create_a_new_instance_using_image_id_and_name - params = { - :image_id => 'img1', - :name => "unit_test_instance1" - } - post_url '/api/instances', params - last_response.status.should == 201 - last_response.headers['Location'].should_not == nil - get_auth_url last_response.headers['Location'], {} - (last_xml_response/'instance/name').text.should == 'unit_test_instance1' - add_created_instance (last_xml_response/'instance').first['id'] - test_instance_attributes(last_xml_response/'instance') - end - - def test_it_create_a_new_instance_using_image_id_and_name_and_hwp_storage_and_hwp_cpu - params = { - :image_id => 'img1', - :realm_id => '', - :name => "unit_test_instance3", - :hwp_id => "m1-large", - :hwp_storage => '850', - :hwp_memory => '7680.0', - :hwp_cpu => "1.0", - } - post_url '/api/instances', params - last_response.status.should == 400 - end - - def test_it_create_a_new_instance_using_image_id_and_name_and_hwp_storage - params = { - :image_id => 'img1', - :name => "unit_test_instance2", - :hwp_id => "m1-small", - :hwp_storage => "160" - } - post_url '/api/instances', params - last_response.status.should == 201 - last_response.headers['Location'].should_not == nil - get_auth_url last_response.headers['Location'], {} - (last_xml_response/'instance/name').text.should == 'unit_test_instance2' - (last_xml_response/'instance/hardware_profile').first['id'].should == 'm1-small' - add_created_instance (last_xml_response/'instance').first['id'] - test_instance_attributes(last_xml_response/'instance') - end - - def test_it_z0_stop_and_start_instance - $created_instances.each do |instance_id| - get_auth_url "/api/instances/#{instance_id}", {} - stop_url = (last_xml_response/'actions/link[@rel="stop"]').first['href'] - stop_url.should_not == nil - post_url stop_url - last_response.status.should == 200 - instance = Nokogiri::XML(last_response.body) - test_instance_attributes(instance) - (instance/'state').text.should == 'STOPPED' - get_auth_url "/api/instances/#{instance_id}", {} - start_url = (last_xml_response/'actions/link[@rel="start"]').first['href'] - start_url.should_not == nil - post_url start_url - last_response.status.should == 200 - instance = Nokogiri::XML(last_response.body) - test_instance_attributes(instance) - (instance/'state').text.should == 'RUNNING' - end + it 'must include the :href attribute for each "instance" element in collection' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + r[:href].wont_be_nil end + end - def test_z0_reboot_instance - $created_instances.each do |instance_id| - get_auth_url "/api/instances/#{instance_id}", {} - reboot_url = (last_xml_response/'actions/link[@rel="reboot"]').first['href'] - reboot_url.should_not == nil - post_url reboot_url - last_response.status.should == 202 - instance = Nokogiri::XML(last_response.body) - test_instance_attributes(instance) - (instance/'state').text.should == 'RUNNING' - end + it 'must use the absolute URL in each :href attribute' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + r[:href].must_match /^http/ end + end - def test_z1_stop_created_instances - $created_instances.each do |instance_id| - get_auth_url "/api/instances/#{instance_id}", {} - stop_url = (last_xml_response/'actions/link[@rel="stop"]').first['href'] - stop_url.should_not == nil - post_url stop_url, {} - last_response.status.should == 200 - instance = Nokogiri::XML(last_response.body) - test_instance_attributes(instance) - (instance/'state').text.should == 'STOPPED' - end + it 'must have the URL ending with the :id of the instance' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + r[:href].must_match /#{r[:id]}$/ end + end - def test_z2_destroy_created_instances - $created_instances.each do |instance_id| - get_auth_url "/api/instances/#{instance_id}", {} - destroy_url = (last_xml_response/'actions/link[@rel="destroy"]').first['href'] - destroy_url.should_not == nil - delete_url destroy_url, {} - last_response.status.should == 204 - end + it 'must return the list of valid parameters for the :index action' do + auth_as_mock + options collection_url(:instances) + '/index' + last_response.headers['Allow'].wont_be_nil + end + + it 'must have the "name" element defined for each instance in collection' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + (r/'name').wont_be_empty end + end - def test_create_key_returns_201 - post_url '/api/keys', {:name => Time.now.to_f.to_s} - last_response.status.should == 201 + it 'must have the "state" element defined for each instance in collection' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + (r/'state').wont_be_empty + (r/'state').first.must_match /(RUNNING|STOPPED|PENDING)/ end + end - private + it 'must return the full "instance" when following the URL in instance element' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + last_response.status.must_equal 200 + end + end - def test_instance_attributes(instance) - (instance/'name').should_not == nil - (instance/'owner_id').should_not == nil - ['RUNNING', 'STOPPED'].include?((instance/'state').text).should == true + it 'must have the "name" element for the instance and it should match with the one in collection' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + (xml_response/'name').wont_be_empty + (xml_response/'name').first.text.must_equal((r/'name').first.text) + end + end + + it 'must have the "name" element for the instance and it should match with the one in collection' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + (xml_response/'state').wont_be_empty + (xml_response/'state').first.text.must_equal((r/'state').first.text) + end + end + + it 'must have the "owner_id" element for the instance and it should match with the one in collection' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + (xml_response/'owner_id').wont_be_empty + (xml_response/'owner_id').first.text.must_equal((r/'owner_id').first.text) + end + end + + it 'must link to the realm that was used to during instance creation' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + (xml_response/'realm').wont_be_empty + (xml_response/'realm').size.must_equal 1 + (xml_response/'realm').first[:id].wont_be_nil + (xml_response/'realm').first[:href].wont_be_nil + (xml_response/'realm').first[:href].must_match /\/#{(xml_response/'realm').first[:id]}$/ + end + end - (instance/'public_addreses').should_not == nil - (instance/'public_addresses/address').to_a.size.should > 0 - (instance/'public_addresses/address').first.text.should_not == "" - (instance/'public_addresses/address').first[:type].should == "hostname" + it 'must link to the image that was used to during instance creation' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + (xml_response/'image').wont_be_empty + (xml_response/'image').size.must_equal 1 + (xml_response/'image').first[:id].wont_be_nil + (xml_response/'image').first[:href].wont_be_nil + (xml_response/'image').first[:href].must_match /\/#{(xml_response/'image').first[:id]}$/ + end + end - (instance/'private_addresses').should_not == nil - (instance/'private_addresses/address').to_a.size.should > 0 - (instance/'private_addresses/address').first.text.should_not == "" - (instance/'private_addresses/address').first[:type].should == "hostname" + it 'must link to the hardware_profile that was used to during instance creation' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + (xml_response/'hardware_profile').wont_be_empty + (xml_response/'hardware_profile').size.must_equal 1 + (xml_response/'hardware_profile').first[:id].wont_be_nil + (xml_response/'hardware_profile').first[:href].wont_be_nil + (xml_response/'hardware_profile').first[:href].must_match /\/#{(xml_response/'hardware_profile').first[:id]}$/ + end + end - (instance/'actions/link').to_a.size.should > 0 - (instance/'actions/link').each do |link| - link['href'].should_not == "" - link['rel'].should_not == "" - link['method'].should_not == "" - ['get', 'post', 'delete', 'put'].include?(link['method']).should == true + it 'should advertise the public and private addresses of the instance' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + (xml_response/'public_addresses').wont_be_empty + (xml_response/'public_addresses').size.must_equal 1 + (xml_response/'public_addresses/address').each do |a| + a[:type].wont_be_nil + a.text.strip.wont_be_empty end + (xml_response/'private_addresses').wont_be_empty + (xml_response/'private_addresses').size.must_equal 1 + (xml_response/'private_addresses/address').each do |a| + a[:type].wont_be_nil + a.text.strip.wont_be_empty + end + end + end - (instance/'image').size.should > 0 - (instance/'image').first['href'].should_not == "" - (instance/'image').first['id'].should_not == "" - get_auth_url (instance/'image').first['href'], {} - (last_xml_response/'image').should_not == nil - (last_xml_response/'image').first['href'] == (instance/'image').first['href'] + it 'should advertise the storage volumes used by the instance' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + (xml_response/'storage_volumes').wont_be_empty + end + end - (instance/'realm').size.should > 0 - (instance/'realm').first['href'].should_not == "" - (instance/'realm').first['id'].should_not == "" - get_auth_url (instance/'realm').first['href'] - (last_xml_response/'realm').should_not == nil - (last_xml_response/'realm').first['href'] == (instance/'realm').first['href'] + it 'should advertise the list of actions that can be executed for each instance' do + auth_as_mock + get collection_url(:instances) + (xml_response/'instances/instance').each do |r| + get collection_url(:instances) + '/' + r[:id] + (xml_response/'actions/link').wont_be_empty + (xml_response/'actions/link').each do |l| + l[:href].wont_be_nil + l[:href].must_match /^http/ + l[:method].wont_be_nil + l[:rel].wont_be_nil + end + end + end - (instance/'hardware_profile').size.should > 0 - (instance/'hardware_profile').first['href'].should_not == "" - (instance/'hardware_profile').first['id'].should_not == "" - get_auth_url (instance/'hardware_profile').first['href'] - (last_xml_response/'hardware_profile').should_not == nil - (last_xml_response/'hardware_profile').first['href'] == (instance/'hardware_profile').first['href'] + it 'should allow to create and destroy new instance using the first available image without realm' do + auth_as_mock + get collection_url(:images) + image_id = (xml_response/'images/image').first[:id] + image_id.wont_be_nil + post collection_url(:instances), { + :image_id => image_id + } + last_response.status.must_equal 201 # HTTP_CREATED + last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance + instance_id = last_response.headers['Location'].split('/').last + # Get the instance and check if ID and image is set correctly + get collection_url(:instances) + '/' + instance_id + last_response.status.must_equal 200 # HTTP_OK + (xml_response/'instance').first[:id].must_equal instance_id + (xml_response/'instance/image').first[:id].must_equal image_id + # If instance is RUNNING then stop it + if (xml_response/'instance/state').first.text == 'RUNNING' + post collection_url(:instances) + '/' + instance_id + '/stop' + last_response.status.must_equal 202 # HTTP_NO_CONTENT end + # Delete created instance + delete collection_url(:instances) + '/' + instance_id + last_response.status.must_equal 204 # HTTP_NO_CONTENT + end + it 'should allow to create and destroy new instance using the first available image within first realm' do + auth_as_mock + get collection_url(:images) + image_id = (xml_response/'images/image').first[:id] + get collection_url(:realms) + realm_id = (xml_response/'realms/realm').first[:id] + image_id.wont_be_nil + realm_id.wont_be_nil + post collection_url(:instances), { + :image_id => image_id, + :realm_id => realm_id, + } + last_response.status.must_equal 201 # HTTP_CREATED + last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance + instance_id = last_response.headers['Location'].split('/').last + # Get the instance and check if ID and image is set correctly + get collection_url(:instances) + '/' + instance_id + last_response.status.must_equal 200 # HTTP_OK + (xml_response/'instance').first[:id].must_equal instance_id + (xml_response/'instance/image').first[:id].must_equal image_id + (xml_response/'instance/realm').first[:id].must_equal realm_id + # If instance is RUNNING then stop it + if (xml_response/'instance/state').first.text == 'RUNNING' + post collection_url(:instances) + '/' + instance_id + '/stop' + last_response.status.must_equal 202 # HTTP_NO_CONTENT + end + # Delete created instance + delete collection_url(:instances) + '/' + instance_id + last_response.status.must_equal 204 # HTTP_NO_CONTENT end + + it 'should allow to create and destroy new instance using the first available image with user defined name' do + auth_as_mock + get collection_url(:images) + image_id = (xml_response/'images/image').first[:id] + image_id.wont_be_nil + name = "i#{Time.now.to_i}" + post collection_url(:instances), { + :image_id => image_id, + :name => name + } + last_response.status.must_equal 201 # HTTP_CREATED + last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance + instance_id = last_response.headers['Location'].split('/').last + # Get the instance and check if ID and image is set correctly + get collection_url(:instances) + '/' + instance_id + last_response.status.must_equal 200 # HTTP_OK + (xml_response/'instance').first[:id].must_equal instance_id + (xml_response/'instance/image').first[:id].must_equal image_id + (xml_response/'instance/name').first.text.must_equal name + # If instance is RUNNING then stop it + if (xml_response/'instance/state').first.text == 'RUNNING' + post collection_url(:instances) + '/' + instance_id + '/stop' + last_response.status.must_equal 202 # HTTP_NO_CONTENT + end + # Delete created instance + delete collection_url(:instances) + '/' + instance_id + last_response.status.must_equal 204 # HTTP_NO_CONTENT + end + + it 'should allow to create and destroy new instance using the first available image and first hardware_profile' do + auth_as_mock + get collection_url(:images) + image_id = (xml_response/'images/image').first[:id] + get collection_url(:hardware_profiles) + hwp_id = (xml_response/'hardware_profiles/hardware_profile').first[:id] + image_id.wont_be_nil + name = "i#{Time.now.to_i}" + post collection_url(:instances), { + :image_id => image_id, + :hwp_id => hwp_id + } + last_response.status.must_equal 201 # HTTP_CREATED + last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance + instance_id = last_response.headers['Location'].split('/').last + # Get the instance and check if ID and image is set correctly + get collection_url(:instances) + '/' + instance_id + last_response.status.must_equal 200 # HTTP_OK + (xml_response/'instance').first[:id].must_equal instance_id + (xml_response/'instance/image').first[:id].must_equal image_id + (xml_response/'instance/hardware_profile').first[:id].must_equal hwp_id + # If instance is RUNNING then stop it + if (xml_response/'instance/state').first.text == 'RUNNING' + post collection_url(:instances) + '/' + instance_id + '/stop' + last_response.status.must_equal 202 # HTTP_NO_CONTENT + end + # Delete created instance + delete collection_url(:instances) + '/' + instance_id + last_response.status.must_equal 204 # HTTP_NO_CONTENT + end + end diff --git a/server/tests/drivers/mock/keys_test.rb b/server/tests/drivers/mock/keys_test.rb new file mode 100644 index 0000000..9267b5a --- /dev/null +++ b/server/tests/drivers/mock/keys_test.rb @@ -0,0 +1,158 @@ +describe 'Deltacloud API Keys' do + include Deltacloud::Test + + it 'must advertise have the keys collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link[@rel=keys]').wont_be_empty + end + + it 'must require authentication to access the "key" collection' do + get collection_url(:keys) + last_response.status.must_equal 401 + end + + it 'should respond with HTTP_OK when accessing the :keys collection with authentication' do + auth_as_mock + get collection_url(:keys) + last_response.status.must_equal 200 + end + + it 'should support the JSON media type' do + auth_as_mock + header 'Accept', 'application/json' + get collection_url(:keys) + last_response.status.must_equal 200 + last_response.headers['Content-Type'].must_equal 'application/json' + end + + it 'must include the ETag in HTTP headers' do + auth_as_mock + get collection_url(:keys) + last_response.headers['ETag'].wont_be_nil + end + + it 'must have the "keys" element on top level' do + auth_as_mock + get collection_url(:keys) + xml_response.root.name.must_equal 'keys' + end + + it 'must have some "key" elements inside "keys"' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').wont_be_empty + end + + it 'must tell the kind of "key" elements inside "keys"' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |k| + k[:type].must_match /(key|password)/ + end + end + + it 'must provide the :id attribute for each key in collection' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |r| + r[:id].wont_be_nil + end + end + + it 'must include the :href attribute for each "key" element in collection' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |r| + r[:href].wont_be_nil + end + end + + it 'must use the absolute URL in each :href attribute' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |r| + r[:href].must_match /^http/ + end + end + + it 'must have the URL ending with the :id of the key' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |r| + r[:href].must_match /#{r[:id]}$/ + end + end + + it 'must return the list of valid parameters for the :index action' do + auth_as_mock + options collection_url(:keys) + '/index' + last_response.headers['Allow'].wont_be_nil + end + + it 'must have the "name" element defined for each key in collection' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |r| + (r/'name').wont_be_empty + end + end + + + it 'must return the full "key" when following the URL in key element' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |r| + get collection_url(:keys) + '/' + r[:id] + last_response.status.must_equal 200 + end + end + + it 'must have the "name" element for the key and it should match with the one in collection' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |r| + get collection_url(:keys) + '/' + r[:id] + (xml_response/'name').wont_be_empty + (xml_response/'name').first.text.must_equal((r/'name').first.text) + end + end + + it 'must have the "name" element for the key and it should match with the one in collection' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |r| + get collection_url(:keys) + '/' + r[:id] + (xml_response/'state').wont_be_empty + (xml_response/'state').first.text.must_equal((r/'state').first.text) + end + end + + it 'should advertise the list of actions that can be executed for each key' do + auth_as_mock + get collection_url(:keys) + (xml_response/'keys/key').each do |r| + get collection_url(:keys) + '/' + r[:id] + (xml_response/'actions/link').wont_be_empty + (xml_response/'actions/link').each do |l| + l[:href].wont_be_nil + l[:href].must_match /^http/ + l[:method].wont_be_nil + l[:rel].wont_be_nil + end + end + end + + it 'should allow to create a new key and then remove it' do + auth_as_mock + key_name = Time.now.to_i.to_s + post collection_url(:keys), { + :name => 'test_key_'+key_name + } + last_response.status.must_equal 201 # HTTP_CREATED + get collection_url(:keys) + '/' + 'test_key_'+key_name + last_response.status.must_equal 200 # HTTP_OK + delete collection_url(:keys) + '/' + 'test_key_'+key_name + last_response.status.must_equal 204 # HTTP_NO_CONTENT + end + +end diff --git a/server/tests/drivers/mock/realms_test.rb b/server/tests/drivers/mock/realms_test.rb index b0db9e4..6bc9101 100644 --- a/server/tests/drivers/mock/realms_test.rb +++ b/server/tests/drivers/mock/realms_test.rb @@ -1,89 +1,129 @@ -# 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. -# - -$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..') -require 'tests/common' - -module DeltacloudUnitTest - class RealmsTest < Test::Unit::TestCase - include Rack::Test::Methods - - def app - Sinatra::Application - end +describe 'Deltacloud API Realms' do + include Deltacloud::Test - def test_it_returns_realms - get_auth_url '/api/realms', {} - (last_xml_response/'realms/realm').length.should > 0 - end + it 'must advertise have the realms collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link[@rel=realms]').wont_be_empty + end + + it 'must require authentication to access the "realm" collection' do + get collection_url(:realms) + last_response.status.must_equal 401 + end + + it 'should respond with HTTP_OK when accessing the :realms collection with authentication' do + auth_as_mock + get collection_url(:realms) + last_response.status.must_equal 200 + end + + it 'should support the JSON media type' do + auth_as_mock + header 'Accept', 'application/json' + get collection_url(:realms) + last_response.status.must_equal 200 + last_response.headers['Content-Type'].must_equal 'application/json' + end + + it 'must include the ETag in HTTP headers' do + auth_as_mock + get collection_url(:realms) + last_response.headers['ETag'].wont_be_nil + end + + it 'must have the "realms" element on top level' do + auth_as_mock + get collection_url(:realms) + xml_response.root.name.must_equal 'realms' + end + + it 'must have some "realm" elements inside "realms"' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').wont_be_empty + end - def test_it_has_correct_attributes_set - get_auth_url '/api/realms', {} - (last_xml_response/'realms/realm').each do |realm| - realm.attributes.keys.sort.should == [ 'href', 'id' ] - end + it 'must provide the :id attribute for each realm in collection' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').each do |r| + r[:id].wont_be_nil end + end - def test_us_has_correct_attributes - get_auth_url '/api/realms', {} - realm = (last_xml_response/'realms/realm[@id="us"]') - test_realm_attributes(realm) + it 'must include the :href attribute for each "realm" element in collection' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').each do |r| + r[:href].wont_be_nil end + end - def test_it_returns_valid_realm - get_auth_url '/api/realms/us', {} - realm = (last_xml_response/'realm') - test_realm_attributes(realm) + it 'must use the absolute URL in each :href attribute' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').each do |r| + r[:href].must_match /^http/ end + end - def test_it_has_unique_ids - get_auth_url '/api/realms', {} - ids = [] - (last_xml_response/'realms/realm').each do |realm| - ids << realm['id'].to_s - end - ids.sort.should == ids.sort.uniq + it 'must have the URL ending with the :id of the realm' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').each do |r| + r[:href].must_match /#{r[:id]}$/ end + end + + it 'must return the list of valid parameters for the :index action' do + auth_as_mock + options collection_url(:realms) + '/index' + last_response.headers['Allow'].wont_be_nil + end - def test_it_responses_to_json - get_auth_url '/api/realms', {}, { :format => :json } - JSON::parse(last_response.body).class.should == Hash - JSON::parse(last_response.body)['realms'].class.should == Array - get_auth_url '/api/realms/us', {}, { :format => :json } - last_response.status.should == 200 - JSON::parse(last_response.body).class.should == Hash - JSON::parse(last_response.body)['realm'].class.should == Hash + it 'must have the "name" element defined for each realm in collection' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').each do |r| + (r/'name').wont_be_empty end + end - def test_it_responses_to_html - get_auth_url '/api/realms', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' - get_auth_url '/api/realms/us', {}, { :format => :html } - last_response.status.should == 200 - Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html' + it 'must have the "state" element defined for each realm in collection' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').each do |r| + (r/'state').wont_be_empty end + end - private + it 'must return the full "realm" when following the URL in realm element' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').each do |r| + get collection_url(:realms) + '/' + r[:id] + last_response.status.must_equal 200 + end + end - def test_realm_attributes(realm) - (realm/'name').should_not == nil - (realm/'limit').should_not == nil - ['AVAILABLE'].include?((realm/'state').text).should == true + it 'must have the "name" element for the realm and it should match with the one in collection' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').each do |r| + get collection_url(:realms) + '/' + r[:id] + (xml_response/'name').wont_be_empty + (xml_response/'name').first.text.must_equal((r/'name').first.text) end + end + it 'must have the "state" element for the realm and it should match with the one in collection' do + auth_as_mock + get collection_url(:realms) + (xml_response/'realms/realm').each do |r| + get collection_url(:realms) + '/' + r[:id] + (xml_response/'state').wont_be_empty + (xml_response/'state').first.text.must_equal((r/'state').first.text) + end end + end diff --git a/server/tests/drivers/mock/setup.rb b/server/tests/drivers/mock/setup.rb deleted file mode 100644 index 60a7094..0000000 --- a/server/tests/drivers/mock/setup.rb +++ /dev/null @@ -1,3 +0,0 @@ -ENV['API_DRIVER'] = "mock" -ENV['API_USER'] = 'mockuser' -ENV['API_PASSWORD'] = 'mockpassword' diff --git a/server/tests/drivers/mock/storage_snapshots_test.rb b/server/tests/drivers/mock/storage_snapshots_test.rb new file mode 100644 index 0000000..52ea847 --- /dev/null +++ b/server/tests/drivers/mock/storage_snapshots_test.rb @@ -0,0 +1,111 @@ +describe 'Deltacloud API storage_snapshots' do + include Deltacloud::Test + + it 'must advertise have the storage_snapshots collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link[@rel=storage_snapshots]').wont_be_empty + end + + it 'must require authentication to access the "storage_snapshot" collection' do + get collection_url(:storage_snapshots) + last_response.status.must_equal 401 + end + + it 'should respond with HTTP_OK when accessing the :storage_snapshots collection with authentication' do + auth_as_mock + get collection_url(:storage_snapshots) + last_response.status.must_equal 200 + end + + it 'should support the JSON media type' do + auth_as_mock + header 'Accept', 'application/json' + get collection_url(:storage_snapshots) + last_response.status.must_equal 200 + last_response.headers['Content-Type'].must_equal 'application/json' + end + + it 'must include the ETag in HTTP headers' do + auth_as_mock + get collection_url(:storage_snapshots) + last_response.headers['ETag'].wont_be_nil + end + + it 'must have the "storage_snapshots" element on top level' do + auth_as_mock + get collection_url(:storage_snapshots) + xml_response.root.name.must_equal 'storage_snapshots' + end + + it 'must have some "storage_snapshot" elements inside "storage_snapshots"' do + auth_as_mock + get collection_url(:storage_snapshots) + (xml_response/'storage_snapshots/storage_snapshot').wont_be_empty + end + + it 'must provide the :id attribute for each storage_snapshot in collection' do + auth_as_mock + get collection_url(:storage_snapshots) + (xml_response/'storage_snapshots/storage_snapshot').each do |r| + r[:id].wont_be_nil + end + end + + it 'must include the :href attribute for each "storage_snapshot" element in collection' do + auth_as_mock + get collection_url(:storage_snapshots) + (xml_response/'storage_snapshots/storage_snapshot').each do |r| + r[:href].wont_be_nil + end + end + + it 'must use the absolute URL in each :href attribute' do + auth_as_mock + get collection_url(:storage_snapshots) + (xml_response/'storage_snapshots/storage_snapshot').each do |r| + r[:href].must_match /^http/ + end + end + + it 'must have the URL ending with the :id of the storage_snapshot' do + auth_as_mock + get collection_url(:storage_snapshots) + (xml_response/'storage_snapshots/storage_snapshot').each do |r| + r[:href].must_match /#{r[:id]}$/ + end + end + + it 'must return the list of valid parameters for the :index action' do + auth_as_mock + options collection_url(:storage_snapshots) + '/index' + last_response.headers['Allow'].wont_be_nil + end + + it 'must have the "name" element defined for each storage_snapshot in collection' do + auth_as_mock + get collection_url(:storage_snapshots) + (xml_response/'storage_snapshots/storage_snapshot').each do |r| + (r/'name').wont_be_empty + end + end + + it 'must return the full "storage_snapshot" when following the URL in storage_snapshot element' do + auth_as_mock + get collection_url(:storage_snapshots) + (xml_response/'storage_snapshots/storage_snapshot').each do |r| + get collection_url(:storage_snapshots) + '/' + r[:id] + last_response.status.must_equal 200 + end + end + + it 'must have the "name" element for the storage_snapshot and it should match with the one in collection' do + auth_as_mock + get collection_url(:storage_snapshots) + (xml_response/'storage_snapshots/storage_snapshot').each do |r| + get collection_url(:storage_snapshots) + '/' + r[:id] + (xml_response/'name').wont_be_empty + (xml_response/'name').first.text.must_equal((r/'name').first.text) + end + end + +end diff --git a/server/tests/drivers/mock/storage_volumes_test.rb b/server/tests/drivers/mock/storage_volumes_test.rb new file mode 100644 index 0000000..cbafd5d --- /dev/null +++ b/server/tests/drivers/mock/storage_volumes_test.rb @@ -0,0 +1,119 @@ +describe 'Deltacloud API storage_volumes' do + include Deltacloud::Test + + it 'must advertise have the storage_volumes collection in API entrypoint' do + get API_ROOT_URL + (xml_response/'api/link[@rel=storage_volumes]').wont_be_empty + end + + it 'must require authentication to access the "storage_volume" collection' do + get collection_url(:storage_volumes) + last_response.status.must_equal 401 + end + + it 'should respond with HTTP_OK when accessing the :storage_volumes collection with authentication' do + auth_as_mock + get collection_url(:storage_volumes) + last_response.status.must_equal 200 + end + + it 'should support the JSON media type' do + auth_as_mock + header 'Accept', 'application/json' + get collection_url(:storage_volumes) + last_response.status.must_equal 200 + last_response.headers['Content-Type'].must_equal 'application/json' + end + + it 'must include the ETag in HTTP headers' do + auth_as_mock + get collection_url(:storage_volumes) + last_response.headers['ETag'].wont_be_nil + end + + it 'must have the "storage_volumes" element on top level' do + auth_as_mock + get collection_url(:storage_volumes) + xml_response.root.name.must_equal 'storage_volumes' + end + + it 'must have some "storage_volume" elements inside "storage_volumes"' do + auth_as_mock + get collection_url(:storage_volumes) + (xml_response/'storage_volumes/storage_volume').wont_be_empty + end + + it 'must provide the :id attribute for each storage_volume in collection' do + auth_as_mock + get collection_url(:storage_volumes) + (xml_response/'storage_volumes/storage_volume').each do |r| + r[:id].wont_be_nil + end + end + + it 'must include the :href attribute for each "storage_volume" element in collection' do + auth_as_mock + get collection_url(:storage_volumes) + (xml_response/'storage_volumes/storage_volume').each do |r| + r[:href].wont_be_nil + end + end + + it 'must use the absolute URL in each :href attribute' do + auth_as_mock + get collection_url(:storage_volumes) + (xml_response/'storage_volumes/storage_volume').each do |r| + r[:href].must_match /^http/ + end + end + + it 'must have the URL ending with the :id of the storage_volume' do + auth_as_mock + get collection_url(:storage_volumes) + (xml_response/'storage_volumes/storage_volume').each do |r| + r[:href].must_match /#{r[:id]}$/ + end + end + + it 'must return the list of valid parameters for the :index action' do + auth_as_mock + options collection_url(:storage_volumes) + '/index' + last_response.headers['Allow'].wont_be_nil + end + + it 'must have the "name" element defined for each storage_volume in collection' do + auth_as_mock + get collection_url(:storage_volumes) + (xml_response/'storage_volumes/storage_volume').each do |r| + (r/'name').wont_be_empty + end + end + + it 'must have the "state" element defined for each storage_volume in collection' do + auth_as_mock + get collection_url(:storage_volumes) + (xml_response/'storage_volumes/storage_volume').each do |r| + (r/'state').wont_be_empty + end + end + + it 'must return the full "storage_volume" when following the URL in storage_volume element' do + auth_as_mock + get collection_url(:storage_volumes) + (xml_response/'storage_volumes/storage_volume').each do |r| + get collection_url(:storage_volumes) + '/' + r[:id] + last_response.status.must_equal 200 + end + end + + it 'must have the "name" element for the storage_volume and it should match with the one in collection' do + auth_as_mock + get collection_url(:storage_volumes) + (xml_response/'storage_volumes/storage_volume').each do |r| + get collection_url(:storage_volumes) + '/' + r[:id] + (xml_response/'name').wont_be_empty + (xml_response/'name').first.text.must_equal((r/'name').first.text) + end + end + +end diff --git a/server/tests/drivers/mock/url_for_test.rb b/server/tests/drivers/mock/url_for_test.rb deleted file mode 100644 index ccd4ebf..0000000 --- a/server/tests/drivers/mock/url_for_test.rb +++ /dev/null @@ -1,67 +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. -# - -$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..') -require 'tests/common' - -module DeltacloudUnitTest - class UrlForTest < Test::Unit::TestCase - include Rack::Test::Methods - - def app - Sinatra::Application - end - - def test_it_works_for_root - verify_url_for("/", "/") - end - - def test_it_works_for_root_absolute - verify_url_for("/", "http://example.org/", :full) - end - - def test_it_works_with_spaces - verify_url_for("/url with spaces", "/url%20with%20spaces") - end - - def test_it_works_when_given_absolute - verify_url_for("http://test.com", "http://test.com") - end - - def test_it_works_when_not_at_root_context - verify_url_for("/", "context/", :path_only, {}, {"SCRIPT_NAME" => "context"}) - end - - def verify_url_for(url, expected_url, mode=:path_only, params={}, rack_env={}) - # generate a unique url for each test - test_url = "/url_for_test/#{expected_url.hash}/#{Time.now.to_i}" - # Create our sinatra test endpoint - self.class.create_test_url_content(test_url, url, mode) - - # verify the generated url matches what we expect - get test_url, params, rack_env - last_response.body.should == expected_url - end - - def self.create_test_url_content(test_url, url_content, mode) - get test_url do - content_type "text/plain" - url_for(url_content, mode) - end - end - - end -end -- 1.7.10.1