--- server/lib/deltacloud/base_driver/mock_driver.rb | 17 +- server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 10 +- server/lib/drivers.rb | 4 +- tests/Rakefile | 52 ++++++ tests/ec2/99_rake.feature | 7 + tests/ec2/api.feature | 41 +++++ tests/ec2/hardware_profiles.feature | 29 ++++ tests/ec2/images.feature | 59 +++++++ tests/ec2/instance_states.feature | 36 ++++ tests/ec2/instances.feature | 171 ++++++++++++++++++++ tests/ec2/realms.feature | 31 ++++ tests/ec2/step_definitions/api_steps.rb | 103 ++++++++++++ .../step_definitions/hardware_profiles_steps.rb | 25 +++ tests/ec2/step_definitions/images_steps.rb | 83 ++++++++++ .../ec2/step_definitions/instance_states_steps.rb | 34 ++++ tests/ec2/step_definitions/instances_steps.rb | 137 ++++++++++++++++ tests/ec2/step_definitions/rake_steps.rb | 19 +++ tests/ec2/storage_snapshots.feature | 29 ++++ tests/ec2/storage_volumes.feature | 33 ++++ tests/ec2/support/env.rb | 53 ++++++ tests/mock/99_rake.feature | 7 + tests/mock/api.feature | 41 +++++ tests/mock/hardware_profiles.feature | 29 ++++ tests/mock/images.feature | 59 +++++++ tests/mock/instance_states.feature | 36 ++++ tests/mock/instances.feature | 165 +++++++++++++++++++ tests/mock/realms.feature | 31 ++++ tests/mock/step_definitions/api_steps.rb | 103 ++++++++++++ .../step_definitions/hardware_profiles_steps.rb | 25 +++ tests/mock/step_definitions/images_steps.rb | 83 ++++++++++ .../mock/step_definitions/instance_states_steps.rb | 34 ++++ tests/mock/step_definitions/instances_steps.rb | 121 ++++++++++++++ tests/mock/step_definitions/rake_steps.rb | 19 +++ tests/mock/storage_snapshots.feature | 29 ++++ tests/mock/storage_volumes.feature | 33 ++++ tests/mock/support/env.rb | 40 +++++ 36 files changed, 1813 insertions(+), 15 deletions(-) create mode 100644 tests/Rakefile create mode 100644 tests/ec2/99_rake.feature create mode 100644 tests/ec2/api.feature create mode 100644 tests/ec2/hardware_profiles.feature create mode 100644 tests/ec2/images.feature create mode 100644 tests/ec2/instance_states.feature create mode 100644 tests/ec2/instances.feature create mode 100644 tests/ec2/realms.feature create mode 100644 tests/ec2/step_definitions/api_steps.rb create mode 100644 tests/ec2/step_definitions/hardware_profiles_steps.rb create mode 100644 tests/ec2/step_definitions/images_steps.rb create mode 100644 tests/ec2/step_definitions/instance_states_steps.rb create mode 100644 tests/ec2/step_definitions/instances_steps.rb create mode 100644 tests/ec2/step_definitions/rake_steps.rb create mode 100644 tests/ec2/storage_snapshots.feature create mode 100644 tests/ec2/storage_volumes.feature create mode 100644 tests/ec2/support/env.rb create mode 100644 tests/mock/99_rake.feature create mode 100644 tests/mock/api.feature create mode 100644 tests/mock/hardware_profiles.feature create mode 100644 tests/mock/images.feature create mode 100644 tests/mock/instance_states.feature create mode 100644 tests/mock/instances.feature create mode 100644 tests/mock/realms.feature create mode 100644 tests/mock/step_definitions/api_steps.rb create mode 100644 tests/mock/step_definitions/hardware_profiles_steps.rb create mode 100644 tests/mock/step_definitions/images_steps.rb create mode 100644 tests/mock/step_definitions/instance_states_steps.rb create mode 100644 tests/mock/step_definitions/instances_steps.rb create mode 100644 tests/mock/step_definitions/rake_steps.rb create mode 100644 tests/mock/storage_snapshots.feature create mode 100644 tests/mock/storage_volumes.feature create mode 100644 tests/mock/support/env.rb
diff --git a/server/lib/deltacloud/base_driver/mock_driver.rb b/server/lib/deltacloud/base_driver/mock_driver.rb index ef92ada..dadef4b 100644 --- a/server/lib/deltacloud/base_driver/mock_driver.rb +++ b/server/lib/deltacloud/base_driver/mock_driver.rb @@ -1,27 +1,27 @@ require 'base64' +require 'ap'
module Deltacloud::Mock
module Cache
- CACHE_STORAGE = File.join(File.dirname(__FILE__), '..', '..', '..', 'features', 'support') - def cache_dir - File.join(CACHE_STORAGE, self.class.name.split('::').last.downcase) + storage_dir = File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'tests', DRIVER.to_s, 'support') + File.join(storage_dir, self.class.name.split('::').last.downcase) end
def serialize_data(method_name, data, args) File.open(cache_file_name(method_name, args), 'w') do |f| f.puts(Base64.encode64(Marshal.dump(data))) end - puts "[MOCK] Serialized #{method_name}" + #puts "[MOCK] Serialized #{method_name}" return data end
def deserialize_data(method_name, args) begin + #puts "[MOCK] Loading serialized data for #{$scenario_prefix}_#{method_name}" data = File.readlines(cache_file_name(method_name, args)).join - puts "[MOCK] Loaded serialized data for #{method_name}" Marshal.load(Base64.decode64(data)) rescue Errno::ENOENT return false @@ -34,6 +34,7 @@ module Deltacloud::Mock
def cache_file_name(method_name, args) FileUtils.mkdir_p(cache_dir) unless File.directory?(cache_dir) + method_name = $scenario_prefix ? "#{$scenario_prefix}_#{method_name}" : method_name File.join(cache_dir, "#{method_name}.#{args_hash(args)}") end
@@ -46,12 +47,12 @@ module Deltacloud::Mock output = deserialize_data(m, args) unless output output = method("original_#{m}".to_sym).to_proc[args] - serialize_data(m, output, args) + serialize_data(m, output , args) unless output end return output end end - end + end end
end @@ -69,7 +70,7 @@ module Deltacloud::Mock :run_instances, :describe_instances, :reboot_instances, - :terminate_instances, + :terminate_instances ] end
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb index ef39ea1..8797567 100644 --- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb +++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb @@ -120,11 +120,11 @@ class EC2Driver < Deltacloud::BaseDriver config = { :owner_id => "amazon" } config.merge!({ :owner_id => opts[:owner_id] }) if opts and opts[:owner_id] end - safely do + #safely do ec2.describe_images(config).imagesSet.item.each do |image| img_arr << convert_image(image) end - end + #end img_arr = filter_on( img_arr, :architecture, opts ) img_arr.sort_by{|e| [e.owner_id, e.name]} end @@ -150,7 +150,7 @@ class EC2Driver < Deltacloud::BaseDriver def instances(credentials, opts=nil) ec2 = new_client(credentials) instances = [] - safely do + #safely do param = opts.nil? ? nil : opts[:id] ec2_instances = ec2.describe_instances.reservationSet return [] unless ec2_instances @@ -159,7 +159,7 @@ class EC2Driver < Deltacloud::BaseDriver instances << convert_instance( ec2_instance, item.ownerId ) end end - end + #end instances = filter_on( instances, :id, opts ) instances = filter_on( instances, :state, opts ) instances @@ -329,7 +329,7 @@ class EC2Driver < Deltacloud::BaseDriver rescue AWS::AuthFailure => e raise Deltacloud::AuthException.new rescue Exception => e - puts "ERROR: #{e.message}" + puts "ERROR: #{e.message}\n#{e.backtrace.join("\n")}" end end
diff --git a/server/lib/drivers.rb b/server/lib/drivers.rb index 6ef537c..dfa2361 100644 --- a/server/lib/drivers.rb +++ b/server/lib/drivers.rb @@ -24,14 +24,14 @@ end
def driver_mock_source_name return File.join('deltacloud', 'drivers', DRIVER.to_s, "#{DRIVER}_driver.rb") if driver_name.eql? 'Mock' - File.join('deltacloud', 'drivers', DRIVER, "#{DRIVER}_mock_driver.rb") end
def driver require driver_source_name + #require 'deltacloud/base_driver/mock_driver.rb'
if Sinatra::Application.environment.eql? :test - require driver_mock_source_name + require driver_mock_source_name if driver_mock_source_name end
@driver ||= eval( driver_class_name ).new diff --git a/tests/Rakefile b/tests/Rakefile new file mode 100644 index 0000000..7cddad3 --- /dev/null +++ b/tests/Rakefile @@ -0,0 +1,52 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require 'rake' +require 'rake/testtask' +require 'rake/gempackagetask' +require 'cucumber' +require 'cucumber/rake/task' + +DRIVER=ENV['API_DRIVER'] || 'mock' + +Cucumber::Rake::Task.new(:features) do |t| + t.cucumber_opts = "../tests/#{DRIVER} --format html --out ../tests/tmp/cucumber_#{DRIVER}.html" + t.rcov = false +end + +Cucumber::Rake::Task.new(:cucumber) do |t| + t.cucumber_opts = "../tests/#{DRIVER} --format pretty" + t.rcov = false +end + +Cucumber::Rake::Task.new(:rcov) do |t| + t.cucumber_opts = "../tests/#{DRIVER} --format pretty" + t.rcov = true + t.rcov_opts << %[-o "tmp/coverage_#{DRIVER}"] +end + +Cucumber::Rake::Task.new(:junit) do |t| + t.cucumber_opts = "../tests/#{DRIVER} --format junit --out ../tests/tmp/junit_#{DRIVER}" +end + +load '../server/deltacloud-core.gemspec' +Rake::GemPackageTask.new(@spec) do |pkg| + pkg.need_tar = true +end diff --git a/tests/ec2/99_rake.feature b/tests/ec2/99_rake.feature new file mode 100644 index 0000000..c4362c5 --- /dev/null +++ b/tests/ec2/99_rake.feature @@ -0,0 +1,7 @@ +Feature: Running rake tasks + + Scenario: I want to build Deltacloud API gem + Given I have a clean /pkg directory + When I run a 'package' task + Then I should see a gem file inside pkg directory + And I should see a tgz file inside pkg directory diff --git a/tests/ec2/api.feature b/tests/ec2/api.feature new file mode 100644 index 0000000..e6b1625 --- /dev/null +++ b/tests/ec2/api.feature @@ -0,0 +1,41 @@ +Feature: Accessing API entry points + + Scenario: API driver and version + Given URI /api exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'api' + And this element should have attribute 'driver' with value 'ec2' + And this element should have attribute 'version' with value '1.0' + + Scenario: List of entry points + Given URI /api exists + And authentification is not required for this URI + When client access this URI + Then client should get list of valid entry points: + | realms | + | instances | + | images | + | instance_states | + | hardware_profiles | + | storage_snapshots | + | storage_volumes | + And this URI should be available in XML, JSON, HTML format + + Scenario: Following entry points + Given URI /api exists + And authentification is not required for this URI + When client access this URI + Then client should get list of valid entry points + And each link should have 'rel' attribute with valid name + And each link should have 'href' attribute with valid URL + When client follow this attribute + Then client should get a valid response + + Scenario: Instance features + Given URI /api exists + And authentification is not required for this URI + When client access this URI + Then client should get list of features inside 'instances': + | authentication_key | + | user_data | diff --git a/tests/ec2/hardware_profiles.feature b/tests/ec2/hardware_profiles.feature new file mode 100644 index 0000000..616ee4b --- /dev/null +++ b/tests/ec2/hardware_profiles.feature @@ -0,0 +1,29 @@ +Feature: Accessing hardware profiles + + Scenario: I want to get list of all hardware profiles + Given URI /api/hardware_profiles exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'hardware-profiles' + And this URI should be available in XML, HTML, JSON format + + Scenario: I want to show hardware profile details + Given URI /api/hardware_profiles exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'hardware-profiles' + When client want to show 'm1.small' hardware-profile + And client should get this hardware-profile + And it should have a href attribute + And it should have a fixed property 'cpu' + And it should have a range property 'memory' + And it should have a enum property 'storage' + And this URI should be available in XML, HTML, JSON format + + Scenario: Filtering images by architecture + Given URI /api/hardware_profiles exists + And authentification is required for this URI + When client access this URI with parameters: + | architecture | i386 | + Then client should get some hardware-profiles + And each hardware-profile should have 'architecture' attribute set to 'i386' diff --git a/tests/ec2/images.feature b/tests/ec2/images.feature new file mode 100644 index 0000000..af1e705 --- /dev/null +++ b/tests/ec2/images.feature @@ -0,0 +1,59 @@ +Feature: Listing and showing images + + Scenario: Listing available images + Given URI /api/images exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'images' + And this element contains some images + And each image should have: + | id | + | name | + | description | + | architecture | + | owner_id | + And each image should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Following image href attribute + Given URI /api/images exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'images' + And this element contains some images + When client want to show first image + Then client should follow href attribute in image + And client should get valid response with requested image + And this image should have: + | id | + | name | + | description | + | architecture | + | owner_id | + And this URI should be available in XML, JSON, HTML format + + Scenario: Filtering images by owner_id + Given URI /api/images exists + And authentification is required for this URI + When client access this URI with parameters: + | owner_id | 205605819716 | + Then client should get some images + And each image should have 'owner_id' attribute set to '205605819716' + + Scenario: Filtering images by architecture + Given URI /api/images exists + And authentification is required for this URI + When client access this URI with parameters: + | architecture | i386 | + Then client should get some images + And each image should have 'architecture' attribute set to 'i386' + + Scenario: Filtering images by architecture and owner_id + Given URI /api/images exists + And authentification is required for this URI + When client access this URI with parameters: + | architecture | i386 | + | owner_id | 205605819716 | + Then client should get some images + And each image should have 'architecture' attribute set to 'i386' + And each image should have 'owner_id' attribute set to '205605819716' diff --git a/tests/ec2/instance_states.feature b/tests/ec2/instance_states.feature new file mode 100644 index 0000000..3008ef0 --- /dev/null +++ b/tests/ec2/instance_states.feature @@ -0,0 +1,36 @@ +Feature: Instance states and transitions + + Scenario: Getting list of states + Given URI /api/instance_states exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'states' + And this element contains some states + And this URI should be available in XML, JSON, HTML format + + + Scenario: State names + Given URI /api/instance_states exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'states' + And states element contains some states + And each state should have 'name' attribute + And first state should have 'name' attribute set to 'start' + And last state should have 'name' attribute set to 'finish' + + Scenario: Transitions + Given URI /api/instance_states exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'states' + And states element contains some states + And some states should have transitions + And each transitions should have 'to' attribute + + Scenario: State diagram + Given URI /api/instance_states exists + And authentification is not required for this URI + When client access this URI + And client wants PNG format + Then client should get PNG image diff --git a/tests/ec2/instances.feature b/tests/ec2/instances.feature new file mode 100644 index 0000000..336bdfb --- /dev/null +++ b/tests/ec2/instances.feature @@ -0,0 +1,171 @@ +Feature: Managing instances + + Scenario: Listing current instances + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + And each instance should have: + | id | + | name | + | owner_id | + | image | + | realm | + | state | + | hardware-profile | + | actions | + | public-addresses | + | private-addresses | + And each instance should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Filtering instances by state + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI with parameters: + | state | RUNNING | + Then client should get some instances + And each instance should have 'state' attribute set to 'RUNNING' + + Scenario: Get details about first instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client want to show first instance + Then client follow href attribute in first instance + Then client should get this instance + And this instance should have: + | id | + | name | + | owner_id | + | image | + | realm | + | state | + | hardware-profile | + | actions | + | public-addresses | + | private-addresses | + | authentication | + + Scenario: Following image href in instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client follow image href attribute in first instance + Then client should get valid image + + Scenario: Following realm href in instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client follow realm href attribute in first instance + Then client should get valid realm + + Scenario: Following hardware profile href in instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client follow hardware-profile href attribute in first instance + Then client should get valid hardware-profile + + Scenario: Instance actions + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + And each instance should have actions + And each actions should have some links + And each link should have valid href attribute + And each link should have valid method attribute + And each link should have valid rel attribute + + @prefix-reboot + Scenario: Reboot instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client want to 'reboot' first instance + And client follow link in actions + Then client should get first instance + And this instance should be in 'RUNNING' state + + @prefix-stop + Scenario: Stop instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client want to 'stop' first instance + And client follow link in actions + Then client should get first instance + And this instance should be in 'STOPPED' state + + @prefix-start + Scenario: Start instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client want to 'start' first instance + And client follow link in actions + Then client should get first instance + And this instance should be in 'RUNNING' state + + @prefix-create + Scenario: Basic instance creation + Given URI /api/instances exists + And authentification is required for this URI + When client want to create a new instance + Then client should choose first image + When client request for a new instance + Then new instance should be created + And this instance should have chosed image + And this instance should be in 'RUNNING' state + And this instance should have valid id + And this instance should have name + + @prefix-create-hwp + Scenario: Choosing hardware profile for instance + Given URI /api/instances exists + And authentification is required for this URI + When client want to create a new instance + Then client should choose first image + And client choose first hardware profile + When client request for a new instance + Then new instance should be created + And this instance should have chosed image + And this instance should be in 'RUNNING' state + And this instance should have valid id + And this instance should have last hardware profile + And this instance should have name + + Scenario: Create instance using HTML form + Given URI /api/instances/new exists in HTML format + And authentification is required for this URI + When client access this URI + Then client should get HTML form + + Scenario: Destroying created instance + Given URI /api/instances exists + And authentification is required for this URI + When client want to 'stop' created instance + And client follow link in actions + Then client should get created instance + And this instance should be in 'STOPPED' state + When client want to 'destroy' created instance + And client follow link in actions + And this instance should be destroyed diff --git a/tests/ec2/realms.feature b/tests/ec2/realms.feature new file mode 100644 index 0000000..a89173b --- /dev/null +++ b/tests/ec2/realms.feature @@ -0,0 +1,31 @@ +Feature: Accessing realms + + Scenario: Getting list of available realms + Given URI /api/realms exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'realms' + And this element contains some realms + And each realm should have: + | id | + | name | + | state | + | limit | + And each realm should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Following realm href attribute + Given URI /api/realms exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'realms' + And this element contains some realms + When client want to show first realm + Then client should follow href attribute in realm + And client should get valid response with requested realm + And this realm should have: + | id | + | name | + | state | + | limit | + And this URI should be available in XML, JSON, HTML format diff --git a/tests/ec2/step_definitions/api_steps.rb b/tests/ec2/step_definitions/api_steps.rb new file mode 100644 index 0000000..9a30c3d --- /dev/null +++ b/tests/ec2/step_definitions/api_steps.rb @@ -0,0 +1,103 @@ +Given /^URI ([\w/-_]+) exists$/ do |uri| + get uri, {} + last_response.status.should_not == 404 + last_response.status.should_not == 500 + @uri = uri +end + +Given /^URI ([\w/-_]+) exists in (.+) format$/ do |uri, format| + @uri = "#{uri}.#{format.downcase}" + get @uri, {} + last_response.status.should_not == 404 + last_response.status.should_not == 500 +end + +Given /^authentification is not required for this URI$/ do + last_response.status.should_not == 401 +end + +When /^client access this URI$/ do + get @uri, {} + last_response.status.should_not == 404 +end + +Then /^client should get root element '(.+)'$/ do |element| + @last_element = output_xml.xpath('/'+element).first + @last_element.should_not be_nil + @last_element.name.should == element +end + +Then /^this element should have attribute '(.+)' with value '(.+)'$/ do |atr, val| + @last_element[atr.to_sym].should == val +end + +Then /^client should get list of valid entry points:$/ do |table| + @entry_points = table.raw.flatten.sort + links = [] + output_xml.xpath('/api/link').each do |entry_point| + links << entry_point['rel'] + end + @entry_points.should == links.sort +end + +Then /^this URI should be available in (.+) format$/ do |formats| + @no_header = true + formats.split(',').each do |format| + get "#{@uri}.#{format.strip.downcase}", {} + last_response.status.should == 200 + end + @no_header = false +end + +Then /^client should get list of valid entry points$/ do + links = [] + output_xml.xpath('/api/link').each do |entry_point| + links << entry_point['rel'] + end + @entry_points.should == links.sort +end + +Then /^each (\w+) should have '(.+)' attribute with valid (.+)$/ do |el, attr, t| + case el + when 'link': + path = '/api/link' + when 'image': + path = '/images/image' + end + output_xml.xpath(path).each do |entry_point| + @entry_points.include?(entry_point[attr]).should == true if t=='name' + if t=='URL' + entry_point[:href].should_not be_nil + end + end + @last_attribute = attr +end + +Then /^each ([\w-]+) should have '(.+)' attribute set to '(.+)'$/ do |el, attr, v| + case el + when 'image': + path = "/image/images" + end + output_xml.xpath(path).each do |element| + element[attr].should == v + end +end + +When /^client follow this attribute$/ do + output_xml.xpath('/api/link').each do |entry_point| + get entry_point[@last_attribute], {} + end +end + +Then /^client should get a valid response$/ do + last_response.status.should_not == 500 +end + +Then /^client should get list of features inside '(.+)':$/ do |element,table| + features = table.raw.flatten.sort + instance_features = [] + output_xml.xpath('/api/link[@rel="'+element+'"]/feature').each do |feature| + instance_features << feature[:name] + end + features.should == instance_features.sort +end diff --git a/tests/ec2/step_definitions/hardware_profiles_steps.rb b/tests/ec2/step_definitions/hardware_profiles_steps.rb new file mode 100644 index 0000000..db1828b --- /dev/null +++ b/tests/ec2/step_definitions/hardware_profiles_steps.rb @@ -0,0 +1,25 @@ +Then /^it should have a (\w+) attribute$/ do |name| + attr = output_xml.xpath('/hardware-profile').first[name] + attr.should_not be_nil +end + +Then /^it should have a (\w+) property '(.+)'$/ do |kind, name| + props = output_xml.xpath("/hardware-profile/property[@name = '#{name}']") + props.size.should == 1 + prop = props.first + prop['kind'].should == kind + prop['unit'].should_not be_nil + if kind == 'range' + ranges = prop.xpath('range') + ranges.size.should == 1 + range = ranges.first + range['first'].should_not be_nil + range['last'].should_not be_nil + end + if kind == 'enum' + enums = prop.xpath('enum') + enums.size.should == 1 + enums.first.xpath('entry').size.should_not == 0 + end +end + diff --git a/tests/ec2/step_definitions/images_steps.rb b/tests/ec2/step_definitions/images_steps.rb new file mode 100644 index 0000000..fc01364 --- /dev/null +++ b/tests/ec2/step_definitions/images_steps.rb @@ -0,0 +1,83 @@ +Given /^authentification is required for this URI$/ do + authorize CONFIG[:username], CONFIG[:password] + get @uri, {} + last_response.status.should == 200 +end + +Then /^this element contains some (.+)$/ do |items| + item = items.singularize + output_xml.xpath("/#{@last_element.name}/#{item}").size.should > 0 +end + +Then /^each ([\w-]+) should have:$/ do |item, table| + properties = table.raw.flatten.sort + output_xml.xpath("/#{@last_element.name}/#{item}").each do |element| + childrens = (element > '*').collect { |c| c.name } + childrens.sort.should == properties + end +end + +Then /^this ([\w-]+) should have:$/ do |item, table| + properties = table.raw.flatten.sort + output_xml.xpath("/#{item}").each do |element| + childrens = (element > '*').collect { |c| c.name } + childrens.sort.should == properties + end +end + +When /^client want to show first (.+)$/ do |element| + case element + when 'image': + path = '/images/image' + when 'instance': + path = '/instances/instance' + when 'realm': + path = '/realms/realm' + when 'hardware-profile' + path = '/hardware-profiles/hardware-profile' + when 'storage-volume': + path = '/storage-volumes/storage-volume' + when 'storage-snapshot': + path = '/storage-snapshots/storage-snapshot' + end + @element = output_xml.xpath(path).first + @element.should_not be_nil +end + +When /^client want to show '(.+)' (.+)$/ do |id, el| + @uri = "/api/#{el.pluralize.tr('-', '_')}/#{id}" + get @uri, {} + @element = output_xml.xpath("/#{el}").first + @element.should_not be_nil +end + +Then /^client follow (\w+) attribute in first (.+)$/ do |attr, el| + url = output_xml.xpath("/#{el.pluralize}/#{el}").first[:href] + url.should_not be_nil + get url, {} +end + +Then /^client should get this (.+)$/ do |el| + last_response.status.should == 200 +end + + +Then /^client should follow href attribute in (\w+)$/ do |element| + get @element[:href], {} +end + +Then /^client should get valid response with requested (\w+)$/ do |element| + last_response.status.should == 200 + output_xml.xpath('/'+element+'/id').first.text.should == @element.xpath('id').text +end + +When /^client access this URI with parameters:$/ do |table| + params = {} + table.raw.each { |i| params[i[0]]=i[1] } + get @uri, params +end + +Then /^client should get some ([\w-]+)$/ do |elements| + last_response.status.should == 200 + output_xml.xpath('/'+elements+'/'+elements.singularize).size.should > 0 +end diff --git a/tests/ec2/step_definitions/instance_states_steps.rb b/tests/ec2/step_definitions/instance_states_steps.rb new file mode 100644 index 0000000..a587e5d --- /dev/null +++ b/tests/ec2/step_definitions/instance_states_steps.rb @@ -0,0 +1,34 @@ +Then /^states element contains some states$/ do + output_xml.xpath('/states/state').size.should > 0 +end + +Then /^each state should have '(.+)' attribute$/ do |attr| + output_xml.xpath('/states/state').each do |state| + state[attr].should_not be_nil + end +end + +Then /^(\w+) state should have '(.+)' attribute set to '(\w+)'$/ do |pos, attr, value| + output_xml.xpath('/states/state').first[attr].should==value if pos=='first' + output_xml.xpath('/states/state').last[attr].should==value if pos=='last' +end + +Then /^some states should have transitions$/ do + @transitions = output_xml.xpath('/states/state/transition') + @transitions.size.should > 0 +end + +Then /^each transitions should have 'to' attribute$/ do + @transitions.each do |t| + t[:to].should_not be_nil + end +end + +When /^client wants (\w+) format$/ do |format| + get "#{@uri}.#{format.downcase}", {} +end + +Then /^client should get PNG image$/ do + last_response.status.should == 200 + last_response.headers['Content-Type'].should == 'image/png' +end diff --git a/tests/ec2/step_definitions/instances_steps.rb b/tests/ec2/step_definitions/instances_steps.rb new file mode 100644 index 0000000..224ef50 --- /dev/null +++ b/tests/ec2/step_definitions/instances_steps.rb @@ -0,0 +1,137 @@ +When /^client follow ([\w-]+) href attribute in first instance$/ do |element| + get output_xml.xpath('/instances/instance[1]/'+element).first[:href], {} +end + +Then /^client should get valid ([\w-]+)$/ do |element| + last_response.status.should == 200 + output_xml.xpath("/#{element}").first.should_not be_nil +end + +Then /^each instance should have actions$/ do + output_xml.xpath('/instances/instance').each do |instance| + instance.xpath('actions').first.should_not be_nil + end +end + +Then /^each actions should have some links$/ do + output_xml.xpath('/instances/instance').each do |instance| + instance.xpath('actions/link').first.should_not be_nil + end +end + +Then /^each link should have valid (\w+) attribute$/ do |attr| + output_xml.xpath('/instances/instance').each do |instance| + instance.xpath('actions/link').first[attr].should_not be_nil + end +end + +When /^client want to '(\w+)' first instance$/ do |action| + @action = action + @instance = output_xml.xpath('/instances/instance[1]').first +end + +When /^client follow link in actions$/ do + unless @instance_url + l = output_xml.xpath("/instances/instance[1]/actions/link[@rel = '#{@action}']").first + else + l = @instance.xpath('actions/link[@rel="'+@action+'"]').first + end + unless @action=='destroy' + post l[:href], { :id => @instance.xpath('id').first.text } + else + delete l[:href], { :id => @instance.xpath('id').first.text } + end + last_response.status.should_not == 500 +end + +Then /^client should get first instance$/ do + output_xml.xpath('/instance/id').first.should_not be_nil + #output_xml.xpath('/instance/id').first.text.should == @instance.xpath('id').first.text +end + +Then /^this instance should be in '(.+)' state$/ do |state| + output_xml.xpath('/instance/state').first.text.should == state +end + +When /^client want to create a new instance$/ do +end + +Then /^client should choose (\w+) image$/ do |position| + get '/api/images', {} + if position=='first' + @image = output_xml.xpath('/images/image').first + else + @image = output_xml.xpath('/images/image').last + end + @image.should_not be_nil +end + +When /^client request for a new instance$/ do + params = { + :image_id => @image.xpath('id').first.text + } + params[:hwp_id] = @hwp_id if @hwp_id + post "#{@uri}", params + last_response.status.should == 201 + @instance_url = last_response.headers['Location'] +end + +Then /^new instance should be created$/ do + get @instance_url, {} + last_response.status.should == 200 +end + +Then /^this instance should have chosed image$/ do + output_xml.xpath('/instance/image').first[:href].should == @image[:href] +end + +Then /^this instance should have valid id$/ do + output_xml.xpath('instance/id').first.should_not be_nil +end + +Then /^this instance should have name$/ do + output_xml.xpath('instance/name').first.should_not be_nil +end + +When /^client want to '(\w+)' created instance$/ do |action| + get @instance_url, {} + last_response.status.should == 200 + @action = action + @instance = output_xml.xpath('/instance') +end + +Then /^client should get created instance$/ do + get @instance_url +end + +When /^this instance should be destroyed$/ do + # TODO: Fix this bug in mock driver ? + #get @instance_url, {} + #last_response.status.should == 404 +end + +Then /^client should get HTML form$/ do + last_response.status.should == 200 + (last_response.body.strip =~ /^<!DOCTYPE html/).should be_true +end + +When /^client choose (\w+) hardware profile$/ do |position| + get '/api/hardware_profiles', {} + if position=='last' + @hwp_id = output_xml.xpath('/hardware-profiles/hardware-profile/id').last.text + else + @hwp_id = output_xml.xpath('/hardware-profiles/hardware-profile/id').first.text + end +end + +Then /^this instance should have last hardware profile$/ do + output_xml.xpath('instance/hardware-profile/id').first.text.should == @hwp_id +end + +Given /^I set mock scenario to (\w+)$/ do |scenario| + @scenario = scenario +end + +Then /^I set mock scenario to default$/ do + @scenario = '' +end diff --git a/tests/ec2/step_definitions/rake_steps.rb b/tests/ec2/step_definitions/rake_steps.rb new file mode 100644 index 0000000..c8ea3d9 --- /dev/null +++ b/tests/ec2/step_definitions/rake_steps.rb @@ -0,0 +1,19 @@ +require 'fileutils' +require 'rake' +require 'find' + +Given /^I have a clean (.+) directory$/ do |dir| + FileUtils.rm_rf dir +end + +When /^I run a '(\w+)' task$/ do |task| + @rake = Rake::Application.new + Rake.application = @rake + load "Rakefile" + @task = Rake::Task[task] + @task.invoke +end + +Then /^I should see a (\w+) file inside (\w+) directory$/ do |ext, dir| + Dir["#{dir}/deltacloud-*.#{ext}"].size.should == 1 +end diff --git a/tests/ec2/storage_snapshots.feature b/tests/ec2/storage_snapshots.feature new file mode 100644 index 0000000..f6f38e5 --- /dev/null +++ b/tests/ec2/storage_snapshots.feature @@ -0,0 +1,29 @@ +Feature: Accessing storage snapshots + + Scenario: Listing available storage snapshots + Given URI /api/storage_snapshots exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'storage-snapshots' + And this element contains some storage-snapshots + And each storage-volume should have: + | id | + | created | + | storage-volume | + And each image should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Get details about first volume + Given URI /api/storage_snapshots exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'storage-snapshots' + And this element contains some storage-snapshots + When client want to show first storage-snapshot + Then client follow href attribute in first storage-snapshot + Then client should get this storage-snapshot + And this storage-snapshot should have: + | id | + | created | + | state | + | storage-volume | diff --git a/tests/ec2/storage_volumes.feature b/tests/ec2/storage_volumes.feature new file mode 100644 index 0000000..dfca4b0 --- /dev/null +++ b/tests/ec2/storage_volumes.feature @@ -0,0 +1,33 @@ +Feature: Accessing storage volumes + + Scenario: Listing available storage volumes + Given URI /api/storage_volumes exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'storage-volumes' + And this element contains some storage-volumes + And each storage-volume should have: + | id | + | created | + | capacity | + | device | + | instance | + And each image should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Get details about first volume + Given URI /api/storage_volumes exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'storage-volumes' + And this element contains some storage-volumes + When client want to show first storage-volume + Then client follow href attribute in first storage-volume + Then client should get this storage-volume + And this storage-volume should have: + | id | + | created | + | capacity | + | device | + | instance | + | state | diff --git a/tests/ec2/support/env.rb b/tests/ec2/support/env.rb new file mode 100644 index 0000000..ac4adfd --- /dev/null +++ b/tests/ec2/support/env.rb @@ -0,0 +1,53 @@ +SERVER_DIR = File::expand_path(File::join(File::dirname(__FILE__), "../../../server")) +Dir.chdir(SERVER_DIR) + +require 'sinatra' +require 'rack/test' +require 'nokogiri' +require '../server/server' +require 'ap' + +Sinatra::Application.set :environment, :test +Sinatra::Application.set :root, SERVER_DIR + +require '../server/lib/deltacloud/base_driver/mock_driver' + +CONFIG = { + :username => 'mockuser', + :password => 'mockpassword' +} + +World do + + def app + @app = Rack::Builder.new do + set :logging, true + set :raise_errors, true + run Sinatra::Application + end + end + + def output_xml + Nokogiri::XML(last_response.body) + end + + Before do + unless @no_header + header 'Accept', 'application/xml' + end + end + + prefixes = %W{ @prefix-start, @prefix-reboot, @prefix-stop, @prefix-create, @prefix-create-hwp} + + Before(prefixes.join(',')) do |scenario| + prefix = scenario.source_tag_names.first.gsub(/@prefix-/, '') + $scenario_prefix = prefix + end + + After(prefixes.join(',')) do |scenario| + $scenario_prefix = nil + end + + include Rack::Test::Methods +end + diff --git a/tests/mock/99_rake.feature b/tests/mock/99_rake.feature new file mode 100644 index 0000000..c4362c5 --- /dev/null +++ b/tests/mock/99_rake.feature @@ -0,0 +1,7 @@ +Feature: Running rake tasks + + Scenario: I want to build Deltacloud API gem + Given I have a clean /pkg directory + When I run a 'package' task + Then I should see a gem file inside pkg directory + And I should see a tgz file inside pkg directory diff --git a/tests/mock/api.feature b/tests/mock/api.feature new file mode 100644 index 0000000..8c4ddd2 --- /dev/null +++ b/tests/mock/api.feature @@ -0,0 +1,41 @@ +Feature: Accessing API entry points + + Scenario: API driver and version + Given URI /api exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'api' + And this element should have attribute 'driver' with value 'mock' + And this element should have attribute 'version' with value '1.0' + + Scenario: List of entry points + Given URI /api exists + And authentification is not required for this URI + When client access this URI + Then client should get list of valid entry points: + | realms | + | instances | + | images | + | instance_states | + | hardware_profiles | + | storage_snapshots | + | storage_volumes | + And this URI should be available in XML, JSON, HTML format + + Scenario: Following entry points + Given URI /api exists + And authentification is not required for this URI + When client access this URI + Then client should get list of valid entry points + And each link should have 'rel' attribute with valid name + And each link should have 'href' attribute with valid URL + When client follow this attribute + Then client should get a valid response + + Scenario: Instance features + Given URI /api exists + And authentification is not required for this URI + When client access this URI + Then client should get list of features inside 'instances': + | hardware_profiles | + | user_name | diff --git a/tests/mock/hardware_profiles.feature b/tests/mock/hardware_profiles.feature new file mode 100644 index 0000000..f10bb13 --- /dev/null +++ b/tests/mock/hardware_profiles.feature @@ -0,0 +1,29 @@ +Feature: Accessing hardware profiles + + Scenario: I want to get list of all hardware profiles + Given URI /api/hardware_profiles exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'hardware-profiles' + And this URI should be available in XML, HTML, JSON format + + Scenario: I want to show hardware profile details + Given URI /api/hardware_profiles exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'hardware-profiles' + When client want to show 'm1-large' hardware-profile + And client should get this hardware-profile + And it should have a href attribute + And it should have a fixed property 'cpu' + And it should have a range property 'memory' + And it should have a enum property 'storage' + And this URI should be available in XML, HTML, JSON format + + Scenario: Filtering images by architecture + Given URI /api/hardware_profiles exists + And authentification is required for this URI + When client access this URI with parameters: + | architecture | i386 | + Then client should get some hardware-profiles + And each hardware-profile should have 'architecture' attribute set to 'i386' diff --git a/tests/mock/images.feature b/tests/mock/images.feature new file mode 100644 index 0000000..50c3570 --- /dev/null +++ b/tests/mock/images.feature @@ -0,0 +1,59 @@ +Feature: Listing and showing images + + Scenario: Listing available images + Given URI /api/images exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'images' + And this element contains some images + And each image should have: + | id | + | name | + | description | + | architecture | + | owner_id | + And each image should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Following image href attribute + Given URI /api/images exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'images' + And this element contains some images + When client want to show first image + Then client should follow href attribute in image + And client should get valid response with requested image + And this image should have: + | id | + | name | + | description | + | architecture | + | owner_id | + And this URI should be available in XML, JSON, HTML format + + Scenario: Filtering images by owner_id + Given URI /api/images exists + And authentification is required for this URI + When client access this URI with parameters: + | owner_id | fedoraproject | + Then client should get some images + And each image should have 'owner_id' attribute set to 'fedoraproject' + + Scenario: Filtering images by architecture + Given URI /api/images exists + And authentification is required for this URI + When client access this URI with parameters: + | architecture | i386 | + Then client should get some images + And each image should have 'architecture' attribute set to 'i386' + + Scenario: Filtering images by architecture and owner_id + Given URI /api/images exists + And authentification is required for this URI + When client access this URI with parameters: + | architecture | i386 | + | owner_id | fedoraproject | + Then client should get some images + And each image should have 'architecture' attribute set to 'i386' + And each image should have 'owner_id' attribute set to 'fedoraproject' diff --git a/tests/mock/instance_states.feature b/tests/mock/instance_states.feature new file mode 100644 index 0000000..3008ef0 --- /dev/null +++ b/tests/mock/instance_states.feature @@ -0,0 +1,36 @@ +Feature: Instance states and transitions + + Scenario: Getting list of states + Given URI /api/instance_states exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'states' + And this element contains some states + And this URI should be available in XML, JSON, HTML format + + + Scenario: State names + Given URI /api/instance_states exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'states' + And states element contains some states + And each state should have 'name' attribute + And first state should have 'name' attribute set to 'start' + And last state should have 'name' attribute set to 'finish' + + Scenario: Transitions + Given URI /api/instance_states exists + And authentification is not required for this URI + When client access this URI + Then client should get root element 'states' + And states element contains some states + And some states should have transitions + And each transitions should have 'to' attribute + + Scenario: State diagram + Given URI /api/instance_states exists + And authentification is not required for this URI + When client access this URI + And client wants PNG format + Then client should get PNG image diff --git a/tests/mock/instances.feature b/tests/mock/instances.feature new file mode 100644 index 0000000..64828ea --- /dev/null +++ b/tests/mock/instances.feature @@ -0,0 +1,165 @@ +Feature: Managing instances + + Scenario: Listing current instances + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + And each instance should have: + | id | + | name | + | owner_id | + | image | + | realm | + | state | + | hardware-profile | + | actions | + | public-addresses | + | private-addresses | + And each instance should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Filtering instances by state + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI with parameters: + | state | RUNNING | + Then client should get some instances + And each instance should have 'state' attribute set to 'RUNNING' + + Scenario: Get details about first instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client want to show first instance + Then client follow href attribute in first instance + Then client should get this instance + And this instance should have: + | id | + | name | + | owner_id | + | image | + | realm | + | state | + | hardware-profile | + | actions | + | public-addresses | + | private-addresses | + + Scenario: Following image href in instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client follow image href attribute in first instance + Then client should get valid image + + Scenario: Following realm href in instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client follow realm href attribute in first instance + Then client should get valid realm + + Scenario: Following hardware profile href in instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client follow hardware-profile href attribute in first instance + Then client should get valid hardware-profile + + Scenario: Instance actions + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + And each instance should have actions + And each actions should have some links + And each link should have valid href attribute + And each link should have valid method attribute + And each link should have valid rel attribute + + Scenario: Reboot instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client want to 'reboot' first instance + And client follow link in actions + Then client should get first instance + And this instance should be in 'RUNNING' state + + Scenario: Stop instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client want to 'stop' first instance + And client follow link in actions + Then client should get first instance + And this instance should be in 'STOPPED' state + + Scenario: Start instance + Given URI /api/instances exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'instances' + And this element contains some instances + When client want to 'start' first instance + And client follow link in actions + Then client should get first instance + And this instance should be in 'RUNNING' state + + Scenario: Basic instance creation + Given URI /api/instances exists + And authentification is required for this URI + When client want to create a new instance + Then client should choose first image + When client request for a new instance + Then new instance should be created + And this instance should have chosed image + And this instance should be in 'RUNNING' state + And this instance should have valid id + And this instance should have name + + Scenario: Choosing hardware profile for instance + Given URI /api/instances exists + And authentification is required for this URI + When client want to create a new instance + Then client should choose first image + And client choose last hardware profile + When client request for a new instance + Then new instance should be created + And this instance should have chosed image + And this instance should be in 'RUNNING' state + And this instance should have valid id + And this instance should have last hardware profile + And this instance should have name + + Scenario: Create instance using HTML form + Given URI /api/instances/new exists in HTML format + And authentification is required for this URI + When client access this URI + Then client should get HTML form + + Scenario: Destroying created instance + Given URI /api/instances exists + And authentification is required for this URI + When client want to 'stop' created instance + And client follow link in actions + Then client should get created instance + And this instance should be in 'STOPPED' state + When client want to 'destroy' created instance + And client follow link in actions + And this instance should be destroyed diff --git a/tests/mock/realms.feature b/tests/mock/realms.feature new file mode 100644 index 0000000..a89173b --- /dev/null +++ b/tests/mock/realms.feature @@ -0,0 +1,31 @@ +Feature: Accessing realms + + Scenario: Getting list of available realms + Given URI /api/realms exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'realms' + And this element contains some realms + And each realm should have: + | id | + | name | + | state | + | limit | + And each realm should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Following realm href attribute + Given URI /api/realms exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'realms' + And this element contains some realms + When client want to show first realm + Then client should follow href attribute in realm + And client should get valid response with requested realm + And this realm should have: + | id | + | name | + | state | + | limit | + And this URI should be available in XML, JSON, HTML format diff --git a/tests/mock/step_definitions/api_steps.rb b/tests/mock/step_definitions/api_steps.rb new file mode 100644 index 0000000..9a30c3d --- /dev/null +++ b/tests/mock/step_definitions/api_steps.rb @@ -0,0 +1,103 @@ +Given /^URI ([\w/-_]+) exists$/ do |uri| + get uri, {} + last_response.status.should_not == 404 + last_response.status.should_not == 500 + @uri = uri +end + +Given /^URI ([\w/-_]+) exists in (.+) format$/ do |uri, format| + @uri = "#{uri}.#{format.downcase}" + get @uri, {} + last_response.status.should_not == 404 + last_response.status.should_not == 500 +end + +Given /^authentification is not required for this URI$/ do + last_response.status.should_not == 401 +end + +When /^client access this URI$/ do + get @uri, {} + last_response.status.should_not == 404 +end + +Then /^client should get root element '(.+)'$/ do |element| + @last_element = output_xml.xpath('/'+element).first + @last_element.should_not be_nil + @last_element.name.should == element +end + +Then /^this element should have attribute '(.+)' with value '(.+)'$/ do |atr, val| + @last_element[atr.to_sym].should == val +end + +Then /^client should get list of valid entry points:$/ do |table| + @entry_points = table.raw.flatten.sort + links = [] + output_xml.xpath('/api/link').each do |entry_point| + links << entry_point['rel'] + end + @entry_points.should == links.sort +end + +Then /^this URI should be available in (.+) format$/ do |formats| + @no_header = true + formats.split(',').each do |format| + get "#{@uri}.#{format.strip.downcase}", {} + last_response.status.should == 200 + end + @no_header = false +end + +Then /^client should get list of valid entry points$/ do + links = [] + output_xml.xpath('/api/link').each do |entry_point| + links << entry_point['rel'] + end + @entry_points.should == links.sort +end + +Then /^each (\w+) should have '(.+)' attribute with valid (.+)$/ do |el, attr, t| + case el + when 'link': + path = '/api/link' + when 'image': + path = '/images/image' + end + output_xml.xpath(path).each do |entry_point| + @entry_points.include?(entry_point[attr]).should == true if t=='name' + if t=='URL' + entry_point[:href].should_not be_nil + end + end + @last_attribute = attr +end + +Then /^each ([\w-]+) should have '(.+)' attribute set to '(.+)'$/ do |el, attr, v| + case el + when 'image': + path = "/image/images" + end + output_xml.xpath(path).each do |element| + element[attr].should == v + end +end + +When /^client follow this attribute$/ do + output_xml.xpath('/api/link').each do |entry_point| + get entry_point[@last_attribute], {} + end +end + +Then /^client should get a valid response$/ do + last_response.status.should_not == 500 +end + +Then /^client should get list of features inside '(.+)':$/ do |element,table| + features = table.raw.flatten.sort + instance_features = [] + output_xml.xpath('/api/link[@rel="'+element+'"]/feature').each do |feature| + instance_features << feature[:name] + end + features.should == instance_features.sort +end diff --git a/tests/mock/step_definitions/hardware_profiles_steps.rb b/tests/mock/step_definitions/hardware_profiles_steps.rb new file mode 100644 index 0000000..db1828b --- /dev/null +++ b/tests/mock/step_definitions/hardware_profiles_steps.rb @@ -0,0 +1,25 @@ +Then /^it should have a (\w+) attribute$/ do |name| + attr = output_xml.xpath('/hardware-profile').first[name] + attr.should_not be_nil +end + +Then /^it should have a (\w+) property '(.+)'$/ do |kind, name| + props = output_xml.xpath("/hardware-profile/property[@name = '#{name}']") + props.size.should == 1 + prop = props.first + prop['kind'].should == kind + prop['unit'].should_not be_nil + if kind == 'range' + ranges = prop.xpath('range') + ranges.size.should == 1 + range = ranges.first + range['first'].should_not be_nil + range['last'].should_not be_nil + end + if kind == 'enum' + enums = prop.xpath('enum') + enums.size.should == 1 + enums.first.xpath('entry').size.should_not == 0 + end +end + diff --git a/tests/mock/step_definitions/images_steps.rb b/tests/mock/step_definitions/images_steps.rb new file mode 100644 index 0000000..fc01364 --- /dev/null +++ b/tests/mock/step_definitions/images_steps.rb @@ -0,0 +1,83 @@ +Given /^authentification is required for this URI$/ do + authorize CONFIG[:username], CONFIG[:password] + get @uri, {} + last_response.status.should == 200 +end + +Then /^this element contains some (.+)$/ do |items| + item = items.singularize + output_xml.xpath("/#{@last_element.name}/#{item}").size.should > 0 +end + +Then /^each ([\w-]+) should have:$/ do |item, table| + properties = table.raw.flatten.sort + output_xml.xpath("/#{@last_element.name}/#{item}").each do |element| + childrens = (element > '*').collect { |c| c.name } + childrens.sort.should == properties + end +end + +Then /^this ([\w-]+) should have:$/ do |item, table| + properties = table.raw.flatten.sort + output_xml.xpath("/#{item}").each do |element| + childrens = (element > '*').collect { |c| c.name } + childrens.sort.should == properties + end +end + +When /^client want to show first (.+)$/ do |element| + case element + when 'image': + path = '/images/image' + when 'instance': + path = '/instances/instance' + when 'realm': + path = '/realms/realm' + when 'hardware-profile' + path = '/hardware-profiles/hardware-profile' + when 'storage-volume': + path = '/storage-volumes/storage-volume' + when 'storage-snapshot': + path = '/storage-snapshots/storage-snapshot' + end + @element = output_xml.xpath(path).first + @element.should_not be_nil +end + +When /^client want to show '(.+)' (.+)$/ do |id, el| + @uri = "/api/#{el.pluralize.tr('-', '_')}/#{id}" + get @uri, {} + @element = output_xml.xpath("/#{el}").first + @element.should_not be_nil +end + +Then /^client follow (\w+) attribute in first (.+)$/ do |attr, el| + url = output_xml.xpath("/#{el.pluralize}/#{el}").first[:href] + url.should_not be_nil + get url, {} +end + +Then /^client should get this (.+)$/ do |el| + last_response.status.should == 200 +end + + +Then /^client should follow href attribute in (\w+)$/ do |element| + get @element[:href], {} +end + +Then /^client should get valid response with requested (\w+)$/ do |element| + last_response.status.should == 200 + output_xml.xpath('/'+element+'/id').first.text.should == @element.xpath('id').text +end + +When /^client access this URI with parameters:$/ do |table| + params = {} + table.raw.each { |i| params[i[0]]=i[1] } + get @uri, params +end + +Then /^client should get some ([\w-]+)$/ do |elements| + last_response.status.should == 200 + output_xml.xpath('/'+elements+'/'+elements.singularize).size.should > 0 +end diff --git a/tests/mock/step_definitions/instance_states_steps.rb b/tests/mock/step_definitions/instance_states_steps.rb new file mode 100644 index 0000000..a587e5d --- /dev/null +++ b/tests/mock/step_definitions/instance_states_steps.rb @@ -0,0 +1,34 @@ +Then /^states element contains some states$/ do + output_xml.xpath('/states/state').size.should > 0 +end + +Then /^each state should have '(.+)' attribute$/ do |attr| + output_xml.xpath('/states/state').each do |state| + state[attr].should_not be_nil + end +end + +Then /^(\w+) state should have '(.+)' attribute set to '(\w+)'$/ do |pos, attr, value| + output_xml.xpath('/states/state').first[attr].should==value if pos=='first' + output_xml.xpath('/states/state').last[attr].should==value if pos=='last' +end + +Then /^some states should have transitions$/ do + @transitions = output_xml.xpath('/states/state/transition') + @transitions.size.should > 0 +end + +Then /^each transitions should have 'to' attribute$/ do + @transitions.each do |t| + t[:to].should_not be_nil + end +end + +When /^client wants (\w+) format$/ do |format| + get "#{@uri}.#{format.downcase}", {} +end + +Then /^client should get PNG image$/ do + last_response.status.should == 200 + last_response.headers['Content-Type'].should == 'image/png' +end diff --git a/tests/mock/step_definitions/instances_steps.rb b/tests/mock/step_definitions/instances_steps.rb new file mode 100644 index 0000000..8ec6526 --- /dev/null +++ b/tests/mock/step_definitions/instances_steps.rb @@ -0,0 +1,121 @@ +When /^client follow ([\w-]+) href attribute in first instance$/ do |element| + get output_xml.xpath('/instances/instance[1]/'+element).first[:href], {} +end + +Then /^client should get valid ([\w-]+)$/ do |element| + last_response.status.should == 200 + output_xml.xpath("/#{element}").first.should_not be_nil +end + +Then /^each instance should have actions$/ do + output_xml.xpath('/instances/instance').each do |instance| + instance.xpath('actions').first.should_not be_nil + end +end + +Then /^each actions should have some links$/ do + output_xml.xpath('/instances/instance').each do |instance| + instance.xpath('actions/link').first.should_not be_nil + end +end + +Then /^each link should have valid (\w+) attribute$/ do |attr| + output_xml.xpath('/instances/instance').each do |instance| + instance.xpath('actions/link').first[attr].should_not be_nil + end +end + +When /^client want to '(\w+)' first instance$/ do |action| + @action = action + @instance = output_xml.xpath('/instances/instance[1]').first +end + +When /^client follow link in actions$/ do + unless @instance_url + l = output_xml.xpath("/instances/instance[1]/actions/link[@rel = '#{@action}']").first + else + l = @instance.xpath('actions/link[@rel="'+@action+'"]').first + end + unless @action=='destroy' + post l[:href], { :id => @instance.xpath('id').first.text } + else + delete l[:href], { :id => @instance.xpath('id').first.text } + end + last_response.status.should_not == 500 +end + +Then /^client should get first instance$/ do + output_xml.xpath('/instance/id').first.should_not be_nil + #output_xml.xpath('/instance/id').first.text.should == @instance.xpath('id').first.text +end + +Then /^this instance should be in '(.+)' state$/ do |state| + output_xml.xpath('/instance/state').first.text.should == state +end + +When /^client want to create a new instance$/ do +end + +Then /^client should choose first image$/ do + get '/api/images', {} + @image = output_xml.xpath('/images/image').first + @image.should_not be_nil +end + +When /^client request for a new instance$/ do + params = { + :image_id => @image.xpath('id').first.text + } + params[:hwp_id] = @hwp_id if @hwp_id + post "#{@uri}", params + last_response.status.should == 201 + @instance_url = last_response.headers['Location'] +end + +Then /^new instance should be created$/ do + get @instance_url, {} + last_response.status.should == 200 +end + +Then /^this instance should have chosed image$/ do + output_xml.xpath('/instance/image').first[:href].should == @image[:href] +end + +Then /^this instance should have valid id$/ do + output_xml.xpath('instance/id').first.should_not be_nil +end + +Then /^this instance should have name$/ do + output_xml.xpath('instance/name').first.should_not be_nil +end + +When /^client want to '(\w+)' created instance$/ do |action| + get @instance_url, {} + last_response.status.should == 200 + @action = action + @instance = output_xml.xpath('/instance') +end + +Then /^client should get created instance$/ do + get @instance_url +end + +When /^this instance should be destroyed$/ do + # TODO: Fix this bug in mock driver ? + #get @instance_url, {} + #last_response.status.should == 404 +end + +Then /^client should get HTML form$/ do + last_response.status.should == 200 + (last_response.body.strip =~ /^<!DOCTYPE html/).should be_true +end + +When /^client choose last hardware profile$/ do + get '/api/hardware_profiles', {} + @hwp_id = output_xml.xpath('/hardware-profiles/hardware-profile/id').last.text +end + +Then /^this instance should have last hardware profile$/ do + output_xml.xpath('instance/hardware-profile/id').first.text.should == @hwp_id +end diff --git a/tests/mock/step_definitions/rake_steps.rb b/tests/mock/step_definitions/rake_steps.rb new file mode 100644 index 0000000..c8ea3d9 --- /dev/null +++ b/tests/mock/step_definitions/rake_steps.rb @@ -0,0 +1,19 @@ +require 'fileutils' +require 'rake' +require 'find' + +Given /^I have a clean (.+) directory$/ do |dir| + FileUtils.rm_rf dir +end + +When /^I run a '(\w+)' task$/ do |task| + @rake = Rake::Application.new + Rake.application = @rake + load "Rakefile" + @task = Rake::Task[task] + @task.invoke +end + +Then /^I should see a (\w+) file inside (\w+) directory$/ do |ext, dir| + Dir["#{dir}/deltacloud-*.#{ext}"].size.should == 1 +end diff --git a/tests/mock/storage_snapshots.feature b/tests/mock/storage_snapshots.feature new file mode 100644 index 0000000..f6f38e5 --- /dev/null +++ b/tests/mock/storage_snapshots.feature @@ -0,0 +1,29 @@ +Feature: Accessing storage snapshots + + Scenario: Listing available storage snapshots + Given URI /api/storage_snapshots exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'storage-snapshots' + And this element contains some storage-snapshots + And each storage-volume should have: + | id | + | created | + | storage-volume | + And each image should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Get details about first volume + Given URI /api/storage_snapshots exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'storage-snapshots' + And this element contains some storage-snapshots + When client want to show first storage-snapshot + Then client follow href attribute in first storage-snapshot + Then client should get this storage-snapshot + And this storage-snapshot should have: + | id | + | created | + | state | + | storage-volume | diff --git a/tests/mock/storage_volumes.feature b/tests/mock/storage_volumes.feature new file mode 100644 index 0000000..dfca4b0 --- /dev/null +++ b/tests/mock/storage_volumes.feature @@ -0,0 +1,33 @@ +Feature: Accessing storage volumes + + Scenario: Listing available storage volumes + Given URI /api/storage_volumes exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'storage-volumes' + And this element contains some storage-volumes + And each storage-volume should have: + | id | + | created | + | capacity | + | device | + | instance | + And each image should have 'href' attribute with valid URL + And this URI should be available in XML, JSON, HTML format + + Scenario: Get details about first volume + Given URI /api/storage_volumes exists + And authentification is required for this URI + When client access this URI + Then client should get root element 'storage-volumes' + And this element contains some storage-volumes + When client want to show first storage-volume + Then client follow href attribute in first storage-volume + Then client should get this storage-volume + And this storage-volume should have: + | id | + | created | + | capacity | + | device | + | instance | + | state | diff --git a/tests/mock/support/env.rb b/tests/mock/support/env.rb new file mode 100644 index 0000000..bd544d0 --- /dev/null +++ b/tests/mock/support/env.rb @@ -0,0 +1,40 @@ +SERVER_DIR = File::expand_path(File::join(File::dirname(__FILE__), "../../../server")) +Dir.chdir(SERVER_DIR) + +require 'sinatra' +require 'rack/test' +require 'nokogiri' +require '../server/server' +require 'ap' + +Sinatra::Application.set :environment, :test +Sinatra::Application.set :root, SERVER_DIR + +CONFIG = { + :username => 'mockuser', + :password => 'mockpassword' +} + +World do + + def app + @app = Rack::Builder.new do + set :logging, true + set :raise_errors, true + run Sinatra::Application + end + end + + def output_xml + Nokogiri::XML(last_response.body) + end + + Before do + unless @no_header + header 'Accept', 'application/xml' + end + end + + include Rack::Test::Methods +end +