From 09280b9dd75ba4df0a818ef334742aa47e6492cd Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Mon, 14 Mar 2016 17:21:01 +0100 Subject: [PATCH 01/34] Add colorized progress test reporter to immediately recognize test outcome See Github for further report formatters: https://github.com/kern/minitest-reporters --- Gemfile | 2 ++ Gemfile.lock | 8 ++++++++ test/test_helper.rb | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/Gemfile b/Gemfile index 91dab34..dc27b8b 100644 --- a/Gemfile +++ b/Gemfile @@ -68,6 +68,8 @@ group :development, :test do gem 'byebug' gem 'quiet_assets' gem 'webmock' + + gem 'minitest-reporters' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index b245104..a7b0210 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -38,6 +38,7 @@ GEM thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) addressable (2.4.0) + ansi (1.5.0) arel (6.0.3) autoprefixer-rails (6.3.1) execjs @@ -150,6 +151,11 @@ GEM mime-types (2.99) mini_portile2 (2.0.0) minitest (5.8.4) + minitest-reporters (1.1.8) + ansi + builder + minitest (>= 5.0) + ruby-progressbar multi_json (1.11.2) nenv (0.3.0) nokogiri (1.6.7.2) @@ -215,6 +221,7 @@ GEM json (~> 1.4) redcarpet (3.3.4) ref (2.0.0) + ruby-progressbar (1.7.5) ruby_parser (3.7.3) sexp_processor (~> 4.1) safe_yaml (1.0.4) @@ -285,6 +292,7 @@ DEPENDENCIES jbuilder (~> 2.0) jquery-rails kaminari + minitest-reporters passenger pg quiet_assets diff --git a/test/test_helper.rb b/test/test_helper.rb index 63ba2e8..0aab98a 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -3,6 +3,12 @@ require 'rails/test_help' require 'webmock/minitest' +require 'minitest/reporters' +Minitest::Reporters.use!( + Minitest::Reporters::ProgressReporter.new, + ENV, + Minitest.backtrace_filter) + class ActiveSupport::TestCase self.use_transactional_fixtures = true From 0e40810b247a6fe2b4b40a62c620a3706bf09fc4 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Mon, 14 Mar 2016 18:39:10 +0100 Subject: [PATCH 02/34] Add test coverage reporter `simplecov` Each test run automatically generates a coverage report in the `coverage` folder. The overhead should be neglectable (e.g., 2s for 600 tests https://twitter.com/qxjit/status/53102603533430784). Currently, ~70% coverage is reported. --- .gitignore | 3 +++ Gemfile | 1 + Gemfile.lock | 7 +++++++ test/test_helper.rb | 3 +++ 4 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 66beecf..f501c5f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ # Ignore .idea directory (Rubymine) /.idea + +# Ignore simplecov coverage report +/coverage/ diff --git a/Gemfile b/Gemfile index dc27b8b..7c9063d 100644 --- a/Gemfile +++ b/Gemfile @@ -70,6 +70,7 @@ group :development, :test do gem 'webmock' gem 'minitest-reporters' + gem 'simplecov', :require => false end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index a7b0210..2329bc8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -71,6 +71,7 @@ GEM delayed_job_active_record (4.1.0) activerecord (>= 3.0, < 5) delayed_job (>= 3.0, < 5) + docile (1.1.5) em-websocket (0.5.1) eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) @@ -237,6 +238,11 @@ GEM rdoc (~> 4.0) sexp_processor (4.6.1) shellany (0.0.1) + simplecov (0.11.2) + docile (~> 1.1.0) + json (~> 1.8) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) slop (3.6.0) spring (1.6.3) sprockets (3.5.2) @@ -305,6 +311,7 @@ DEPENDENCIES redcarpet sass-rails (~> 5.0) sdoc (~> 0.4.0) + simplecov spring sqlite3 terminal-notifier-guard diff --git a/test/test_helper.rb b/test/test_helper.rb index 0aab98a..1b6b1e3 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,3 +1,6 @@ +require 'simplecov' +SimpleCov.start 'rails' + ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) require 'rails/test_help' From b991532159693020ac5253277cbaabdd4a631a7e Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Mon, 14 Mar 2016 19:14:08 +0100 Subject: [PATCH 03/34] Add `capybara` acceptance test framework Capybara (https://github.com/jnicklas/capybara) is the most popular integration testing tool in the Rails community according to Ruby Toolbox (https://www.ruby-toolbox.com/categories/browser_testing). Capybara provides a convenient DSL to write browser tests. --- Gemfile | 3 +++ Gemfile.lock | 12 +++++++++++- test/integration/general_stories_test.rb | 5 +++++ test/test_helper.rb | 6 ++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 7c9063d..48fec4b 100644 --- a/Gemfile +++ b/Gemfile @@ -71,6 +71,9 @@ group :development, :test do gem 'minitest-reporters' gem 'simplecov', :require => false + + # Acceptance test framework for web applications: https://github.com/jnicklas/capybara + gem 'capybara' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 2329bc8..6439233 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -54,6 +54,13 @@ GEM thor (~> 0.19) builder (3.2.2) byebug (8.2.2) + capybara (2.6.2) + addressable + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) coderay (1.1.0) coffee-rails (4.1.1) coffee-script (>= 2.2.0) @@ -149,7 +156,7 @@ GEM mail (2.6.3) mime-types (>= 1.16, < 3) method_source (0.8.2) - mime-types (2.99) + mime-types (2.99.1) mini_portile2 (2.0.0) minitest (5.8.4) minitest-reporters (1.1.8) @@ -276,6 +283,8 @@ GEM addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff + xpath (2.0.0) + nokogiri (~> 1.3) PLATFORMS ruby @@ -285,6 +294,7 @@ DEPENDENCIES bootstrap (~> 4.0.0.alpha3) bourbon byebug + capybara coffee-rails (~> 4.1.0) delayed_job_active_record font-awesome-sass (~> 4.5.0) diff --git a/test/integration/general_stories_test.rb b/test/integration/general_stories_test.rb index 3b07909..dc64710 100644 --- a/test/integration/general_stories_test.rb +++ b/test/integration/general_stories_test.rb @@ -1,6 +1,11 @@ require 'test_helper' class GeneralStoriesTest < ActionDispatch::IntegrationTest + test 'landing page has title text' do + visit root_path + assert page.has_content?('Finely crafted Cloud Application Deployments') + end + test "get application list" do get cloud_applications_path assert_response :success diff --git a/test/test_helper.rb b/test/test_helper.rb index 1b6b1e3..58a1065 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -12,6 +12,12 @@ ENV, Minitest.backtrace_filter) +require 'capybara/rails' +class ActionDispatch::IntegrationTest + # Make the Capybara DSL available in all integration tests + include Capybara::DSL +end + class ActiveSupport::TestCase self.use_transactional_fixtures = true From b6911dc8f99e2947a8460600681434956d3acd2f Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Tue, 15 Mar 2016 10:35:10 +0100 Subject: [PATCH 04/34] Add Javascript-enabled test support Adding support for JS-enabled tests has some important implications: * The transactional database cleaning strategy cannot be used anymore because JS tests run in a separate thread. * Loading all fixtures using an alternative DB cleaning strategy (e.g., deletion or truncation) imposes a performance penalty. Preferring `poltergeist` over `selenium-webdriver`: From the two most popular browser testing drivers (https://www.ruby-toolbox.com/categories/browser_testing), `poltergeist` is truly headless (no need for firefox or virtual framebuffer) and more suitable for CI builds. Additional dependency `phantomjs` is required: Phantomjs (http://phantomjs.org/) need to be installed on every machine running the integration tests. Separate test configuration for integration tests: * Selectively enable the javascript test driver for integration tests. * Configure database cleaner deletion strategy * Disable WebMock but allow connections to localhost. This addresses the issue `WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled. Unregistered request:` => See: https://robots.thoughtbot.com/using-capybara-to-test-javascript-that-makes-http Avoid hacks proposing to use transactions and JS-tests together: A couple of websites suggest to use shared DB connections with transactional fixtures (e.g., http://blog.plataformatec.com.br/2011/12/three-tips-to-improve-the-performance-of-your-test-suite/). However, multiple sources report inconsistent behaviors such as race conditions using this monkey-patching approach (see http://infinitemonkeys.influitive.com/dont-use-a-shared-connection-on-full-stack-capybara-tests/) Additional resources: * Extensive guide on Rails 4.1 Testing: http://www.ironhorserails.com/posts/1?locale=en * Pro MiniTest and fixtures: http://brandonhilkert.com/blog/7-reasons-why-im-sticking-with-minitest-and-fixtures-in-rails/ * Using fixtures: http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html --- Gemfile | 4 ++++ Gemfile.lock | 12 ++++++++++ README.rdoc | 4 +++- test/integration/general_stories_test.rb | 14 ++++++++++++ test/test_helper.rb | 28 ++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 48fec4b..436caa1 100644 --- a/Gemfile +++ b/Gemfile @@ -74,6 +74,10 @@ group :development, :test do # Acceptance test framework for web applications: https://github.com/jnicklas/capybara gem 'capybara' + # Support headless UI tests; Requires PhantomJS: http://phantomjs.org/ + gem 'poltergeist' + # Different DB cleaning strategy is required for Javascript UI tests running in a separate thread + gem 'database_cleaner' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 6439233..f148099 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,6 +61,7 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + cliver (0.3.2) coderay (1.1.0) coffee-rails (4.1.1) coffee-script (>= 2.2.0) @@ -72,6 +73,7 @@ GEM concurrent-ruby (1.0.0) crack (0.4.3) safe_yaml (~> 1.0.0) + database_cleaner (1.5.1) debug_inspector (0.0.2) delayed_job (4.1.1) activesupport (>= 3.0, < 5.0) @@ -175,6 +177,11 @@ GEM rack rake (>= 0.8.1) pg (0.18.4) + poltergeist (1.9.0) + capybara (~> 2.1) + cliver (~> 0.3.1) + multi_json (~> 1.0) + websocket-driver (>= 0.2.0) pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -283,6 +290,9 @@ GEM addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff + websocket-driver (0.6.3) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) xpath (2.0.0) nokogiri (~> 1.3) @@ -296,6 +306,7 @@ DEPENDENCIES byebug capybara coffee-rails (~> 4.1.0) + database_cleaner delayed_job_active_record font-awesome-sass (~> 4.5.0) guard @@ -311,6 +322,7 @@ DEPENDENCIES minitest-reporters passenger pg + poltergeist quiet_assets rack-livereload rails (~> 4.2.4) diff --git a/README.rdoc b/README.rdoc index ad98ae6..345c93c 100644 --- a/README.rdoc +++ b/README.rdoc @@ -26,7 +26,9 @@ specifications and requirements. rbenv install $(cat cloud-stove/.ruby-version). For RVM, run rvm install $(cat cloud-stove/.ruby-version). - + +* Install `phantomjs` for headless UI tests: http://phantomjs.org/download.html + * Set up dependencies and database cd cloud-stove bin/setup diff --git a/test/integration/general_stories_test.rb b/test/integration/general_stories_test.rb index dc64710..ddd6d55 100644 --- a/test/integration/general_stories_test.rb +++ b/test/integration/general_stories_test.rb @@ -6,6 +6,20 @@ class GeneralStoriesTest < ActionDispatch::IntegrationTest assert page.has_content?('Finely crafted Cloud Application Deployments') end + test 'create new application blueprint' do + bp = Blueprint.new(name: 'bp1', body: 'body1') + + visit blueprints_path + first(:link, 'New Blueprint').click + fill_in('Name', with: bp.name) + fill_in('Body', with: bp.body) + click_link 'Add Component' + click_button 'Save' + + assert page.has_content?('Blueprint was successfully created.') + assert page.has_content?(bp.name) + end + test "get application list" do get cloud_applications_path assert_response :success diff --git a/test/test_helper.rb b/test/test_helper.rb index 58a1065..1504793 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -13,9 +13,37 @@ Minitest.backtrace_filter) require 'capybara/rails' +require 'capybara/poltergeist' class ActionDispatch::IntegrationTest # Make the Capybara DSL available in all integration tests include Capybara::DSL + + # Transactional fixtures do not work for Javascript-enabled + # UI tests running in a separate thread + self.use_transactional_fixtures = false + # Do not load fixtures because this would be slow in combination + # with the deletion/truncation DB cleaning strategy + fixtures + + Capybara.javascript_driver = :poltergeist + # Transaction cannot be used due to Poltergeist running in a separate thread + # Deletion is often faster than truncation on Postgres + DatabaseCleaner.strategy = :deletion + + setup do + WebMock.disable! + # Prohibit external connections but allow requests to localhost + # Allows for local stubbing and ignoring external requests + # by redirecting them to localhost. See: + # https://robots.thoughtbot.com/using-capybara-to-test-javascript-that-makes-http + WebMock.disable_net_connect!(allow_localhost: true) + Capybara.current_driver = Capybara.javascript_driver + DatabaseCleaner.start + end + + teardown do + DatabaseCleaner.clean + end end class ActiveSupport::TestCase From 463a62a79441bff196adeb7285e7cc2e6bf1f4f8 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Tue, 15 Mar 2016 12:52:57 +0100 Subject: [PATCH 05/34] Fix test that calls external service Integration tests should not make requests to external services. Using environment-specific configurations in combination with the WebMock configuration allowing requests to localhost (see `test_helper.rb`) allows to overcome this issue. Therefore, requests made from Javascript should be configurable and either be ignored (redirected to `localhost`) or suitable mock implementations must be provided and setup before running the test. --- app/views/shared/_main_navigation.html.haml | 2 +- config/application.rb | 4 ++++ config/environments/test.rb | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/views/shared/_main_navigation.html.haml b/app/views/shared/_main_navigation.html.haml index 4bc29be..653f1b3 100644 --- a/app/views/shared/_main_navigation.html.haml +++ b/app/views/shared/_main_navigation.html.haml @@ -18,7 +18,7 @@ %button.btn.btn-link.nav-link.dropdown-toggle{data: { toggle: 'dropdown' }} -# = icon 'user' %span.hidden-sm-down Arthur Dent - = image_tag "//www.gravatar.com/avatar/#{Digest::MD5.hexdigest('inzinger@gmail.com')}", class: 'avatar' + = image_tag "//#{Rails.configuration.x.gravatar_host}/avatar/#{Digest::MD5.hexdigest('inzinger@gmail.com')}", class: 'avatar' .dropdown-menu.dropdown-menu-right = link_to icon('user', t('.my_account')), root_path, class: 'dropdown-item' .dropdown-divider diff --git a/config/application.rb b/config/application.rb index e8970b0..05ad8c4 100644 --- a/config/application.rb +++ b/config/application.rb @@ -24,5 +24,9 @@ class Application < Rails::Application config.active_record.raise_in_transactional_callbacks = true config.active_job.queue_adapter = :delayed_job + + # Custom configuration using the recommended `config.x` property: + # http://guides.rubyonrails.org/configuring.html#custom-configuration + config.x.gravatar_host = 'www.gravatar.com' end end diff --git a/config/environments/test.rb b/config/environments/test.rb index 1c19f08..39b316e 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -39,4 +39,7 @@ # Raises error for missing translations # config.action_view.raise_on_missing_translations = true + + # Custom Cloud Stove config + config.x.gravatar_host = 'localhost' end From c5dd7e060f9780732c178077be54c6b9a7547460 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Tue, 15 Mar 2016 16:32:20 +0100 Subject: [PATCH 06/34] Add IT for creating a new application blueprint --- test/integration/general_stories_test.rb | 42 ++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/test/integration/general_stories_test.rb b/test/integration/general_stories_test.rb index ddd6d55..d26a0c2 100644 --- a/test/integration/general_stories_test.rb +++ b/test/integration/general_stories_test.rb @@ -7,17 +7,55 @@ class GeneralStoriesTest < ActionDispatch::IntegrationTest end test 'create new application blueprint' do - bp = Blueprint.new(name: 'bp1', body: 'body1') + c1 = Component.new(name: 'Application Server', + component_type: 'application-server', + body: '# Introduction + Explained at [Wikipedia][1] + + [1]: https://en.wikipedia.org/wiki/Application_server', + deployment_rule: DeploymentRule.new(more_attributes: '{"when x users":"then y servers"}')) + c2 = Component.new(name: 'Database Server', + component_type: 'database', + body: '# Performance Considerations + Typically Disk I/O, RAM bound (CPU not as important)', + deployment_rule: DeploymentRule.new(more_attributes: '{"when x connections":"then y threads"}')) + bp = Blueprint.new(name: 'Multitier Architecture', body: + '# Basic Properties + - Web Frontend + - Application Server + - Database Backend', + components: [c1,c2]) visit blueprints_path + assert page.has_content?('No blueprints found') + first(:link, 'New Blueprint').click fill_in('Name', with: bp.name) fill_in('Body', with: bp.body) - click_link 'Add Component' + + add_component(c1, 1) + add_component(c2, 2) click_button 'Save' assert page.has_content?('Blueprint was successfully created.') assert page.has_content?(bp.name) + assert page.has_content?(c1.name) + # Check Markdown rendering of title header + assert page.has_xpath?('//section[@class="component"]/h2[text()="Introduction"]') + assert page.has_content?(c2.name) + end + + def add_component(component, n) + click_link 'Add Component' + within(:xpath, "//form/div[3]/fieldset[#{component_fieldset(n)}]") do + fill_in('Name', with: component.name) + fill_in('Component type', with: component.component_type) + fill_in('Body', with: component.body) + end + end + + def component_fieldset(n) + (n*2) - 1 end test "get application list" do From 110dd6f351ac3b45821f5a87fdf2934b88440f5e Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Tue, 15 Mar 2016 18:44:26 +0100 Subject: [PATCH 07/34] Use FactoryGirl for integration tests Fixtures cannot be safely and efficiently used for JS-enabled integration tests. FactoryGirl is the most popular replacement for fixtures offering a convenient syntax. Github: https://github.com/thoughtbot/factory_girl Rubydoc: http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md --- Gemfile | 2 + Gemfile.lock | 6 +++ test/factories.rb | 50 ++++++++++++++++++++++++ test/integration/general_stories_test.rb | 30 ++++++-------- test/test_helper.rb | 2 + 5 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 test/factories.rb diff --git a/Gemfile b/Gemfile index 436caa1..1f113ee 100644 --- a/Gemfile +++ b/Gemfile @@ -78,6 +78,8 @@ group :development, :test do gem 'poltergeist' # Different DB cleaning strategy is required for Javascript UI tests running in a separate thread gem 'database_cleaner' + # Fixtures replacement for integration tests + gem 'factory_girl_rails' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index f148099..8db5028 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -87,6 +87,11 @@ GEM erubis (2.7.0) eventmachine (1.0.9.1) execjs (2.6.0) + factory_girl (4.5.0) + activesupport (>= 3.0.0) + factory_girl_rails (4.6.0) + factory_girl (~> 4.5.0) + railties (>= 3.0.0) ffi (1.9.10) font-awesome-sass (4.5.0) sass (>= 3.2) @@ -308,6 +313,7 @@ DEPENDENCIES coffee-rails (~> 4.1.0) database_cleaner delayed_job_active_record + factory_girl_rails font-awesome-sass (~> 4.5.0) guard guard-bundler diff --git a/test/factories.rb b/test/factories.rb new file mode 100644 index 0000000..895d66c --- /dev/null +++ b/test/factories.rb @@ -0,0 +1,50 @@ +FactoryGirl.define do + factory :blueprint do + sequence(:name) { |n| "blueprint#{n}"} + sequence(:body) { |n| "# blueprint body#{n}" } + transient do + component_count 3 + end + after(:create) do |component, evaluator| + create_list(:component, evaluator.component_count) + end + end + + factory :mt_blueprint, class: Blueprint do + name 'Multitier Architecture' + body '# Basic Properties + - Web Frontend + - Application Server + - Database Backend' + components { |c| [c.association(:component), c.association(:db_component)] } + end + + factory :component do + sequence(:name) { |n| "component#{n}" } + component_type 'application-server' + sequence(:body) { |n| "# component body#{n}" } + deployment_rule + end + + factory :app_component, class: Component do + name 'Application Server' + component_type 'application-server' + body '# Introduction + Explained at [Wikipedia][1] + + [1]: https://en.wikipedia.org/wiki/Application_server' + deployment_rule + end + + factory :db_component, class: Component do + name 'Database Server' + component_type 'database' + body '# Performance Considerations + Typically Disk I/O, RAM bound (CPU not as important)' + deployment_rule + end + + factory :deployment_rule do + more_attributes '{"when x users":"then y servers"}' + end +end diff --git a/test/integration/general_stories_test.rb b/test/integration/general_stories_test.rb index d26a0c2..0bbcf0b 100644 --- a/test/integration/general_stories_test.rb +++ b/test/integration/general_stories_test.rb @@ -6,25 +6,10 @@ class GeneralStoriesTest < ActionDispatch::IntegrationTest assert page.has_content?('Finely crafted Cloud Application Deployments') end - test 'create new application blueprint' do - c1 = Component.new(name: 'Application Server', - component_type: 'application-server', - body: '# Introduction - Explained at [Wikipedia][1] - - [1]: https://en.wikipedia.org/wiki/Application_server', - deployment_rule: DeploymentRule.new(more_attributes: '{"when x users":"then y servers"}')) - c2 = Component.new(name: 'Database Server', - component_type: 'database', - body: '# Performance Considerations - Typically Disk I/O, RAM bound (CPU not as important)', - deployment_rule: DeploymentRule.new(more_attributes: '{"when x connections":"then y threads"}')) - bp = Blueprint.new(name: 'Multitier Architecture', body: - '# Basic Properties - - Web Frontend - - Application Server - - Database Backend', - components: [c1,c2]) + test 'create new blueprint' do + c1 = build_stubbed(:app_component) + c2 = build_stubbed(:db_component) + bp = build_stubbed(:mt_blueprint) visit blueprints_path assert page.has_content?('No blueprints found') @@ -58,6 +43,13 @@ def component_fieldset(n) (n*2) - 1 end + test 'list blueprints' do + n = 5 + create_list(:blueprint, n) + visit blueprints_path + assert page.has_content?("Displaying all #{n} blueprints") + end + test "get application list" do get cloud_applications_path assert_response :success diff --git a/test/test_helper.rb b/test/test_helper.rb index 1504793..96c3718 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -11,10 +11,12 @@ Minitest::Reporters::ProgressReporter.new, ENV, Minitest.backtrace_filter) +FactoryGirl.lint require 'capybara/rails' require 'capybara/poltergeist' class ActionDispatch::IntegrationTest + include FactoryGirl::Syntax::Methods # Make the Capybara DSL available in all integration tests include Capybara::DSL From 05b2d57ee69b5749e7fed6db31e319c5207bf6f8 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Thu, 17 Mar 2016 13:56:57 +0100 Subject: [PATCH 08/34] Replace all fixtures with factories A priori loaded fixtures are incompatible with JS-enabled tests which require alternative DB cleaning strategies (deletion or truncation) Unless integration tests were run in isolation from other test, subtle errors caused the tests to behave non-deterministically. Therefore, we need to lazily load test data via factories. In order to optimize performance, only JS-enabled integration tests use the much slower `deletion` DB cleaning strategy whereas all other test use `transaction`. NOTICE: In the `test_helper.rb`, we have distinguish between test types via `is_integration_test?`. Using separate `setup` hooks for `ActionDispatch::IntegrationTest` and `ActiveSupport::TestCase` does not work because both hooks would get executed and disturb each other. --- lib/tasks/factory_girl.rake | 18 +++++ .../controllers/blueprints_controller_test.rb | 2 +- .../cloud_applications_controller_test.rb | 4 +- .../controllers/components_controller_test.rb | 2 +- test/factories.rb | 79 ++++++++++++++++++- ...application_deployment_recommendations.yml | 7 -- test/fixtures/blueprints.yml | 15 ---- test/fixtures/cloud_applications.yml | 19 ----- test/fixtures/components.yml | 31 -------- test/fixtures/concrete_components.yml | 31 -------- test/fixtures/deployment_recommendations.yml | 11 --- test/fixtures/deployment_rules.yml | 9 --- test/fixtures/providers.yml | 9 --- test/fixtures/resources.yml | 9 --- test/fixtures/slo_sets.yml | 23 ------ test/jobs/compute_recommendations_job_test.rb | 8 +- test/models/cloud_application_test.rb | 2 +- test/test_helper.rb | 64 ++++++++------- 18 files changed, 134 insertions(+), 209 deletions(-) create mode 100644 lib/tasks/factory_girl.rake delete mode 100644 test/fixtures/application_deployment_recommendations.yml delete mode 100644 test/fixtures/blueprints.yml delete mode 100644 test/fixtures/cloud_applications.yml delete mode 100644 test/fixtures/components.yml delete mode 100644 test/fixtures/concrete_components.yml delete mode 100644 test/fixtures/deployment_recommendations.yml delete mode 100644 test/fixtures/deployment_rules.yml delete mode 100644 test/fixtures/providers.yml delete mode 100644 test/fixtures/resources.yml delete mode 100644 test/fixtures/slo_sets.yml diff --git a/lib/tasks/factory_girl.rake b/lib/tasks/factory_girl.rake new file mode 100644 index 0000000..94ef88f --- /dev/null +++ b/lib/tasks/factory_girl.rake @@ -0,0 +1,18 @@ +# Recommended way to lint factories to avoid performance overhead if running few tests +# https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#linting-factories +# Usage: `rake factory_girl:lint` +namespace :factory_girl do + desc 'Verify that all FactoryGirl factories are valid' + task lint: :environment do + if Rails.env.test? + begin + DatabaseCleaner.start + FactoryGirl.lint + ensure + DatabaseCleaner.clean_with :transaction + end + else + system("bundle exec rake factory_girl:lint RAILS_ENV='test'") + end + end +end diff --git a/test/controllers/blueprints_controller_test.rb b/test/controllers/blueprints_controller_test.rb index 49cdee5..2ea61bb 100644 --- a/test/controllers/blueprints_controller_test.rb +++ b/test/controllers/blueprints_controller_test.rb @@ -2,7 +2,7 @@ class BlueprintsControllerTest < ActionController::TestCase setup do - @blueprint = blueprints(:one) + @blueprint = create(:blueprint) end test "should get index" do diff --git a/test/controllers/cloud_applications_controller_test.rb b/test/controllers/cloud_applications_controller_test.rb index affdf17..4750237 100644 --- a/test/controllers/cloud_applications_controller_test.rb +++ b/test/controllers/cloud_applications_controller_test.rb @@ -2,7 +2,7 @@ class CloudApplicationsControllerTest < ActionController::TestCase setup do - @cloud_application = cloud_applications(:one) + @cloud_application = create(:cloud_application) end test "should get index" do @@ -24,7 +24,7 @@ class CloudApplicationsControllerTest < ActionController::TestCase end test "should get new for instance from blueprint" do - blueprint = blueprints(:multitier_app) + blueprint = create(:mt_blueprint) get :new, blueprint: blueprint.id assert_response :success assert_not_nil @controller.params[:blueprint] diff --git a/test/controllers/components_controller_test.rb b/test/controllers/components_controller_test.rb index 891fc52..fc7d5b6 100644 --- a/test/controllers/components_controller_test.rb +++ b/test/controllers/components_controller_test.rb @@ -3,7 +3,7 @@ class ComponentsControllerTest < ActionController::TestCase setup do - @component = components(:one) + @component = create(:component) end test "should get index" do diff --git a/test/factories.rb b/test/factories.rb index 895d66c..3501d58 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -1,7 +1,12 @@ FactoryGirl.define do + factory :cloud_application do + sequence(:name) { |n| "CloudApplication#{n}"} + blueprint + end + factory :blueprint do - sequence(:name) { |n| "blueprint#{n}"} - sequence(:body) { |n| "# blueprint body#{n}" } + sequence(:name) { |n| "Blueprint#{n}"} + sequence(:body) { |n| "# Blueprint body#{n} with lots of info about this app type." } transient do component_count 3 end @@ -20,9 +25,9 @@ end factory :component do - sequence(:name) { |n| "component#{n}" } + sequence(:name) { |n| "Component#{n}" } component_type 'application-server' - sequence(:body) { |n| "# component body#{n}" } + sequence(:body) { |n| "# Component body#{n} with info about single component" } deployment_rule end @@ -44,7 +49,73 @@ deployment_rule end + factory :lb_component, class: Component do + name 'Load Balancer' + component_type 'load-balancer' + association :blueprint, factory: :mt_blueprint + end + + factory :cdn_component, class: Component do + name 'CDN' + component_type 'cdn' + association :blueprint, factory: :mt_blueprint + end + + factory :concrete_component do + sequence(:name) { |n| "ConcreteComponent#{n}"} + + trait :webrick do + name 'WebRick Application Server' + association component: :app_component + cloud_application + end + + trait :sqlite do + name 'SQLite Database' + association component: :db_component + cloud_application + end + + trait :nginx do + name 'NginX Load Balancer' + association component: :lb_component + cloud_application + end + end + factory :deployment_rule do more_attributes '{"when x users":"then y servers"}' end + + factory :deployment_recommendation do + # This model initially had no columns defined. + end + + factory :provider do + sequence(:name) { |n| "Provider#{n}"} + end + + factory :resource do + sequence(:name) { |n| "Resource#{n}"} + end + + factory :slo_set do + more_attributes '{ "metric": "availability", "relation": ">=", "value": "0.995" }' + concrete_component + + trait :webrick do + more_attributes '{"availability":{"$gte":"0.99"},"costs":{"$lte":"200","currency":"$","interval":"month"}}' + concrete_component :webrick + end + + trait :sqlite do + more_attributes '{"availability":{"$gte":"0.999"},"costs":{"$lte":"200","currency":"$","interval":"month"}}' + concrete_component :sqlite + end + + trait :nginx do + more_attributes '{"availability":{"$gte":"0.999"},"costs":{"$lte":"200","currency":"$","interval":"month"}}' + concrete_component :nginx + end + end end diff --git a/test/fixtures/application_deployment_recommendations.yml b/test/fixtures/application_deployment_recommendations.yml deleted file mode 100644 index d455474..0000000 --- a/test/fixtures/application_deployment_recommendations.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - cloud_application_id: 1 - -two: - cloud_application_id: 2 diff --git a/test/fixtures/blueprints.yml b/test/fixtures/blueprints.yml deleted file mode 100644 index a164fbd..0000000 --- a/test/fixtures/blueprints.yml +++ /dev/null @@ -1,15 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - name: MyString - -two: - name: MyString - -multitier_app: - name: Multitier Application - more_attributes: | - <%= { - body: 'Lots of information about this application type.' - }.to_json %> - diff --git a/test/fixtures/cloud_applications.yml b/test/fixtures/cloud_applications.yml deleted file mode 100644 index 5a22307..0000000 --- a/test/fixtures/cloud_applications.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: - name: App One - blueprint: one - - -two: - name: App Two - - -test_application: - id: 1 - name: Chilli Sauce Web Shop - blueprint: one diff --git a/test/fixtures/components.yml b/test/fixtures/components.yml deleted file mode 100644 index d3f38f2..0000000 --- a/test/fixtures/components.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - id: 1 - name: MyString - component_type: MyType - -two: - name: MyString - component_type: MyType - -app_server: - name: Application Server - component_type: app_server - blueprint: multitier_app - -database: - name: Database - component_type: database - blueprint: multitier_app - - -load_balancer: - name: Load Balancer - component_type: load_balancer - blueprint: multitier_app - -cdn: - name: CDN - component_type: cdn - blueprint: multitier_app diff --git a/test/fixtures/concrete_components.yml b/test/fixtures/concrete_components.yml deleted file mode 100644 index a66b925..0000000 --- a/test/fixtures/concrete_components.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - name: MyString - more_attributes: MyText - component: - -two: - name: MyString - more_attributes: MyText - - - -component_one: - id: 1 - name: WebRick Application Server - component: app_server - cloud_application: test_application - -component_two: - id: 2 - name: SQLite database - component: database - cloud_application: test_application - -component_three: - id: 3 - name: NginX load balancer - component: load_balancer - cloud_application: test_application - diff --git a/test/fixtures/deployment_recommendations.yml b/test/fixtures/deployment_recommendations.yml deleted file mode 100644 index 937a0c0..0000000 --- a/test/fixtures/deployment_recommendations.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value diff --git a/test/fixtures/deployment_rules.yml b/test/fixtures/deployment_rules.yml deleted file mode 100644 index e61f1f5..0000000 --- a/test/fixtures/deployment_rules.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - more_attributes: - component_id: 1 - -two: - more_attributes: - component_id: diff --git a/test/fixtures/providers.yml b/test/fixtures/providers.yml deleted file mode 100644 index 5e2d88b..0000000 --- a/test/fixtures/providers.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - name: MyString - # more_attributes: "{}" - -two: - name: MyString - # more_attributes: "{}" diff --git a/test/fixtures/resources.yml b/test/fixtures/resources.yml deleted file mode 100644 index 03dc73c..0000000 --- a/test/fixtures/resources.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - name: MyString - provider_id: - -two: - name: MyString - provider_id: diff --git a/test/fixtures/slo_sets.yml b/test/fixtures/slo_sets.yml deleted file mode 100644 index 0569a73..0000000 --- a/test/fixtures/slo_sets.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - more_attributes: '{ "metric": "availability", "relation": ">=", "value": "0.995" }' - -two: - more_attributes: '{ "metric": "response_time", "relation": "<=", "value": "2.5" }' - - -availability_cost_one: - id: 1 - more_attributes: '{"availability":{"$gte":"0.99"},"costs":{"$lte":"200","currency":"$","interval":"month"}}' - concrete_component: component_one - -availability_cost_two: - id: 2 - more_attributes: '{"availability":{"$gte":"0.999"},"costs":{"$lte":"200","currency":"$","interval":"month"}}' - concrete_component: component_two - -availability_cost_three: - id: 3 - more_attributes: '{"availability":{"$gte":"0.999"},"costs":{"$lte":"200","currency":"$","interval":"month"}}' - concrete_component: component_three \ No newline at end of file diff --git a/test/jobs/compute_recommendations_job_test.rb b/test/jobs/compute_recommendations_job_test.rb index aef6ade..572a4da 100644 --- a/test/jobs/compute_recommendations_job_test.rb +++ b/test/jobs/compute_recommendations_job_test.rb @@ -1,7 +1,7 @@ class ComputeRecommendationsJobTest < ActiveJob::TestCase def setup - @cloud_application = cloud_applications(:test_application) + @cloud_application = create(:cloud_application) end # Ensures that the job gets enqueued correctly @@ -15,9 +15,5 @@ def setup end assert_enqueued_jobs 1 assert_performed_jobs 0 - - end - - -end \ No newline at end of file +end diff --git a/test/models/cloud_application_test.rb b/test/models/cloud_application_test.rb index 672356e..68d79a8 100644 --- a/test/models/cloud_application_test.rb +++ b/test/models/cloud_application_test.rb @@ -2,7 +2,7 @@ class CloudApplicationTest < ActiveSupport::TestCase test "should create new app instance from blueprint" do - blueprint = blueprints(:multitier_app) + blueprint = create(:blueprint) cloud_application = CloudApplication.new_from_blueprint(blueprint) assert_kind_of CloudApplication, cloud_application diff --git a/test/test_helper.rb b/test/test_helper.rb index 96c3718..06044e2 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -5,68 +5,72 @@ require File.expand_path('../../config/environment', __FILE__) require 'rails/test_help' require 'webmock/minitest' +require 'database_cleaner' require 'minitest/reporters' Minitest::Reporters.use!( - Minitest::Reporters::ProgressReporter.new, + Minitest::Reporters::ProgressReporter.new(color: true), ENV, Minitest.backtrace_filter) -FactoryGirl.lint require 'capybara/rails' require 'capybara/poltergeist' class ActionDispatch::IntegrationTest - include FactoryGirl::Syntax::Methods # Make the Capybara DSL available in all integration tests include Capybara::DSL - # Transactional fixtures do not work for Javascript-enabled - # UI tests running in a separate thread - self.use_transactional_fixtures = false - # Do not load fixtures because this would be slow in combination - # with the deletion/truncation DB cleaning strategy - fixtures - + # Use the PhantomJS headless WebKit browser Capybara.javascript_driver = :poltergeist - # Transaction cannot be used due to Poltergeist running in a separate thread - # Deletion is often faster than truncation on Postgres - DatabaseCleaner.strategy = :deletion +end + +class ActiveSupport::TestCase + # Consistently use FactoryGirl instead of fixtures + include FactoryGirl::Syntax::Methods + self.use_transactional_fixtures = false setup do + if is_integration_test? + setup_integration_test + else + setup_general_test + end + DatabaseCleaner.start + end + + teardown do + WebMock.disable! + DatabaseCleaner.clean + end + + def is_integration_test? + self.is_a? ActionDispatch::IntegrationTest + end + + def setup_integration_test WebMock.disable! # Prohibit external connections but allow requests to localhost # Allows for local stubbing and ignoring external requests # by redirecting them to localhost. See: # https://robots.thoughtbot.com/using-capybara-to-test-javascript-that-makes-http WebMock.disable_net_connect!(allow_localhost: true) - Capybara.current_driver = Capybara.javascript_driver - DatabaseCleaner.start - end + Rails.logger.warn 'WebMock is disabled. External services are blocked. Local services are allowed.' - teardown do - DatabaseCleaner.clean + Capybara.current_driver = Capybara.javascript_driver + # Transaction cannot be used due to Poltergeist running in a separate thread + # Deletion and truncation performed roughly the same + DatabaseCleaner.strategy = :deletion end -end -class ActiveSupport::TestCase - self.use_transactional_fixtures = true - - setup do + def setup_general_test if ENV['ENABLE_NET_CONNECT'] Rails.logger.warn 'WebMock is disabled. External services will be used.' else WebMock.enable! Rails.logger.info 'WebMock is active. No external services will be used.' end + DatabaseCleaner.strategy = :transaction end - teardown do - WebMock.disable! - end - - # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. - fixtures :all - # Read web request response stub from file in `test/stubs` directory # # To record a request for WebMock to replay later, save a response From 48f112a3cb6c2f7ae8391ffb30fcc9f92dd8d0c2 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Thu, 17 Mar 2016 14:44:54 +0100 Subject: [PATCH 09/34] Use familiar default test reporter --- test/test_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 06044e2..cef1f8d 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -9,7 +9,7 @@ require 'minitest/reporters' Minitest::Reporters.use!( - Minitest::Reporters::ProgressReporter.new(color: true), + Minitest::Reporters::DefaultReporter.new(color: true), ENV, Minitest.backtrace_filter) From f34c95c9f404c25cbe29ee3091dd95dbc047cdfa Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Thu, 17 Mar 2016 19:18:07 +0100 Subject: [PATCH 10/34] Fix SimpleCov config for guard MiniTest The code coverage analysis tool did not correctly report test coverage results if executed via guard-minitest. Externalizing the config into `.simplecov` is considered best practice: https://github.com/colszowka/simplecov/issues/235#issuecomment-60394178 The `.simplecov` config is optimized for Cloud Stove based on the Rails defaults from: https://github.com/colszowka/simplecov/blob/master/lib/simplecov/defaults.rb Positive side effect: Adding Spring support for MiniTest accelerates repeated test executions through reloading: https://github.com/guard/guard-minitest#spring The new Rake task `rake test:coverage` also triggers coverage analysis. SimpleCov is very fast (i.e., a couple of seconds for ~10 min Rails test suite) and thus they enable coverage analysis by default. For conditional execution, see: https://github.com/colszowka/simplecov#running-coverage-only-on-demand --- .simplecov | 36 ++++++++++++++++++++++++++++++++++++ Gemfile | 2 +- Guardfile | 2 +- lib/tasks/simplecov.rake | 7 +++++++ test/test_helper.rb | 1 - 5 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 .simplecov create mode 100644 lib/tasks/simplecov.rake diff --git a/.simplecov b/.simplecov new file mode 100644 index 0000000..c0f1130 --- /dev/null +++ b/.simplecov @@ -0,0 +1,36 @@ +# https://github.com/colszowka/simplecov#using-simplecov-for-centralized-config +# Maybe put some conditional here not to execute the code below unless ENV['COVERAGE'] == 'true' +SimpleCov.start do + # see https://github.com/colszowka/simplecov/blob/master/lib/simplecov/defaults.rb + load_profile 'test_frameworks' + coverage_dir 'coverage' + command_name 'MiniTest' + merge_timeout 3600 # 1 hour + track_files "{app,lib}/**/*.rb" + + # Groups + add_group 'Controllers', 'app/controllers' + add_group 'Models', 'app/models' + # add_group 'Mailers', 'app/mailers' + add_group 'Helpers', 'app/helpers' + add_group 'Jobs', %w(app/jobs app/workers) + # add_group 'Libraries', 'lib' + + add_group 'Provider updater', 'app/provider_updater' + add_group 'Long files' do |src_file| + src_file.lines.count > 100 + end + class MaxLinesFilter < SimpleCov::Filter + def matches?(source_file) + source_file.lines.count < filter_argument + end + end + add_group 'Short files', MaxLinesFilter.new(5) + + # Exclude these paths from analysis + add_filter '/config/' + add_filter '/db/' + add_filter 'lib/plugins' + add_filter 'vendor' + add_filter 'bundle' +end diff --git a/Gemfile b/Gemfile index 1f113ee..578fa41 100644 --- a/Gemfile +++ b/Gemfile @@ -70,7 +70,7 @@ group :development, :test do gem 'webmock' gem 'minitest-reporters' - gem 'simplecov', :require => false + gem 'simplecov', require: false # Acceptance test framework for web applications: https://github.com/jnicklas/capybara gem 'capybara' diff --git a/Guardfile b/Guardfile index c502ce1..0ed00a7 100644 --- a/Guardfile +++ b/Guardfile @@ -27,7 +27,7 @@ guard :bundler do files.each { |file| watch(helper.real_path(file)) } end -guard :minitest do +guard :minitest, spring: true do # with Minitest::Unit watch(%r{^test/(.*)\/?test_(.*)\.rb$}) watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" } diff --git a/lib/tasks/simplecov.rake b/lib/tasks/simplecov.rake new file mode 100644 index 0000000..408a2aa --- /dev/null +++ b/lib/tasks/simplecov.rake @@ -0,0 +1,7 @@ +# Usage: `rake test:coverage` +namespace :test do + task :coverage do + require 'simplecov' + Rake::Task['test'].execute + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index cef1f8d..14c3d73 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,5 +1,4 @@ require 'simplecov' -SimpleCov.start 'rails' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) From c8f0c28703f1c5d7953fcfa3f2052c24da434e56 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Thu, 17 Mar 2016 19:57:39 +0100 Subject: [PATCH 11/34] Add Tips and Tricks section to contribution guide Knowing how to use guard for continuous test execution motivates to maintain a valuable test suite. Debugging integration tests (e.g., via `save_and_open_page`) gives you a glimpse idea what's going on: https://github.com/jnicklas/capybara#debugging --- CONTRIBUTING.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8720cf5..4400af4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,4 +21,23 @@ You should also periodically push your topic branches during development. That way, there will always be a reasonably current backup of your work in the upstream repository, and the whole team can get a feel on what others are -working on. \ No newline at end of file +working on. + +## Tips and Tricks + +* Continuous test execution and live reload: + + ``` + bundle exec guard + ``` + + * Automatically runs affected tests on file edit. Type `all` to manually run all tests. + * Automatically reloads a page on asset modification via the following browser plugin: http://livereload.com/extensions/ + +* Save a snapshot of the page and open it in a browser for inspection: + + ``` + save_and_open_page + ``` + + * Use anywhere within an integration test. From bf9640702e00c860da0e44dd938bb4281d64e437 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Thu, 17 Mar 2016 21:42:15 +0100 Subject: [PATCH 12/34] Remove dummy tests --- test/integration/general_stories_test.rb | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/test/integration/general_stories_test.rb b/test/integration/general_stories_test.rb index 0bbcf0b..8499215 100644 --- a/test/integration/general_stories_test.rb +++ b/test/integration/general_stories_test.rb @@ -49,19 +49,4 @@ def component_fieldset(n) visit blueprints_path assert page.has_content?("Displaying all #{n} blueprints") end - - test "get application list" do - get cloud_applications_path - assert_response :success - end - - test "get blueprints list" do - get blueprints_path - assert_response :success - end - - test "get providers overview" do - get providers_path - assert_response :success - end end From bf4dfce4872f3287f3f0d44ef88543a71da91d89 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Thu, 17 Mar 2016 21:45:38 +0100 Subject: [PATCH 13/34] Split general_stories_test --- test/integration/blueprint_stories_test.rb | 47 ++++++++++++++++++++++ test/integration/general_stories_test.rb | 44 -------------------- 2 files changed, 47 insertions(+), 44 deletions(-) create mode 100644 test/integration/blueprint_stories_test.rb diff --git a/test/integration/blueprint_stories_test.rb b/test/integration/blueprint_stories_test.rb new file mode 100644 index 0000000..1f0df9e --- /dev/null +++ b/test/integration/blueprint_stories_test.rb @@ -0,0 +1,47 @@ +require 'test_helper' + +class BlueprintStoriesTest < ActionDispatch::IntegrationTest + test 'create new blueprint' do + c1 = build_stubbed(:app_component) + c2 = build_stubbed(:db_component) + bp = build_stubbed(:mt_blueprint) + + visit blueprints_path + assert page.has_content?('No blueprints found') + + first(:link, 'New Blueprint').click + fill_in('Name', with: bp.name) + fill_in('Body', with: bp.body) + + add_component(c1, 1) + add_component(c2, 2) + click_button 'Save' + + assert page.has_content?('Blueprint was successfully created.') + assert page.has_content?(bp.name) + assert page.has_content?(c1.name) + # Check Markdown rendering of title header + assert page.has_xpath?('//section[@class="component"]/h2[text()="Introduction"]') + assert page.has_content?(c2.name) + end + + def add_component(component, n) + click_link 'Add Component' + within(:xpath, "//form/div[3]/fieldset[#{component_fieldset(n)}]") do + fill_in('Name', with: component.name) + fill_in('Component type', with: component.component_type) + fill_in('Body', with: component.body) + end + end + + def component_fieldset(n) + (n*2) - 1 + end + + test 'list blueprints' do + n = 5 + create_list(:blueprint, n) + visit blueprints_path + assert page.has_content?("Displaying all #{n} blueprints") + end +end diff --git a/test/integration/general_stories_test.rb b/test/integration/general_stories_test.rb index 8499215..eb6dc46 100644 --- a/test/integration/general_stories_test.rb +++ b/test/integration/general_stories_test.rb @@ -5,48 +5,4 @@ class GeneralStoriesTest < ActionDispatch::IntegrationTest visit root_path assert page.has_content?('Finely crafted Cloud Application Deployments') end - - test 'create new blueprint' do - c1 = build_stubbed(:app_component) - c2 = build_stubbed(:db_component) - bp = build_stubbed(:mt_blueprint) - - visit blueprints_path - assert page.has_content?('No blueprints found') - - first(:link, 'New Blueprint').click - fill_in('Name', with: bp.name) - fill_in('Body', with: bp.body) - - add_component(c1, 1) - add_component(c2, 2) - click_button 'Save' - - assert page.has_content?('Blueprint was successfully created.') - assert page.has_content?(bp.name) - assert page.has_content?(c1.name) - # Check Markdown rendering of title header - assert page.has_xpath?('//section[@class="component"]/h2[text()="Introduction"]') - assert page.has_content?(c2.name) - end - - def add_component(component, n) - click_link 'Add Component' - within(:xpath, "//form/div[3]/fieldset[#{component_fieldset(n)}]") do - fill_in('Name', with: component.name) - fill_in('Component type', with: component.component_type) - fill_in('Body', with: component.body) - end - end - - def component_fieldset(n) - (n*2) - 1 - end - - test 'list blueprints' do - n = 5 - create_list(:blueprint, n) - visit blueprints_path - assert page.has_content?("Displaying all #{n} blueprints") - end end From 35bfa7278882428dfa1e8bab743a56923008d4db Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Thu, 17 Mar 2016 21:53:43 +0100 Subject: [PATCH 14/34] Fix blueprint with components factory --- test/factories.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/factories.rb b/test/factories.rb index 3501d58..7ca1235 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -7,11 +7,13 @@ factory :blueprint do sequence(:name) { |n| "Blueprint#{n}"} sequence(:body) { |n| "# Blueprint body#{n} with lots of info about this app type." } + + #blueprint_with_components transient do - component_count 3 + components_count 1 end - after(:create) do |component, evaluator| - create_list(:component, evaluator.component_count) + after(:create) do |blueprint, evaluator| + create_list(:component, evaluator.components_count, blueprint: blueprint) end end From 102f5b4d434d54e9c3602a5c384759caeeca57f0 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Thu, 17 Mar 2016 21:54:37 +0100 Subject: [PATCH 15/34] Add blueprint edit and destroy story test --- test/integration/blueprint_stories_test.rb | 38 +++++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/test/integration/blueprint_stories_test.rb b/test/integration/blueprint_stories_test.rb index 1f0df9e..b18f6e6 100644 --- a/test/integration/blueprint_stories_test.rb +++ b/test/integration/blueprint_stories_test.rb @@ -1,6 +1,13 @@ require 'test_helper' class BlueprintStoriesTest < ActionDispatch::IntegrationTest + test 'list blueprints' do + n = 5 + create_list(:blueprint, n) + visit blueprints_path + assert page.has_content?("Displaying all #{n} blueprints") + end + test 'create new blueprint' do c1 = build_stubbed(:app_component) c2 = build_stubbed(:db_component) @@ -38,10 +45,31 @@ def component_fieldset(n) (n*2) - 1 end - test 'list blueprints' do - n = 5 - create_list(:blueprint, n) - visit blueprints_path - assert page.has_content?("Displaying all #{n} blueprints") + test 'edit blueprint' do + bp = create(:blueprint, components_count: 2) + num_components = bp.components.count + bp.name = 'Renamed Blueprint' + + visit blueprint_path(bp) + click_link 'Edit' + + fill_in 'blueprint_name', with: bp.name + check 'blueprint_components_attributes_1__destroy' + click_button 'Save' + + new_bp = Blueprint.find(bp.id) + assert new_bp.components.count == (num_components - 1) + assert page.has_content?(bp.name) + end + + test 'destroy blueprint' do + bp = create(:blueprint) + + visit blueprint_path(bp) + click_link 'Destroy' + page.accept_alert + + assert_raises(ActiveRecord::RecordNotFound) { bp.reload } + assert page.has_content? 'Blueprint was successfully destroyed.' end end From 6a447bacce6ba807bae01d710f47929b223e3bc9 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Fri, 18 Mar 2016 11:22:40 +0100 Subject: [PATCH 16/34] Add copy blueprint story test --- test/integration/blueprint_stories_test.rb | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/integration/blueprint_stories_test.rb b/test/integration/blueprint_stories_test.rb index b18f6e6..490fa7a 100644 --- a/test/integration/blueprint_stories_test.rb +++ b/test/integration/blueprint_stories_test.rb @@ -54,7 +54,7 @@ def component_fieldset(n) click_link 'Edit' fill_in 'blueprint_name', with: bp.name - check 'blueprint_components_attributes_1__destroy' + check 'blueprint_components_attributes_0__destroy' click_button 'Save' new_bp = Blueprint.find(bp.id) @@ -72,4 +72,24 @@ def component_fieldset(n) assert_raises(ActiveRecord::RecordNotFound) { bp.reload } assert page.has_content? 'Blueprint was successfully destroyed.' end + + test 'copy blueprint' do + bp = create(:blueprint, components_count: 2) + new_component = build_stubbed(:component) + new_name = 'Cloned blueprint' + + visit blueprint_path(bp) + click_link 'Copy' + + check 'blueprint_components_attributes_1__destroy' + fill_in('Name', with: new_name, match: :first) + click_button 'Save' + + assert page.has_content? 'Blueprint was successfully created.' + assert page.has_content? new_name + new_bp = Blueprint.find_by_name new_name + new_bp.components.count == 1 + # Updating the new blueprint must not affect the old one + assert bp.components.count == 2 + end end From 1d467aab2e6a58cdbc423fed0c472f427e84160d Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Fri, 18 Mar 2016 12:54:32 +0100 Subject: [PATCH 17/34] Fix missing assert and bracket consistency --- test/integration/blueprint_stories_test.rb | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/integration/blueprint_stories_test.rb b/test/integration/blueprint_stories_test.rb index 490fa7a..ed0683b 100644 --- a/test/integration/blueprint_stories_test.rb +++ b/test/integration/blueprint_stories_test.rb @@ -5,7 +5,7 @@ class BlueprintStoriesTest < ActionDispatch::IntegrationTest n = 5 create_list(:blueprint, n) visit blueprints_path - assert page.has_content?("Displaying all #{n} blueprints") + assert page.has_content? "Displaying all #{n} blueprints" end test 'create new blueprint' do @@ -14,30 +14,30 @@ class BlueprintStoriesTest < ActionDispatch::IntegrationTest bp = build_stubbed(:mt_blueprint) visit blueprints_path - assert page.has_content?('No blueprints found') + assert page.has_content? 'No blueprints found' first(:link, 'New Blueprint').click - fill_in('Name', with: bp.name) - fill_in('Body', with: bp.body) + fill_in 'Name', with: bp.name + fill_in 'Body', with: bp.body add_component(c1, 1) add_component(c2, 2) click_button 'Save' - assert page.has_content?('Blueprint was successfully created.') - assert page.has_content?(bp.name) - assert page.has_content?(c1.name) + assert page.has_content? 'Blueprint was successfully created.' + assert page.has_content? bp.name + assert page.has_content? c1.name # Check Markdown rendering of title header assert page.has_xpath?('//section[@class="component"]/h2[text()="Introduction"]') - assert page.has_content?(c2.name) + assert page.has_content? c2.name end def add_component(component, n) click_link 'Add Component' within(:xpath, "//form/div[3]/fieldset[#{component_fieldset(n)}]") do - fill_in('Name', with: component.name) - fill_in('Component type', with: component.component_type) - fill_in('Body', with: component.body) + fill_in 'Name', with: component.name + fill_in 'Component type', with: component.component_type + fill_in 'Body', with: component.body end end @@ -58,8 +58,8 @@ def component_fieldset(n) click_button 'Save' new_bp = Blueprint.find(bp.id) - assert new_bp.components.count == (num_components - 1) - assert page.has_content?(bp.name) + assert_equal (num_components - 1), new_bp.components.count + assert page.has_content? bp.name end test 'destroy blueprint' do @@ -82,14 +82,14 @@ def component_fieldset(n) click_link 'Copy' check 'blueprint_components_attributes_1__destroy' - fill_in('Name', with: new_name, match: :first) + fill_in 'Name', with: new_name, match: :first click_button 'Save' assert page.has_content? 'Blueprint was successfully created.' assert page.has_content? new_name new_bp = Blueprint.find_by_name new_name - new_bp.components.count == 1 + assert_equal 1, new_bp.components.count # Updating the new blueprint must not affect the old one - assert bp.components.count == 2 + assert_equal 2, bp.components.count end end From 52c49281918041ed3eb87bb6c741fd9c78811d6e Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sat, 19 Mar 2016 16:34:21 +0100 Subject: [PATCH 18/34] Add PhantomJS installation to wercker build The JS-enabled tests require the `phantomjs` binary. Although the script conditionally installs PhantomJS, it needs to be downloaded on every build because wercker steps are not cached. --- wercker.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/wercker.yml b/wercker.yml index 4f376fa..424467c 100644 --- a/wercker.yml +++ b/wercker.yml @@ -23,6 +23,20 @@ build: # Read more about steps on our dev center: # http://devcenter.wercker.com/docs/steps/index.html steps: + - script: + name: Install PhantomJS + code: | + VERSION=2.1.1 && + # NOTE: Does not check for legacy version + if ! which phantomjs; then + INSTALL_DIR=/usr/local/share && + PHANTOM_JS=phantomjs-$VERSION-linux-x86_64 && + PHANTOM_JS_ZIP=$PHANTOM_JS.tar.bz2 && + cd $INSTALL_DIR && + curl --remote-name --location -- https://bitbucket.org/ariya/phantomjs/downloads/$PHANTOM_JS_ZIP && + tar xjf $PHANTOM_JS_ZIP && + ln -s $INSTALL_DIR/$PHANTOM_JS/bin/phantomjs /usr/local/bin/phantomjs + fi - script: name: Bundle config code: | From 7acb51092498a88ac1231811aecb262a5a9e601f Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sat, 19 Mar 2016 16:34:41 +0100 Subject: [PATCH 19/34] Remove DB seed from wercker build Integration tests require a clean database and truncation/deletion DB cleaning strategy would erase the seeds anyways after the first test. Some tests fail if they are executed immediately after seeding the database. Example: test that checks whether exactly n blueprints are displayed after creating n. --- wercker.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/wercker.yml b/wercker.yml index 424467c..ec2987f 100644 --- a/wercker.yml +++ b/wercker.yml @@ -48,11 +48,6 @@ build: - script: name: Set up db code: bundle exec rake db:schema:load RAILS_ENV=test - - - script: - name: Seed db - code: bundle exec rake db:seed RAILS_ENV=test - - script: name: test code: bundle exec rake test From aeb23938a3146a197aed993590b2bb82432df27a Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sat, 19 Mar 2016 16:39:31 +0100 Subject: [PATCH 20/34] Add wercker build files to gitignore We should forsee local wercker build files in the gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index f501c5f..918197f 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,9 @@ # Ignore simplecov coverage report /coverage/ + +# Ignore wercker build files +/_builds/ +/_cache/ +/_projects/ +/_steps/ From 519dd4cc387355c42233bb12f805e5af9374189b Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sat, 19 Mar 2016 16:41:55 +0100 Subject: [PATCH 21/34] Add instructions how to run local wercker build --- CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4400af4..263e79c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,3 +41,13 @@ working on. ``` * Use anywhere within an integration test. + +* Test Wercker CI build locally + + ``` + wercker build + ``` + + * Requires wercker CLI: http://wercker.com/cli/ + * Use `--attach-on-error` to debug failing builds + * Use `--docker-local` to use locally cached containers From a2243d3220f974c22f936c27d1dcd4798a3efdda Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sat, 19 Mar 2016 17:27:54 +0100 Subject: [PATCH 22/34] Add mandatory resource_type to resource factory --- test/factories.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/factories.rb b/test/factories.rb index 7ca1235..d66a51d 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -99,6 +99,7 @@ factory :resource do sequence(:name) { |n| "Resource#{n}"} + resource_type 'compute' end factory :slo_set do From dd7f7deefa3b0b728a7a033ecf0b01b54ae1ad47 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sat, 19 Mar 2016 21:56:32 +0100 Subject: [PATCH 23/34] Clean DB with deletion before running test suite Each test suite run should start in clean state. Any trash in the test database, possibly caused by failing or interrupted previous tests, will get deleted. --- test/test_helper.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_helper.rb b/test/test_helper.rb index 14c3d73..cd04f2b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -12,6 +12,9 @@ ENV, Minitest.backtrace_filter) +# Clean the database from any prior trash (e.g., caused by failing/interrupted tests) +DatabaseCleaner.clean_with :deletion + require 'capybara/rails' require 'capybara/poltergeist' class ActionDispatch::IntegrationTest From 0131c276b8c77f081725a7b36d52b9dcbef4fc36 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sat, 19 Mar 2016 22:10:15 +0100 Subject: [PATCH 24/34] Refactor factories Top-down ordering (i.e., most high level factories first) improves clarify Proper factory nesting reduces redundancy and clarifies membership Correctly refer *_component factories in concrete_component traits --- test/factories.rb | 100 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/test/factories.rb b/test/factories.rb index d66a51d..a6d33ac 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -1,9 +1,4 @@ FactoryGirl.define do - factory :cloud_application do - sequence(:name) { |n| "CloudApplication#{n}"} - blueprint - end - factory :blueprint do sequence(:name) { |n| "Blueprint#{n}"} sequence(:body) { |n| "# Blueprint body#{n} with lots of info about this app type." } @@ -23,7 +18,7 @@ - Web Frontend - Application Server - Database Backend' - components { |c| [c.association(:component), c.association(:db_component)] } + components { |c| [c.association(:app_component), c.association(:db_component)] } end factory :component do @@ -31,36 +26,45 @@ component_type 'application-server' sequence(:body) { |n| "# Component body#{n} with info about single component" } deployment_rule - end - factory :app_component, class: Component do - name 'Application Server' - component_type 'application-server' - body '# Introduction + factory :app_component do + name 'Application Server' + component_type 'application-server' + body '# Introduction Explained at [Wikipedia][1] [1]: https://en.wikipedia.org/wiki/Application_server' - deployment_rule - end + deployment_rule + end - factory :db_component, class: Component do - name 'Database Server' - component_type 'database' - body '# Performance Considerations + factory :db_component do + name 'Database Server' + component_type 'database' + body '# Performance Considerations Typically Disk I/O, RAM bound (CPU not as important)' - deployment_rule + deployment_rule + end + + factory :lb_component do + name 'Load Balancer' + component_type 'load-balancer' + association :blueprint, factory: :mt_blueprint + end + + factory :cdn_component do + name 'CDN' + component_type 'cdn' + association :blueprint, factory: :mt_blueprint + end end - factory :lb_component, class: Component do - name 'Load Balancer' - component_type 'load-balancer' - association :blueprint, factory: :mt_blueprint + factory :deployment_rule do + more_attributes '{"when x users":"then y servers"}' end - factory :cdn_component, class: Component do - name 'CDN' - component_type 'cdn' - association :blueprint, factory: :mt_blueprint + factory :cloud_application do + sequence(:name) { |n| "Cloud application#{n}"} + blueprint end factory :concrete_component do @@ -68,40 +72,25 @@ trait :webrick do name 'WebRick Application Server' - association component: :app_component - cloud_application + association :component, factory: :app_component + end + + trait :postgres do + name 'PostgreSQL Database' + association :component, factory: :db_component end trait :sqlite do name 'SQLite Database' - association component: :db_component - cloud_application + association :component, factory: :db_component end trait :nginx do name 'NginX Load Balancer' - association component: :lb_component - cloud_application + association :component, factory: :lb_component end end - factory :deployment_rule do - more_attributes '{"when x users":"then y servers"}' - end - - factory :deployment_recommendation do - # This model initially had no columns defined. - end - - factory :provider do - sequence(:name) { |n| "Provider#{n}"} - end - - factory :resource do - sequence(:name) { |n| "Resource#{n}"} - resource_type 'compute' - end - factory :slo_set do more_attributes '{ "metric": "availability", "relation": ">=", "value": "0.995" }' concrete_component @@ -121,4 +110,17 @@ concrete_component :nginx end end + + factory :provider do + sequence(:name) { |n| "Provider#{n}"} + end + + factory :resource do + sequence(:name) { |n| "Resource#{n}"} + resource_type 'compute' + end + + factory :deployment_recommendation do + # This model initially had no columns defined. + end end From 15374d4b17a3ea579154b193c6b713fbb538e9c3 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sat, 19 Mar 2016 22:14:44 +0100 Subject: [PATCH 25/34] Add blueprint updated assert to story test Improve naming --- test/integration/blueprint_stories_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/blueprint_stories_test.rb b/test/integration/blueprint_stories_test.rb index ed0683b..143ebd1 100644 --- a/test/integration/blueprint_stories_test.rb +++ b/test/integration/blueprint_stories_test.rb @@ -48,18 +48,18 @@ def component_fieldset(n) test 'edit blueprint' do bp = create(:blueprint, components_count: 2) num_components = bp.components.count - bp.name = 'Renamed Blueprint' + new_name = 'Renamed Blueprint' visit blueprint_path(bp) click_link 'Edit' - - fill_in 'blueprint_name', with: bp.name + fill_in 'blueprint_name', with: new_name check 'blueprint_components_attributes_0__destroy' click_button 'Save' new_bp = Blueprint.find(bp.id) assert_equal (num_components - 1), new_bp.components.count - assert page.has_content? bp.name + assert page.has_content? 'Blueprint was successfully updated.' + assert page.has_content? new_name end test 'destroy blueprint' do From f15a296f3f446a45c5177e35fb30d167f214b266 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sun, 20 Mar 2016 14:07:47 +0100 Subject: [PATCH 26/34] Add new cloud app from blueprint IT --- test/factories.rb | 24 ++++++++++++++++++ .../cloud_application_stories_test.rb | 25 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 test/integration/cloud_application_stories_test.rb diff --git a/test/factories.rb b/test/factories.rb index a6d33ac..288b152 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -65,6 +65,28 @@ factory :cloud_application do sequence(:name) { |n| "Cloud application#{n}"} blueprint + + # cloud_application_with_concrete_components + transient do + concrete_components_count 1 + end + after(:create) do |cloud_app, evaluator| + create_list(:concrete_component, evaluator.concrete_components_count, cloud_application: cloud_app) + end + end + + factory :rails_cloud_application, class: CloudApplication do + name 'Rails Application' + body 'A traditional wep application, let\'s say a web shop with + * Rails as the application server + * PostgreSQL as database' + + after(:create) do |rails_cloud_app| + cc = [create(:concrete_component, :webrick, cloud_application: rails_cloud_app), + create(:concrete_component, :postgres, cloud_application: rails_cloud_app)] + rails_cloud_app.concrete_components = cc + rails_cloud_app.save + end end factory :concrete_component do @@ -72,11 +94,13 @@ trait :webrick do name 'WebRick Application Server' + body 'Specific things about the Rails app.' association :component, factory: :app_component end trait :postgres do name 'PostgreSQL Database' + body 'Specific things about this postgres db.' association :component, factory: :db_component end diff --git a/test/integration/cloud_application_stories_test.rb b/test/integration/cloud_application_stories_test.rb new file mode 100644 index 0000000..48f1ebf --- /dev/null +++ b/test/integration/cloud_application_stories_test.rb @@ -0,0 +1,25 @@ +require 'test_helper' + +class CloudApplicationStoriesTest < ActionDispatch::IntegrationTest + test 'new cloud application from blueprint' do + bp = create(:mt_blueprint) + cloud_app = build_stubbed(:rails_cloud_application) + webrick = build_stubbed(:concrete_component, :webrick) + postgres = build_stubbed(:concrete_component, :postgres) + + visit blueprint_path(bp) + click_link 'New Application Instance' + + fill_in 'cloud_application_name', with: cloud_app.name + fill_in 'cloud_application_body', with: cloud_app.body + fill_in 'cloud_application_concrete_components_attributes_0_name', with: webrick.name + fill_in 'cloud_application_concrete_components_attributes_0_body', with: webrick.body + fill_in 'cloud_application_concrete_components_attributes_1_name', with: postgres.name + fill_in 'cloud_application_concrete_components_attributes_1_body', with: postgres.body + click_button 'Save' + + assert page.has_content? 'Cloud application was successfully created.' + assert page.has_content? webrick.name + assert page.has_content? postgres.name + end +end From 3b421f95bf83e851e94121afd828dc38045edc75 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sun, 20 Mar 2016 14:09:58 +0100 Subject: [PATCH 27/34] Add `show_page` helper for JS debugging Capybara's `save_and_open_page` html looks very different without considering assets such as CSS and JS. The `show_page` helper leverages the `public` directory of a concurrently running rails server to load these assets such that the page looks authentic. Also enable asset debugging for the test environment. Capybara is also capable of launching its own server to serve assets during test execution: \# Use a Capybara server to load assets Capybara.server_port = 3001 Capybara.app_host = "http://localhost:#{Capybara.server_port}" ActionController::Base.asset_host = Capybara.app_host --- .gitignore | 3 +++ CONTRIBUTING.md | 6 ++---- config/environments/test.rb | 6 +++++- test/test_helper.rb | 24 ++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 918197f..1a5a7c4 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ /_cache/ /_projects/ /_steps/ + +# Ignore Capybara page savings from IT debugging +/public/capybara diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 263e79c..4845651 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,14 +34,12 @@ working on. * Automatically runs affected tests on file edit. Type `all` to manually run all tests. * Automatically reloads a page on asset modification via the following browser plugin: http://livereload.com/extensions/ -* Save a snapshot of the page and open it in a browser for inspection: +* Save a snapshot of the page during an integration test: ``` - save_and_open_page + show_page ``` - * Use anywhere within an integration test. - * Test Wercker CI build locally ``` diff --git a/config/environments/test.rb b/config/environments/test.rb index 39b316e..32c2038 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -12,6 +12,10 @@ # preloads Rails for running tests, you may have to set it to true. config.eager_load = false + # Serve assets for JS-enabled tests and enable debug mode + config.assets.enabled = true + config.assets.debug = true + # Configure static file server for tests with Cache-Control for performance. config.serve_static_files = true config.static_cache_control = 'public, max-age=3600' @@ -41,5 +45,5 @@ # config.action_view.raise_on_missing_translations = true # Custom Cloud Stove config - config.x.gravatar_host = 'localhost' + config.x.gravatar_host = 'localhost:3000' end diff --git a/test/test_helper.rb b/test/test_helper.rb index cd04f2b..d336e89 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -23,6 +23,30 @@ class ActionDispatch::IntegrationTest # Use the PhantomJS headless WebKit browser Capybara.javascript_driver = :poltergeist + + # Side-load assets from concurrently running app server + # when using `save_and_open_page` + Capybara.asset_host = 'http://localhost:3000' + + # Saving a page to public (for debugging) allows to serve it authentical (with assets) via `rails s` + def show_page + file = capybara_file + dest = Rails.root.join('public', 'capybara', file) + save_page dest + url = "http://localhost:3000/capybara/#{file}" + puts "File saved to: #{dest} + Accessible via: #{url}" + # Uncomment this if you want to immediately open the file + # system("open #{url}") + end + + private + + # Analogous to Capybara: https://github.com/jnicklas/capybara/blob/07e777742532ba4a0e4957f3241fc4fb6a903e86/lib/capybara/session.rb#L724 + def capybara_file + timestamp = Time.new.strftime("%Y%m%d%H%M%S") + "capybara-#{timestamp}#{rand(10**10)}.html" + end end class ActiveSupport::TestCase From 00b2a0eddfe7e3a355918a76d6f46453613a0e63 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sun, 20 Mar 2016 14:22:23 +0100 Subject: [PATCH 28/34] Add factory helper for loading hashes from json Overly long `more_attributes` fields are cumbersome to define within factories. This helper allows to load these hashes from json files in the fixtures directory (similar to WebMock). --- test/factories.rb | 1 + test/helpers/factory_girl.rb | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 test/helpers/factory_girl.rb diff --git a/test/factories.rb b/test/factories.rb index 288b152..ea29001 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -1,3 +1,4 @@ +require_relative 'helpers/factory_girl' FactoryGirl.define do factory :blueprint do sequence(:name) { |n| "Blueprint#{n}"} diff --git a/test/helpers/factory_girl.rb b/test/helpers/factory_girl.rb new file mode 100644 index 0000000..9ae02e7 --- /dev/null +++ b/test/helpers/factory_girl.rb @@ -0,0 +1,7 @@ +require 'json' +module FactoryHelpers + def self.hash_from_json(filename) + file = File.read(Rails.root + 'test/fixtures/factories' + filename) + JSON.parse(file) + end +end From 5b30fa2d0b90478a4f546c4430fccd606166c90f Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sun, 20 Mar 2016 14:25:48 +0100 Subject: [PATCH 29/34] Add listing providers test This test uses a subset of the aws provider data loaded from a json file. Error: While the provider name gets displayed correctly, the price list does not show up within the PhantomJS. The same behavior was observed using Selenium as Capybara test driver. --- test/factories.rb | 5 + test/fixtures/factories/provider-amazon.json | 1876 ++++++++++++++++++ test/integration/provider_stories_test.rb | 12 + 3 files changed, 1893 insertions(+) create mode 100644 test/fixtures/factories/provider-amazon.json create mode 100644 test/integration/provider_stories_test.rb diff --git a/test/factories.rb b/test/factories.rb index ea29001..413fe4b 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -138,6 +138,11 @@ factory :provider do sequence(:name) { |n| "Provider#{n}"} + + factory :aws_provider do + name 'Amazon' + more_attributes FactoryHelpers::hash_from_json('provider-amazon.json') + end end factory :resource do diff --git a/test/fixtures/factories/provider-amazon.json b/test/fixtures/factories/provider-amazon.json new file mode 100644 index 0000000..77bb2df --- /dev/null +++ b/test/fixtures/factories/provider-amazon.json @@ -0,0 +1,1876 @@ +{ + "sla": { + "compute": { + "uri": "https://aws.amazon.com/ec2/sla/", + "availability": "0.9995" + }, + "storage": { + "uri": "https://aws.amazon.com/s3/sla/" + } + }, + "pricelist": { + "compute": { + "vers": 0.01, + "config": { + "rate": "perhr", + "valueColumns": [ + "vCPU", + "ECU", + "memoryGiB", + "storageGB", + "linux" + ], + "currencies": [ + "USD" + ], + "regions": [ + { + "region": "us-east-1", + "instanceTypes": [ + { + "type": "generalCurrentGen", + "sizes": [ + { + "size": "t2.nano", + "vCPU": "1", + "ECU": "variable", + "memoryGiB": "0.5", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.0065" + } + } + ] + }, + { + "size": "t2.micro", + "vCPU": "1", + "ECU": "variable", + "memoryGiB": "1", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.013" + } + } + ] + }, + { + "size": "t2.small", + "vCPU": "1", + "ECU": "variable", + "memoryGiB": "2", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.026" + } + } + ] + }, + { + "size": "t2.medium", + "vCPU": "2", + "ECU": "variable", + "memoryGiB": "4", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.052" + } + } + ] + }, + { + "size": "t2.large", + "vCPU": "2", + "ECU": "variable", + "memoryGiB": "8", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.104" + } + } + ] + }, + { + "size": "m4.large", + "vCPU": "2", + "ECU": "6.5", + "memoryGiB": "8", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.12" + } + } + ] + }, + { + "size": "m4.xlarge", + "vCPU": "4", + "ECU": "13", + "memoryGiB": "16", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.239" + } + } + ] + }, + { + "size": "m4.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "32", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.479" + } + } + ] + }, + { + "size": "m4.4xlarge", + "vCPU": "16", + "ECU": "53.5", + "memoryGiB": "64", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.958" + } + } + ] + }, + { + "size": "m4.10xlarge", + "vCPU": "40", + "ECU": "124.5", + "memoryGiB": "160", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.394" + } + } + ] + }, + { + "size": "m3.medium", + "vCPU": "1", + "ECU": "3", + "memoryGiB": "3.75", + "storageGB": "1 x 4 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.067" + } + } + ] + }, + { + "size": "m3.large", + "vCPU": "2", + "ECU": "6.5", + "memoryGiB": "7.5", + "storageGB": "1 x 32 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.133" + } + } + ] + }, + { + "size": "m3.xlarge", + "vCPU": "4", + "ECU": "13", + "memoryGiB": "15", + "storageGB": "2 x 40 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.266" + } + } + ] + }, + { + "size": "m3.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "30", + "storageGB": "2 x 80 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.532" + } + } + ] + } + ] + }, + { + "type": "computeCurrentGen", + "sizes": [ + { + "size": "c4.large", + "vCPU": "2", + "ECU": "8", + "memoryGiB": "3.75", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.105" + } + } + ] + }, + { + "size": "c4.xlarge", + "vCPU": "4", + "ECU": "16", + "memoryGiB": "7.5", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.209" + } + } + ] + }, + { + "size": "c4.2xlarge", + "vCPU": "8", + "ECU": "31", + "memoryGiB": "15", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.419" + } + } + ] + }, + { + "size": "c4.4xlarge", + "vCPU": "16", + "ECU": "62", + "memoryGiB": "30", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.838" + } + } + ] + }, + { + "size": "c4.8xlarge", + "vCPU": "36", + "ECU": "132", + "memoryGiB": "60", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.675" + } + } + ] + }, + { + "size": "c3.large", + "vCPU": "2", + "ECU": "7", + "memoryGiB": "3.75", + "storageGB": "2 x 16 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.105" + } + } + ] + }, + { + "size": "c3.xlarge", + "vCPU": "4", + "ECU": "14", + "memoryGiB": "7.5", + "storageGB": "2 x 40 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.21" + } + } + ] + }, + { + "size": "c3.2xlarge", + "vCPU": "8", + "ECU": "28", + "memoryGiB": "15", + "storageGB": "2 x 80 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.42" + } + } + ] + }, + { + "size": "c3.4xlarge", + "vCPU": "16", + "ECU": "55", + "memoryGiB": "30", + "storageGB": "2 x 160 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.84" + } + } + ] + }, + { + "size": "c3.8xlarge", + "vCPU": "32", + "ECU": "108", + "memoryGiB": "60", + "storageGB": "2 x 320 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.68" + } + } + ] + } + ] + }, + { + "type": "gpuCurrentGen", + "sizes": [ + { + "size": "g2.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "15", + "storageGB": "60 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.65" + } + } + ] + }, + { + "size": "g2.8xlarge", + "vCPU": "32", + "ECU": "104", + "memoryGiB": "60", + "storageGB": "2 x 120 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.6" + } + } + ] + } + ] + }, + { + "type": "hiMemCurrentGen", + "sizes": [ + { + "size": "r3.large", + "vCPU": "2", + "ECU": "6.5", + "memoryGiB": "15", + "storageGB": "1 x 32 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.166" + } + } + ] + }, + { + "size": "r3.xlarge", + "vCPU": "4", + "ECU": "13", + "memoryGiB": "30.5", + "storageGB": "1 x 80 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.333" + } + } + ] + }, + { + "size": "r3.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "61", + "storageGB": "1 x 160 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.665" + } + } + ] + }, + { + "size": "r3.4xlarge", + "vCPU": "16", + "ECU": "52", + "memoryGiB": "122", + "storageGB": "1 x 320 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.33" + } + } + ] + }, + { + "size": "r3.8xlarge", + "vCPU": "32", + "ECU": "104", + "memoryGiB": "244", + "storageGB": "2 x 320 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.66" + } + } + ] + } + ] + }, + { + "type": "storageCurrentGen", + "sizes": [ + { + "size": "i2.xlarge", + "vCPU": "4", + "ECU": "14", + "memoryGiB": "30.5", + "storageGB": "1 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.853" + } + } + ] + }, + { + "size": "i2.2xlarge", + "vCPU": "8", + "ECU": "27", + "memoryGiB": "61", + "storageGB": "2 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.705" + } + } + ] + }, + { + "size": "i2.4xlarge", + "vCPU": "16", + "ECU": "53", + "memoryGiB": "122", + "storageGB": "4 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "3.41" + } + } + ] + }, + { + "size": "i2.8xlarge", + "vCPU": "32", + "ECU": "104", + "memoryGiB": "244", + "storageGB": "8 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "6.82" + } + } + ] + }, + { + "size": "d2.xlarge", + "vCPU": "4", + "ECU": "14", + "memoryGiB": "30.5", + "storageGB": "3 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.69" + } + } + ] + }, + { + "size": "d2.2xlarge", + "vCPU": "8", + "ECU": "28", + "memoryGiB": "61", + "storageGB": "6 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.38" + } + } + ] + }, + { + "size": "d2.4xlarge", + "vCPU": "16", + "ECU": "56", + "memoryGiB": "122", + "storageGB": "12 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.76" + } + } + ] + }, + { + "size": "d2.8xlarge", + "vCPU": "36", + "ECU": "116", + "memoryGiB": "244", + "storageGB": "24 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "5.52" + } + } + ] + } + ] + } + ] + }, + { + "region": "eu-west-1", + "instanceTypes": [ + { + "type": "generalCurrentGen", + "sizes": [ + { + "size": "t2.nano", + "vCPU": "1", + "ECU": "variable", + "memoryGiB": "0.5", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.007" + } + } + ] + }, + { + "size": "t2.micro", + "vCPU": "1", + "ECU": "variable", + "memoryGiB": "1", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.014" + } + } + ] + }, + { + "size": "t2.small", + "vCPU": "1", + "ECU": "variable", + "memoryGiB": "2", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.028" + } + } + ] + }, + { + "size": "t2.medium", + "vCPU": "2", + "ECU": "variable", + "memoryGiB": "4", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.056" + } + } + ] + }, + { + "size": "t2.large", + "vCPU": "2", + "ECU": "variable", + "memoryGiB": "8", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.112" + } + } + ] + }, + { + "size": "m4.large", + "vCPU": "2", + "ECU": "6.5", + "memoryGiB": "8", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.132" + } + } + ] + }, + { + "size": "m4.xlarge", + "vCPU": "4", + "ECU": "13", + "memoryGiB": "16", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.264" + } + } + ] + }, + { + "size": "m4.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "32", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.528" + } + } + ] + }, + { + "size": "m4.4xlarge", + "vCPU": "16", + "ECU": "53.5", + "memoryGiB": "64", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.056" + } + } + ] + }, + { + "size": "m4.10xlarge", + "vCPU": "40", + "ECU": "124.5", + "memoryGiB": "160", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.641" + } + } + ] + }, + { + "size": "m3.medium", + "vCPU": "1", + "ECU": "3", + "memoryGiB": "3.75", + "storageGB": "1 x 4 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.073" + } + } + ] + }, + { + "size": "m3.large", + "vCPU": "2", + "ECU": "6.5", + "memoryGiB": "7.5", + "storageGB": "1 x 32 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.146" + } + } + ] + }, + { + "size": "m3.xlarge", + "vCPU": "4", + "ECU": "13", + "memoryGiB": "15", + "storageGB": "2 x 40 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.293" + } + } + ] + }, + { + "size": "m3.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "30", + "storageGB": "2 x 80 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.585" + } + } + ] + } + ] + }, + { + "type": "computeCurrentGen", + "sizes": [ + { + "size": "c4.large", + "vCPU": "2", + "ECU": "8", + "memoryGiB": "3.75", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.119" + } + } + ] + }, + { + "size": "c4.xlarge", + "vCPU": "4", + "ECU": "16", + "memoryGiB": "7.5", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.238" + } + } + ] + }, + { + "size": "c4.2xlarge", + "vCPU": "8", + "ECU": "31", + "memoryGiB": "15", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.477" + } + } + ] + }, + { + "size": "c4.4xlarge", + "vCPU": "16", + "ECU": "62", + "memoryGiB": "30", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.953" + } + } + ] + }, + { + "size": "c4.8xlarge", + "vCPU": "36", + "ECU": "132", + "memoryGiB": "60", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.906" + } + } + ] + }, + { + "size": "c3.large", + "vCPU": "2", + "ECU": "7", + "memoryGiB": "3.75", + "storageGB": "2 x 16 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.12" + } + } + ] + }, + { + "size": "c3.xlarge", + "vCPU": "4", + "ECU": "14", + "memoryGiB": "7.5", + "storageGB": "2 x 40 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.239" + } + } + ] + }, + { + "size": "c3.2xlarge", + "vCPU": "8", + "ECU": "28", + "memoryGiB": "15", + "storageGB": "2 x 80 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.478" + } + } + ] + }, + { + "size": "c3.4xlarge", + "vCPU": "16", + "ECU": "55", + "memoryGiB": "30", + "storageGB": "2 x 160 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.956" + } + } + ] + }, + { + "size": "c3.8xlarge", + "vCPU": "32", + "ECU": "108", + "memoryGiB": "60", + "storageGB": "2 x 320 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.912" + } + } + ] + } + ] + }, + { + "type": "gpuCurrentGen", + "sizes": [ + { + "size": "g2.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "15", + "storageGB": "60 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.702" + } + } + ] + }, + { + "size": "g2.8xlarge", + "vCPU": "32", + "ECU": "104", + "memoryGiB": "60", + "storageGB": "2 x 120 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.808" + } + } + ] + } + ] + }, + { + "type": "hiMemCurrentGen", + "sizes": [ + { + "size": "r3.large", + "vCPU": "2", + "ECU": "6.5", + "memoryGiB": "15", + "storageGB": "1 x 32 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.185" + } + } + ] + }, + { + "size": "r3.xlarge", + "vCPU": "4", + "ECU": "13", + "memoryGiB": "30.5", + "storageGB": "1 x 80 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.371" + } + } + ] + }, + { + "size": "r3.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "61", + "storageGB": "1 x 160 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.741" + } + } + ] + }, + { + "size": "r3.4xlarge", + "vCPU": "16", + "ECU": "52", + "memoryGiB": "122", + "storageGB": "1 x 320 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.482" + } + } + ] + }, + { + "size": "r3.8xlarge", + "vCPU": "32", + "ECU": "104", + "memoryGiB": "244", + "storageGB": "2 x 320 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.964" + } + } + ] + } + ] + }, + { + "type": "storageCurrentGen", + "sizes": [ + { + "size": "i2.xlarge", + "vCPU": "4", + "ECU": "14", + "memoryGiB": "30.5", + "storageGB": "1 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.938" + } + } + ] + }, + { + "size": "i2.2xlarge", + "vCPU": "8", + "ECU": "27", + "memoryGiB": "61", + "storageGB": "2 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.876" + } + } + ] + }, + { + "size": "i2.4xlarge", + "vCPU": "16", + "ECU": "53", + "memoryGiB": "122", + "storageGB": "4 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "3.751" + } + } + ] + }, + { + "size": "i2.8xlarge", + "vCPU": "32", + "ECU": "104", + "memoryGiB": "244", + "storageGB": "8 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "7.502" + } + } + ] + }, + { + "size": "d2.xlarge", + "vCPU": "4", + "ECU": "14", + "memoryGiB": "30.5", + "storageGB": "3 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.735" + } + } + ] + }, + { + "size": "d2.2xlarge", + "vCPU": "8", + "ECU": "28", + "memoryGiB": "61", + "storageGB": "6 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.47" + } + } + ] + }, + { + "size": "d2.4xlarge", + "vCPU": "16", + "ECU": "56", + "memoryGiB": "122", + "storageGB": "12 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.94" + } + } + ] + }, + { + "size": "d2.8xlarge", + "vCPU": "36", + "ECU": "116", + "memoryGiB": "244", + "storageGB": "24 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "5.88" + } + } + ] + } + ] + } + ] + }, + { + "region": "ap-southeast-1", + "instanceTypes": [ + { + "type": "generalCurrentGen", + "sizes": [ + { + "size": "t2.nano", + "vCPU": "1", + "ECU": "variable", + "memoryGiB": "0.5", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.01" + } + } + ] + }, + { + "size": "t2.micro", + "vCPU": "1", + "ECU": "variable", + "memoryGiB": "1", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.02" + } + } + ] + }, + { + "size": "t2.small", + "vCPU": "1", + "ECU": "variable", + "memoryGiB": "2", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.04" + } + } + ] + }, + { + "size": "t2.medium", + "vCPU": "2", + "ECU": "variable", + "memoryGiB": "4", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.08" + } + } + ] + }, + { + "size": "t2.large", + "vCPU": "2", + "ECU": "variable", + "memoryGiB": "8", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.16" + } + } + ] + }, + { + "size": "m4.large", + "vCPU": "2", + "ECU": "6.5", + "memoryGiB": "8", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.178" + } + } + ] + }, + { + "size": "m4.xlarge", + "vCPU": "4", + "ECU": "13", + "memoryGiB": "16", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.355" + } + } + ] + }, + { + "size": "m4.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "32", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.711" + } + } + ] + }, + { + "size": "m4.4xlarge", + "vCPU": "16", + "ECU": "53.5", + "memoryGiB": "64", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.421" + } + } + ] + }, + { + "size": "m4.10xlarge", + "vCPU": "40", + "ECU": "124.5", + "memoryGiB": "160", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "3.553" + } + } + ] + }, + { + "size": "m3.medium", + "vCPU": "1", + "ECU": "3", + "memoryGiB": "3.75", + "storageGB": "1 x 4 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.098" + } + } + ] + }, + { + "size": "m3.large", + "vCPU": "2", + "ECU": "6.5", + "memoryGiB": "7.5", + "storageGB": "1 x 32 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.196" + } + } + ] + }, + { + "size": "m3.xlarge", + "vCPU": "4", + "ECU": "13", + "memoryGiB": "15", + "storageGB": "2 x 40 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.392" + } + } + ] + }, + { + "size": "m3.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "30", + "storageGB": "2 x 80 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.784" + } + } + ] + } + ] + }, + { + "type": "computeCurrentGen", + "sizes": [ + { + "size": "c4.large", + "vCPU": "2", + "ECU": "8", + "memoryGiB": "3.75", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.144" + } + } + ] + }, + { + "size": "c4.xlarge", + "vCPU": "4", + "ECU": "16", + "memoryGiB": "7.5", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.289" + } + } + ] + }, + { + "size": "c4.2xlarge", + "vCPU": "8", + "ECU": "31", + "memoryGiB": "15", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.578" + } + } + ] + }, + { + "size": "c4.4xlarge", + "vCPU": "16", + "ECU": "62", + "memoryGiB": "30", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.155" + } + } + ] + }, + { + "size": "c4.8xlarge", + "vCPU": "36", + "ECU": "132", + "memoryGiB": "60", + "storageGB": "ebsonly", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.31" + } + } + ] + }, + { + "size": "c3.large", + "vCPU": "2", + "ECU": "7", + "memoryGiB": "3.75", + "storageGB": "2 x 16 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.132" + } + } + ] + }, + { + "size": "c3.xlarge", + "vCPU": "4", + "ECU": "14", + "memoryGiB": "7.5", + "storageGB": "2 x 40 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.265" + } + } + ] + }, + { + "size": "c3.2xlarge", + "vCPU": "8", + "ECU": "28", + "memoryGiB": "15", + "storageGB": "2 x 80 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.529" + } + } + ] + }, + { + "size": "c3.4xlarge", + "vCPU": "16", + "ECU": "55", + "memoryGiB": "30", + "storageGB": "2 x 160 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.058" + } + } + ] + }, + { + "size": "c3.8xlarge", + "vCPU": "32", + "ECU": "108", + "memoryGiB": "60", + "storageGB": "2 x 320 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.117" + } + } + ] + } + ] + }, + { + "type": "gpuCurrentGen", + "sizes": [ + { + "size": "g2.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "15", + "storageGB": "60 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1" + } + } + ] + }, + { + "size": "g2.8xlarge", + "vCPU": "32", + "ECU": "104", + "memoryGiB": "60", + "storageGB": "2 x 120 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "4" + } + } + ] + } + ] + }, + { + "type": "hiMemCurrentGen", + "sizes": [ + { + "size": "r3.large", + "vCPU": "2", + "ECU": "6.5", + "memoryGiB": "15", + "storageGB": "1 x 32 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.2" + } + } + ] + }, + { + "size": "r3.xlarge", + "vCPU": "4", + "ECU": "13", + "memoryGiB": "30.5", + "storageGB": "1 x 80 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.399" + } + } + ] + }, + { + "size": "r3.2xlarge", + "vCPU": "8", + "ECU": "26", + "memoryGiB": "61", + "storageGB": "1 x 160 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.798" + } + } + ] + }, + { + "size": "r3.4xlarge", + "vCPU": "16", + "ECU": "52", + "memoryGiB": "122", + "storageGB": "1 x 320 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.596" + } + } + ] + }, + { + "size": "r3.8xlarge", + "vCPU": "32", + "ECU": "104", + "memoryGiB": "244", + "storageGB": "2 x 320 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "3.192" + } + } + ] + } + ] + }, + { + "type": "storageCurrentGen", + "sizes": [ + { + "size": "i2.xlarge", + "vCPU": "4", + "ECU": "14", + "memoryGiB": "30.5", + "storageGB": "1 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.018" + } + } + ] + }, + { + "size": "i2.2xlarge", + "vCPU": "8", + "ECU": "27", + "memoryGiB": "61", + "storageGB": "2 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "2.035" + } + } + ] + }, + { + "size": "i2.4xlarge", + "vCPU": "16", + "ECU": "53", + "memoryGiB": "122", + "storageGB": "4 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "4.07" + } + } + ] + }, + { + "size": "i2.8xlarge", + "vCPU": "32", + "ECU": "104", + "memoryGiB": "244", + "storageGB": "8 x 800 SSD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "8.14" + } + } + ] + }, + { + "size": "d2.xlarge", + "vCPU": "4", + "ECU": "14", + "memoryGiB": "30.5", + "storageGB": "3 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "0.87" + } + } + ] + }, + { + "size": "d2.2xlarge", + "vCPU": "8", + "ECU": "28", + "memoryGiB": "61", + "storageGB": "6 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "1.74" + } + } + ] + }, + { + "size": "d2.4xlarge", + "vCPU": "16", + "ECU": "56", + "memoryGiB": "122", + "storageGB": "12 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "3.48" + } + } + ] + }, + { + "size": "d2.8xlarge", + "vCPU": "36", + "ECU": "116", + "memoryGiB": "244", + "storageGB": "24 x 2000 HDD", + "valueColumns": [ + { + "name": "linux", + "prices": { + "USD": "6.96" + } + } + ] + } + ] + } + ] + } + ] + } + } + } +} diff --git a/test/integration/provider_stories_test.rb b/test/integration/provider_stories_test.rb new file mode 100644 index 0000000..6e00535 --- /dev/null +++ b/test/integration/provider_stories_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +class ProviderStoriesTest < ActionDispatch::IntegrationTest + test 'listing providers' do + aws = create(:aws_provider) + + visit providers_path + assert page.has_content? 'Amazon' + # TODO: The provider price list does not show up at all using PhantomJS or Selenium Javascript driver + # assert page.has_content? 't2.nano' + end +end From 4791e5ed0445cb49db79db7c23f5fdccc33c4045 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sun, 20 Mar 2016 15:57:48 +0100 Subject: [PATCH 30/34] Add additional provider factories --- test/factories.rb | 22 +- test/fixtures/factories/provider-azure.json | 238 +++++ test/fixtures/factories/provider-google.json | 966 ++++++++++++++++++ test/fixtures/factories/provider-joyent.json | 809 +++++++++++++++ .../factories/provider-rackspace.json | 166 +++ test/integration/provider_stories_test.rb | 2 +- 6 files changed, 2201 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/factories/provider-azure.json create mode 100644 test/fixtures/factories/provider-google.json create mode 100644 test/fixtures/factories/provider-joyent.json create mode 100644 test/fixtures/factories/provider-rackspace.json diff --git a/test/factories.rb b/test/factories.rb index 413fe4b..7273907 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -139,10 +139,30 @@ factory :provider do sequence(:name) { |n| "Provider#{n}"} - factory :aws_provider do + factory :amazon_provider do name 'Amazon' more_attributes FactoryHelpers::hash_from_json('provider-amazon.json') end + + factory :azure_provider do + name 'Microsoft Azure' + more_attributes FactoryHelpers::hash_from_json('provider-azure.json') + end + + factory :google_provider do + name 'Google' + more_attributes FactoryHelpers::hash_from_json('provider-google.json') + end + + factory :joyent_provider do + name 'Joyent' + more_attributes FactoryHelpers::hash_from_json('provider-joyent.json') + end + + factory :rackspace_provider do + name 'Rackspace' + more_attributes FactoryHelpers::hash_from_json('provider-rackspace.json') + end end factory :resource do diff --git a/test/fixtures/factories/provider-azure.json b/test/fixtures/factories/provider-azure.json new file mode 100644 index 0000000..30578da --- /dev/null +++ b/test/fixtures/factories/provider-azure.json @@ -0,0 +1,238 @@ +{ + "pricelist": { + "compute": { + "D1v2": { + "type": "compute", + "cores": "1", + "mem_gb": "3.5", + "price_per_hour": 0.073 + }, + "D2v2": { + "type": "compute", + "cores": "2", + "mem_gb": "7", + "price_per_hour": 0.146 + }, + "D3v2": { + "type": "compute", + "cores": "4", + "mem_gb": "14", + "price_per_hour": 0.293 + }, + "D4v2": { + "type": "compute", + "cores": "8", + "mem_gb": "28", + "price_per_hour": 0.585 + }, + "D5v2": { + "type": "compute", + "cores": "16", + "mem_gb": "56", + "price_per_hour": 1.17 + }, + "D11v2": { + "type": "compute", + "cores": "2", + "mem_gb": "14", + "price_per_hour": 0.185 + }, + "D12v2": { + "type": "compute", + "cores": "4", + "mem_gb": "28", + "price_per_hour": 0.371 + }, + "D13v2": { + "type": "compute", + "cores": "8", + "mem_gb": "56", + "price_per_hour": 0.741 + }, + "D14v2": { + "type": "compute", + "cores": "16", + "mem_gb": "112", + "price_per_hour": 1.482 + }, + "A0": { + "type": "compute", + "cores": "1", + "mem_gb": "0.75", + "price_per_hour": 0.02 + }, + "A1": { + "type": "compute", + "cores": "1", + "mem_gb": "1.75", + "price_per_hour": 0.06 + }, + "A2": { + "type": "compute", + "cores": "2", + "mem_gb": "3.5", + "price_per_hour": 0.12 + }, + "A3": { + "type": "compute", + "cores": "4", + "mem_gb": "7", + "price_per_hour": 0.24 + }, + "A4": { + "type": "compute", + "cores": "8", + "mem_gb": "14", + "price_per_hour": 0.48 + }, + "A5": { + "type": "compute", + "cores": "2", + "mem_gb": "14", + "price_per_hour": 0.25 + }, + "A6": { + "type": "compute", + "cores": "4", + "mem_gb": "28", + "price_per_hour": 0.55 + }, + "A7": { + "type": "compute", + "cores": "8", + "mem_gb": "56", + "price_per_hour": 1.0 + }, + "D1": { + "type": "compute", + "cores": "1", + "mem_gb": "3.5", + "price_per_hour": 0.067 + }, + "D2": { + "type": "compute", + "cores": "2", + "mem_gb": "7", + "price_per_hour": 0.134 + }, + "D3": { + "type": "compute", + "cores": "4", + "mem_gb": "14", + "price_per_hour": 0.268 + }, + "D4": { + "type": "compute", + "cores": "8", + "mem_gb": "28", + "price_per_hour": 0.536 + }, + "D11": { + "type": "compute", + "cores": "2", + "mem_gb": "14", + "price_per_hour": 0.175 + }, + "D12": { + "type": "compute", + "cores": "4", + "mem_gb": "28", + "price_per_hour": 0.35 + }, + "D13": { + "type": "compute", + "cores": "8", + "mem_gb": "56", + "price_per_hour": 0.7 + }, + "D14": { + "type": "compute", + "cores": "16", + "mem_gb": "112", + "price_per_hour": 1.387 + }, + "G1": { + "type": "compute", + "cores": "2", + "mem_gb": "28", + "price_per_hour": 0.61 + }, + "G2": { + "type": "compute", + "cores": "4", + "mem_gb": "56", + "price_per_hour": 1.22 + }, + "G3": { + "type": "compute", + "cores": "8", + "mem_gb": "112", + "price_per_hour": 2.44 + }, + "G4": { + "type": "compute", + "cores": "16", + "mem_gb": "224", + "price_per_hour": 4.88 + }, + "G5": { + "type": "compute", + "cores": "32", + "mem_gb": "448", + "price_per_hour": 8.69 + }, + "A8": { + "type": "compute", + "cores": "8", + "mem_gb": "56", + "price_per_hour": 0.975 + }, + "A9": { + "type": "compute", + "cores": "16", + "mem_gb": "112", + "price_per_hour": 1.95 + }, + "A10": { + "type": "compute", + "cores": "8", + "mem_gb": "56", + "price_per_hour": 0.78 + }, + "A11": { + "type": "compute", + "cores": "16", + "mem_gb": "112", + "price_per_hour": 1.56 + } + }, + "storage": { + "LRS": { + "type": "storage", + "price_per_month_gb": "0.024" + }, + "ZRS": { + "type": "storage", + "price_per_month_gb": "0.03" + }, + "GRS": { + "type": "storage", + "price_per_month_gb": "0.048" + }, + "RA-GRS": { + "type": "storage", + "price_per_month_gb": "0.061" + } + } + }, + "sla": { + "storage": { + "uri": "https://azure.microsoft.com/en-us/support/legal/sla/storage/v1_0/", + "availability": "0.9999" + }, + "compute": { + "uri": "https://azure.microsoft.com/en-us/support/legal/sla/virtual-machines/v1_0/", + "availability": "0.9995" + } + } +} \ No newline at end of file diff --git a/test/fixtures/factories/provider-google.json b/test/fixtures/factories/provider-google.json new file mode 100644 index 0000000..f6ed1b5 --- /dev/null +++ b/test/fixtures/factories/provider-google.json @@ -0,0 +1,966 @@ +{ + "pricelist": { + "comment": "If you've gotten here by mistake, this is the JSON data used by our pricing calculator. It is helpful for developers. Go to https://cloud.google.com/products/calculator/ to get back to our web calculator.", + "version": "v1.6", + "updated": "16-October-2015", + "gcp_price_list": { + "sustained_use_base": 0.25, + "sustained_use_tiers": { + "0.25": 1.0, + "0.50": 0.8, + "0.75": 0.6, + "1.0": 0.4 + }, + "CP-COMPUTEENGINE-VMIMAGE-F1-MICRO": { + "us": 0.008, + "europe": 0.009, + "asia": 0.009, + "cores": "shared", + "memory": "0.6", + "gceu": "Shared CPU, not guaranteed", + "maxNumberOfPd": 4, + "maxPdSize": 3, + "ssd": [ + 0 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-G1-SMALL": { + "us": 0.027, + "europe": 0.03, + "asia": 0.03, + "cores": "shared", + "memory": "1.7", + "gceu": 1.38, + "maxNumberOfPd": 4, + "maxPdSize": 3, + "ssd": [ + 0 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-1": { + "us": 0.05, + "europe": 0.055, + "asia": 0.055, + "cores": "1", + "memory": "3.75", + "gceu": 2.75, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-2": { + "us": 0.1, + "europe": 0.11, + "asia": 0.11, + "cores": "2", + "memory": "7.5", + "gceu": 5.5, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-4": { + "us": 0.2, + "europe": 0.22, + "asia": 0.22, + "cores": "4", + "memory": "15", + "gceu": 11, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-8": { + "us": 0.4, + "europe": 0.44, + "asia": 0.44, + "cores": "8", + "memory": "30", + "gceu": 22, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-16": { + "us": 0.8, + "europe": 0.88, + "asia": 0.88, + "cores": "16", + "memory": "60", + "gceu": 44, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-32": { + "us": 1.6, + "europe": 1.76, + "asia": 1.76, + "cores": "32", + "memory": "120", + "gceu": 88, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-2": { + "us": 0.126, + "europe": 0.139, + "asia": 0.139, + "cores": "2", + "memory": "13", + "gceu": 5.5, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-4": { + "us": 0.252, + "europe": 0.278, + "asia": 0.278, + "cores": "4", + "memory": "26", + "gceu": 11, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-8": { + "us": 0.504, + "europe": 0.556, + "asia": 0.556, + "cores": "8", + "memory": "52", + "gceu": 22, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-16": { + "us": 1.008, + "europe": 1.112, + "asia": 1.112, + "cores": "16", + "memory": "104", + "gceu": 44, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-32": { + "us": 2.016, + "europe": 2.224, + "asia": 2.224, + "cores": "32", + "memory": "208", + "gceu": 88, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-2": { + "us": 0.076, + "europe": 0.084, + "asia": 0.084, + "cores": "2", + "memory": "1.8", + "gceu": 5.5, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-4": { + "us": 0.152, + "europe": 0.168, + "asia": 0.168, + "cores": "4", + "memory": "3.6", + "gceu": 11, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-8": { + "us": 0.304, + "europe": 0.336, + "asia": 0.336, + "cores": "8", + "memory": "7.2", + "gceu": 22, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-16": { + "us": 0.608, + "europe": 0.672, + "asia": 0.672, + "cores": "16", + "memory": "14.40", + "gceu": 44, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-32": { + "us": 1.216, + "europe": 1.344, + "asia": 1.344, + "cores": "32", + "memory": "28.80", + "gceu": 88, + "maxNumberOfPd": 16, + "maxPdSize": 10, + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-F1-MICRO-PREEMPTIBLE": { + "us": 0.005, + "europe": 0.005, + "asia": 0.005, + "cores": "shared", + "memory": "0.6", + "ssd": [ + 0 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-G1-SMALL-PREEMPTIBLE": { + "us": 0.01, + "europe": 0.01, + "asia": 0.01, + "cores": "shared", + "memory": "1.7", + "ssd": [ + 0 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-1-PREEMPTIBLE": { + "us": 0.015, + "europe": 0.0165, + "asia": 0.0165, + "cores": "1", + "memory": "3.75", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-2-PREEMPTIBLE": { + "us": 0.03, + "europe": 0.033, + "asia": 0.033, + "cores": "2", + "memory": "7.5", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-4-PREEMPTIBLE": { + "us": 0.06, + "europe": 0.066, + "asia": 0.066, + "cores": "4", + "memory": "15", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-8-PREEMPTIBLE": { + "us": 0.12, + "europe": 0.132, + "asia": 0.132, + "cores": "8", + "memory": "30", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-16-PREEMPTIBLE": { + "us": 0.24, + "europe": 0.264, + "asia": 0.264, + "cores": "16", + "memory": "60", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-32-PREEMPTIBLE": { + "us": 0.48, + "europe": 0.528, + "asia": 0.528, + "cores": "32", + "memory": "120", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-2-PREEMPTIBLE": { + "us": 0.035, + "europe": 0.0385, + "asia": 0.0385, + "cores": "2", + "memory": "13", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-4-PREEMPTIBLE": { + "us": 0.07, + "europe": 0.077, + "asia": 0.077, + "cores": "4", + "memory": "26", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-8-PREEMPTIBLE": { + "us": 0.14, + "europe": 0.154, + "asia": 0.154, + "cores": "8", + "memory": "52", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-16-PREEMPTIBLE": { + "us": 0.28, + "europe": 0.308, + "asia": 0.308, + "cores": "16", + "memory": "104", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHMEM-32-PREEMPTIBLE": { + "us": 0.56, + "europe": 0.616, + "asia": 0.616, + "cores": "32", + "memory": "208", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-2-PREEMPTIBLE": { + "us": 0.02, + "europe": 0.022, + "asia": 0.022, + "cores": "2", + "memory": "1.8", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-4-PREEMPTIBLE": { + "us": 0.04, + "europe": 0.044, + "asia": 0.044, + "cores": "4", + "memory": "3.6", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-8-PREEMPTIBLE": { + "us": 0.08, + "europe": 0.088, + "asia": 0.088, + "cores": "8", + "memory": "7.2", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-16-PREEMPTIBLE": { + "us": 0.16, + "europe": 0.176, + "asia": 0.176, + "cores": "16", + "memory": "14.40", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-VMIMAGE-N1-HIGHCPU-32-PREEMPTIBLE": { + "us": 0.32, + "europe": 0.352, + "asia": 0.352, + "cores": "32", + "memory": "28.80", + "ssd": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "CP-COMPUTEENGINE-LOCAL-SSD": { + "us": 0.0003 + }, + "CP-COMPUTEENGINE-OS": { + "win": { + "low": 0.02, + "high": 0.04, + "cores": "shared", + "percore": true + }, + "rhel": { + "low": 0.06, + "high": 0.13, + "cores": "4", + "percore": false + }, + "suse": { + "low": 0.02, + "high": 0.11, + "cores": "shared", + "percore": false + } + }, + "CP-COMPUTEENGINE-STORAGE-PD-CAPACITY": { + "us": 0.04 + }, + "CP-COMPUTEENGINE-STORAGE-PD-SSD": { + "us": 0.17 + }, + "CP-COMPUTEENGINE-PD-IO-REQUEST": { + "us": 0.0 + }, + "CP-COMPUTEENGINE-STORAGE-PD-SNAPSHOT": { + "us": 0.026 + }, + "CP-BIGSTORE-CLASS-A-REQUEST": { + "us": 0.01 + }, + "CP-BIGSTORE-CLASS-B-REQUEST": { + "us": 0.01 + }, + "CP-CLOUDSQL-PERUSE-D0": { + "us": 0.025 + }, + "CP-CLOUDSQL-PERUSE-D1": { + "us": 0.1 + }, + "CP-CLOUDSQL-PERUSE-D2": { + "us": 0.19 + }, + "CP-CLOUDSQL-PERUSE-D4": { + "us": 0.29 + }, + "CP-CLOUDSQL-PERUSE-D8": { + "us": 0.58 + }, + "CP-CLOUDSQL-PERUSE-D16": { + "us": 1.16 + }, + "CP-CLOUDSQL-PERUSE-D32": { + "us": 2.31 + }, + "CP-CLOUDSQL-PACKAGE-D0": { + "us": 0.36 + }, + "CP-CLOUDSQL-PACKAGE-D1": { + "us": 1.46 + }, + "CP-CLOUDSQL-PACKAGE-D2": { + "us": 2.93 + }, + "CP-CLOUDSQL-PACKAGE-D4": { + "us": 4.4 + }, + "CP-CLOUDSQL-PACKAGE-D8": { + "us": 8.78 + }, + "CP-CLOUDSQL-PACKAGE-D16": { + "us": 17.57 + }, + "CP-CLOUDSQL-PACKAGE-D32": { + "us": 35.13 + }, + "CP-CLOUDSQL-STORAGE": { + "us": 0.24 + }, + "CP-CLOUDSQL-TRAFFIC": { + "us": 0.12 + }, + "CP-CLOUDSQL-IO": { + "us": 0.1 + }, + "CP-BIGSTORE-STORAGE": { + "us": 0.026 + }, + "CP-BIGSTORE-STORAGE-DRA": { + "us": 0.02 + }, + "CP-NEARLINE-STORAGE": { + "us": 0.01 + }, + "CP-NEARLINE-RESTORE-SIZE": { + "us": 0.01 + }, + "FORWARDING_RULE_CHARGE_BASE": { + "us": 0.025, + "fixed": true + }, + "FORWARDING_RULE_CHARGE_EXTRA": { + "us": 0.01 + }, + "NETWORK_LOAD_BALANCED_INGRESS": { + "us": 0.008 + }, + "CP-COMPUTEENGINE-INTERNET-EGRESS-NA-NA": { + "tiers": { + "1024": 0.12, + "10240": 0.11, + "92160": 0.08 + } + }, + "CP-COMPUTEENGINE-INTERNET-EGRESS-APAC-APAC": { + "tiers": { + "1024": 0.12, + "10240": 0.11, + "92160": 0.08 + } + }, + "CP-COMPUTEENGINE-INTERNET-EGRESS-AU-AU": { + "tiers": { + "1024": 0.19, + "10240": 0.18, + "92160": 0.15 + } + }, + "CP-COMPUTEENGINE-INTERNET-EGRESS-CN-CN": { + "tiers": { + "1024": 0.23, + "10240": 0.22, + "92160": 0.2 + } + }, + "CP-COMPUTEENGINE-INTERCONNECT-US-US": { + "us": 0.04 + }, + "CP-COMPUTEENGINE-INTERCONNECT-EU-EU": { + "us": 0.05 + }, + "CP-COMPUTEENGINE-INTERCONNECT-APAC-APAC": { + "us": 0.06 + }, + "CP-COMPUTEENGINE-INTERNET-EGRESS-ZONE": { + "us": 0.01 + }, + "CP-COMPUTEENGINE-INTERNET-EGRESS-REGION": { + "us": 0.01 + }, + "CP-APP-ENGINE-INSTANCES": { + "us": 0.05, + "freequota": { + "quantity": 28 + } + }, + "CP-APP-ENGINE-OUTGOING-TRAFFIC": { + "us": 0.12, + "freequota": { + "quantity": 1 + } + }, + "CP-APP-ENGINE-CLOUD-STORAGE": { + "us": 0.026, + "freequota": { + "quantity": 5 + } + }, + "CP-APP-ENGINE-MEMCACHE": { + "us": 0.06 + }, + "CP-APP-ENGINE-SEARCH": { + "us": 5.0e-05, + "freequota": { + "quantity": 100 + } + }, + "CP-APP-ENGINE-INDEXING-DOCUMENTS": { + "us": 2.0, + "freequota": { + "quantity": 0.01 + } + }, + "CP-APP-ENGINE-DOCUMENT-STORAGE": { + "us": 0.18, + "freequota": { + "quantity": 0.25 + } + }, + "CP-APP-ENGINE-LOGS-API": { + "us": 0.12, + "freequota": { + "quantity": 0.1 + } + }, + "CP-APP-ENGINE-TASK-QUEUE": { + "us": 0.026, + "freequota": { + "quantity": 5 + } + }, + "CP-APP-ENGINE-LOGS-STORAGE": { + "us": 0.026, + "freequota": { + "quantity": 1 + } + }, + "CP-APP-ENGINE-SSL-VIRTUAL-IP": { + "us": 39 + }, + "CP-CLOUD-DATASTORE-INSTANCES": { + "us": 0.18, + "freequota": { + "quantity": 1 + } + }, + "CP-CLOUD-DATASTORE-WRITE-OP": { + "us": 6.0e-07, + "freequota": { + "quantity": 50000 + } + }, + "CP-CLOUD-DATASTORE-READ-OP": { + "us": 6.0e-07, + "freequota": { + "quantity": 50000 + } + }, + "CP-BIGQUERY-GENERAL": { + "storage": { + "us": 0.02 + }, + "interactiveQueries": { + "us": 5, + "freequota": { + "quantity": 1 + } + }, + "streamingInserts": { + "us": 5.0e-05 + } + }, + "CP-CLOUD-DNS-ZONES": { + "tiers": { + "25": 0.2, + "10000": 0.1, + "100000": 0.03 + } + }, + "CP-CLOUD-DNS-QUERIES": { + "tiers": { + "1000000000": 4.0e-07, + "10000000000": 2.0e-07 + } + }, + "CP-TRANSLATE-API-TRANSLATION": { + "us": 2.0e-05 + }, + "CP-TRANSLATE-API-DETECTION": { + "us": 2.0e-05 + }, + "CP-PREDICTION-PREDICTION": { + "tiers": { + "10000": 0, + "100000": 0.0005 + } + }, + "CP-PREDICTION-BULK-TRAINING": { + "us": 0.002 + }, + "CP-PREDICTION-STREAMING-TRAINING": { + "tiers": { + "10000": 0, + "100000": 5.0e-05 + } + }, + "CP-GENOMICS-STORAGE": { + "us": 0.022 + }, + "CP-GENOMICS-QUERIES": { + "us": 1.0 + }, + "CP-DATAFLOW-BATCH": { + "us": 0.01 + }, + "CP-DATAFLOW-STREAMING": { + "us": 0.015 + }, + "CP-BIGTABLE-NODES": { + "us": 0.65 + }, + "CP-BIGTABLE-SSD": { + "us": 0.17 + }, + "CP-BIGTABLE-HDD": { + "us": 0.026 + }, + "CP-PUB-SUB-OPERATIONS": { + "tiers": { + "250": 0.4, + "750": 0.2, + "1750": 0.1, + "100000": 0.05 + } + }, + "CP-COMPUTEENGINE-STATIC-IP-CHARGE": { + "us": 0.01 + }, + "CP-DATAPROC": { + "us": 0.01 + }, + "CP-CONTAINER-ENGINE-BASIC": { + "us": 0.0 + }, + "CP-CONTAINER-ENGINE-STANDARD": { + "us": 0.15 + }, + "CP-SUPPORT-BRONZE": { + "us": 0.0 + }, + "CP-SUPPORT-SILVER": { + "us": 150 + }, + "CP-SUPPORT-GOLD": { + "us": 400, + "schedule": { + "10000": 0.09, + "50000": 0.07, + "200000": 0.05, + "1000000000": 0.03 + } + }, + "CP-COMPUTEENGINE-CUSTOM-VM-CORE": { + "us": 0.03492, + "europe": 0.03841, + "asia": 0.03841 + }, + "CP-COMPUTEENGINE-CUSTOM-VM-RAM": { + "us": 0.00468, + "europe": 0.00515, + "asia": 0.00515 + }, + "CP-COMPUTEENGINE-CUSTOM-VM-CORE-PREEMPTIBLE": { + "us": 0.01002, + "europe": 0.01103, + "asia": 0.01103 + }, + "CP-COMPUTEENGINE-CUSTOM-VM-RAM-PREEMPTIBLE": { + "us": 0.00156, + "europe": 0.00172, + "asia": 0.00172 + }, + "CP-DB-F1-MICRO": { + "us": 0.015 + }, + "CP-DB-G1-SMALL": { + "us": 0.05 + }, + "CP-DB-N1-STANDARD-1": { + "us": 0.0965 + }, + "CP-DB-N1-STANDARD-2": { + "us": 0.193 + }, + "CP-DB-N1-STANDARD-4": { + "us": 0.386 + }, + "CP-DB-N1-STANDARD-8": { + "us": 0.772 + }, + "CP-DB-N1-STANDARD-16": { + "us": 1.5445 + }, + "CP-DB-N1-HIGHMEM-2": { + "us": 0.2515 + }, + "CP-DB-N1-HIGHMEM-4": { + "us": 0.503 + }, + "CP-DB-N1-HIGHMEM-8": { + "us": 1.006 + }, + "CP-DB-N1-HIGHMEM-16": { + "us": 2.012 + }, + "CP-CLOUDSQL-STORAGE-SSD": { + "us": 0.17 + }, + "CP-CLOUDSQL-BACKUP": { + "us": 0.08 + } + } + }, + "sla": { + "compute": { + "uri": "https://cloud.google.com/compute/sla", + "availability": "0.9995" + }, + "storage": { + "uri": "https://cloud.google.com/storage/sla", + "availability": "0.999" + } + } +} \ No newline at end of file diff --git a/test/fixtures/factories/provider-joyent.json b/test/fixtures/factories/provider-joyent.json new file mode 100644 index 0000000..57a91ba --- /dev/null +++ b/test/fixtures/factories/provider-joyent.json @@ -0,0 +1,809 @@ +{ + "pricelist": { + "compute": { + "Portfolio": [ + { + "API Name": "t4-standard-128M", + "Common Name": "Standard 128M", + "Disk GB": 3, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.0032, + "RAM GiB": 0.128, + "vCPUs": 0.0625 + }, + { + "API Name": "t4-standard-256M", + "Common Name": "Standard 256M", + "Disk GB": 6, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.0065, + "RAM GiB": 0.256, + "vCPUs": 0.125 + }, + { + "API Name": "t4-standard-512M", + "Common Name": "Standard 512M", + "Disk GB": 12, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.0131, + "RAM GiB": 0.512, + "vCPUs": 0.25 + }, + { + "API Name": "t4-standard-1G", + "Common Name": "Standard 1G", + "Disk GB": 25, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.0262, + "RAM GiB": 1, + "vCPUs": 0.5 + }, + { + "API Name": "t4-standard-2G", + "Common Name": "Standard 2G", + "Disk GB": 50, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.0525, + "RAM GiB": 2, + "vCPUs": 1 + }, + { + "API Name": "t4-standard-4G", + "Common Name": "Standard 4G", + "Disk GB": 100, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.105, + "RAM GiB": 4, + "vCPUs": 2 + }, + { + "API Name": "t4-standard-8G", + "Common Name": "Standard 8G", + "Disk GB": 200, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.21, + "RAM GiB": 8, + "vCPUs": 4 + }, + { + "API Name": "t4-standard-16G", + "Common Name": "Standard 16G", + "Disk GB": 400, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.42, + "RAM GiB": 16, + "vCPUs": 8 + }, + { + "API Name": "t4-standard-32G", + "Common Name": "Standard 32G", + "Disk GB": 800, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.84, + "RAM GiB": 32, + "vCPUs": 16 + }, + { + "API Name": "t4-standard-64G", + "Common Name": "Standard 64G", + "Disk GB": 1600, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 1.68, + "RAM GiB": 64, + "vCPUs": 32 + }, + { + "API Name": "g3-highmemory-17.125-smartos", + "Common Name": "High Memory 17.125", + "Disk GB": 420, + "Grey Out": "Yes", + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.409, + "RAM GiB": 17.13, + "vCPUs": 2 + }, + { + "API Name": "g3-highmemory-34.25-smartos", + "Common Name": "High Memory 34.25", + "Disk GB": 843, + "Grey Out": "Yes", + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.817, + "RAM GiB": 34.25, + "vCPUs": 4 + }, + { + "API Name": "g3-highmemory-68.375-smartos", + "Common Name": "High Memory 68.375", + "Disk GB": 1122, + "Grey Out": "Yes", + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 1.63, + "RAM GiB": 68.38, + "vCPUs": 8 + }, + { + "API Name": "g3-highmemory-144-smartos", + "Common Name": "High Memory 144", + "Disk GB": 2363, + "Grey Out": "Yes", + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 3.433, + "RAM GiB": 144, + "vCPUs": 18 + }, + { + "API Name": "g3-highmemory-256-smartos-cc", + "Common Name": "High Memory 256", + "Disk GB": 4200, + "Flags": "cc", + "Grey Out": "Yes", + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 6.102, + "RAM GiB": 256, + "vCPUs": 32 + }, + { + "API Name": "g3-highcpu-1.75-smartos", + "Common Name": "High CPU 1.75", + "Disk GB": 75, + "Grey Out": "Yes", + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.127, + "RAM GiB": 1.75, + "vCPUs": 2 + }, + { + "API Name": "g3-highcpu-7-smartos", + "Common Name": "High CPU 7", + "Disk GB": 263, + "Grey Out": "Yes", + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.508, + "RAM GiB": 7, + "vCPUs": 7 + }, + { + "API Name": "g3-highcpu-16-smartos", + "Common Name": "High CPU 16", + "Disk GB": 600, + "Grey Out": "Yes", + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 1.16, + "RAM GiB": 16, + "vCPUs": 16 + }, + { + "API Name": "g3-highcpu-24-smartos", + "Common Name": "High CPU 24", + "Disk GB": 900, + "Grey Out": "Yes", + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 1.739, + "RAM GiB": 24, + "vCPUs": 24 + }, + { + "API Name": "g3-highcpu-32-smartos-cc", + "Common Name": "High CPU 32", + "Disk GB": 1200, + "Flags": "cc", + "Grey Out": "Yes", + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 2.319, + "RAM GiB": 32, + "vCPUs": 32 + }, + { + "API Name": "g3-highio-15-smartos", + "Common Name": "High I/O 15", + "Disk GB": 360, + "Grey Out": "Yes", + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 0.76, + "RAM GiB": 15, + "vCPUs": 2 + }, + { + "API Name": "g3-highio-30-smartos", + "Common Name": "High I/O 30", + "Disk GB": 725, + "Grey Out": "Yes", + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 1.52, + "RAM GiB": 30, + "vCPUs": 4 + }, + { + "API Name": "g3-highio-60.5-smartos", + "Common Name": "High I/O 60.5", + "Disk GB": 1452, + "Grey Out": "Yes", + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 3.067, + "RAM GiB": 60.5, + "vCPUs": 8 + }, + { + "API Name": "g3-highio-128-smartos", + "Common Name": "High I/O 128", + "Disk GB": 3072, + "Grey Out": "Yes", + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 6.488, + "RAM GiB": 128, + "vCPUs": 16 + }, + { + "API Name": "g3-highio-256-smartos-cc", + "Common Name": "High I/O 256", + "Disk GB": 6144, + "Flags": "cc", + "Grey Out": "Yes", + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Docker Container", + "Price": 12.976, + "RAM GiB": 256, + "vCPUs": 32 + }, + { + "API Name": "t4-standard-128M", + "Common Name": "Standard 128M", + "Disk GB": 3, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.0032, + "RAM GiB": 0.128, + "vCPUs": 0.0625 + }, + { + "API Name": "t4-standard-256M", + "Common Name": "Standard 256M", + "Disk GB": 6, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.0065, + "RAM GiB": 0.256, + "vCPUs": 0.125 + }, + { + "API Name": "t4-standard-512M", + "Common Name": "Standard 512M", + "Disk GB": 12, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.0131, + "RAM GiB": 0.512, + "vCPUs": 0.25 + }, + { + "API Name": "t4-standard-1G", + "Common Name": "Standard 1G", + "Disk GB": 25, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.0262, + "RAM GiB": 1, + "vCPUs": 0.5 + }, + { + "API Name": "t4-standard-2G", + "Common Name": "Standard 2G", + "Disk GB": 50, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.0525, + "RAM GiB": 2, + "vCPUs": 1 + }, + { + "API Name": "t4-standard-4G", + "Common Name": "Standard 4G", + "Disk GB": 100, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.105, + "RAM GiB": 4, + "vCPUs": 2 + }, + { + "API Name": "t4-standard-8G", + "Common Name": "Standard 8G", + "Disk GB": 200, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.21, + "RAM GiB": 8, + "vCPUs": 4 + }, + { + "API Name": "t4-standard-16G", + "Common Name": "Standard 16G", + "Disk GB": 400, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.42, + "RAM GiB": 16, + "vCPUs": 8 + }, + { + "API Name": "t4-standard-32G", + "Common Name": "Standard 32G", + "Disk GB": 800, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.84, + "RAM GiB": 32, + "vCPUs": 16 + }, + { + "API Name": "t4-standard-64G", + "Common Name": "Standard 64G", + "Disk GB": 1600, + "Instance Family": "Triton – Standard", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 1.68, + "RAM GiB": 64, + "vCPUs": 32 + }, + { + "API Name": "g3-highmemory-17.125-smartos", + "Common Name": "High Memory 17.125", + "Disk GB": 420, + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.409, + "RAM GiB": 17.13, + "vCPUs": 2 + }, + { + "API Name": "g3-highmemory-34.25-smartos", + "Common Name": "High Memory 34.25", + "Disk GB": 843, + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.817, + "RAM GiB": 34.25, + "vCPUs": 4 + }, + { + "API Name": "g3-highmemory-68.375-smartos", + "Common Name": "High Memory 68.375", + "Disk GB": 1122, + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 1.63, + "RAM GiB": 68.38, + "vCPUs": 8 + }, + { + "API Name": "g3-highmemory-144-smartos", + "Common Name": "High Memory 144", + "Disk GB": 2363, + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 3.433, + "RAM GiB": 144, + "vCPUs": 18 + }, + { + "API Name": "g3-highmemory-256-smartos-cc", + "Common Name": "High Memory 256", + "Disk GB": 4200, + "Flags": "cc", + "Instance Family": "High Memory", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 6.102, + "RAM GiB": 256, + "vCPUs": 32 + }, + { + "API Name": "g3-highcpu-1.75-smartos", + "Common Name": "High CPU 1.75", + "Disk GB": 75, + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.127, + "RAM GiB": 1.75, + "vCPUs": 2 + }, + { + "API Name": "g3-highcpu-7-smartos", + "Common Name": "High CPU 7", + "Disk GB": 263, + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.508, + "RAM GiB": 7, + "vCPUs": 7 + }, + { + "API Name": "g3-highcpu-16-smartos", + "Common Name": "High CPU 16", + "Disk GB": 600, + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 1.16, + "RAM GiB": 16, + "vCPUs": 16 + }, + { + "API Name": "g3-highcpu-24-smartos", + "Common Name": "High CPU 24", + "Disk GB": 900, + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 1.739, + "RAM GiB": 24, + "vCPUs": 24 + }, + { + "API Name": "g3-highcpu-32-smartos-cc", + "Common Name": "High CPU 32", + "Disk GB": 1200, + "Flags": "cc", + "Instance Family": "High CPU", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 2.319, + "RAM GiB": 32, + "vCPUs": 32 + }, + { + "API Name": "g3-highio-15-smartos", + "Common Name": "High I/O 15", + "Disk GB": 360, + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 0.76, + "RAM GiB": 15, + "vCPUs": 2 + }, + { + "API Name": "g3-highio-30-smartos", + "Common Name": "High I/O 30", + "Disk GB": 725, + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 1.52, + "RAM GiB": 30, + "vCPUs": 4 + }, + { + "API Name": "g3-highio-60.5-smartos", + "Common Name": "High I/O 60.5", + "Disk GB": 1452, + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 3.067, + "RAM GiB": 60.5, + "vCPUs": 8 + }, + { + "API Name": "g3-highio-128-smartos", + "Common Name": "High I/O 128", + "Disk GB": 3072, + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 6.488, + "RAM GiB": 128, + "vCPUs": 16 + }, + { + "API Name": "g3-highio-256-smartos-cc", + "Common Name": "High I/O 256", + "Disk GB": 6144, + "Flags": "cc", + "Instance Family": "High I/O", + "Network": "10 Gbit/s", + "OS": "Infrastructure Container", + "Price": 12.976, + "RAM GiB": 256, + "vCPUs": 32 + }, + { + "API Name": "g3-standard-1-kvm", + "AWS Windows Price": 0.02, + "Common Name": "Standard 1", + "Disk GB": 33, + "Instance Family": "Standard", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 0.032, + "RAM GiB": 1, + "vCPUs": 0.25 + }, + { + "API Name": "g3-standard-1.75-kvm", + "AWS Windows Price": 0.091, + "Common Name": "Standard 1.75", + "Disk GB": 56, + "Instance Family": "Standard", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 0.056, + "RAM GiB": 1.75, + "vCPUs": 1 + }, + { + "API Name": "g3-standard-2-kvm", + "AWS Windows Price": 0.182, + "Common Name": "Standard 2", + "Disk GB": 66, + "Instance Family": "Standard", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 0.064, + "RAM GiB": 2, + "vCPUs": 1 + }, + { + "API Name": "g3-standard-3.75-kvm", + "AWS Windows Price": 0.364, + "Common Name": "Standard 3.75", + "Disk GB": 123, + "Instance Family": "Standard", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 0.12, + "RAM GiB": 3.75, + "vCPUs": 1 + }, + { + "API Name": "g3-standard-4-kvm", + "AWS Windows Price": 0.728, + "Common Name": "Standard 4", + "Disk GB": 131, + "Instance Family": "Standard", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 0.128, + "RAM GiB": 4, + "vCPUs": 1 + }, + { + "API Name": "g3-highmemory-17.125-kvm", + "AWS Windows Price": 0.51, + "Common Name": "High Memory 17.125", + "Disk GB": 420, + "Instance Family": "High Memory", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 0.409, + "RAM GiB": 17.13, + "vCPUs": 2 + }, + { + "API Name": "g3-highmemory-34.25-kvm", + "AWS Windows Price": 1.02, + "Common Name": "High Memory 34.25", + "Disk GB": 843, + "Instance Family": "High Memory", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 0.817, + "RAM GiB": 34.25, + "vCPUs": 4 + }, + { + "API Name": "g3-highmemory-68.375-kvm", + "AWS Windows Price": 2.04, + "Common Name": "High Memory 68.375", + "Disk GB": 1122, + "Instance Family": "High Memory", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 1.63, + "RAM GiB": 68.38, + "vCPUs": 8 + }, + { + "API Name": "g3-highmemory-144-kvm", + "Common Name": "High Memory 144", + "Disk GB": 2363, + "Instance Family": "High Memory", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 3.433, + "RAM GiB": 144, + "vCPUs": 18 + }, + { + "API Name": "g3-highmemory-256-kvm-cc", + "Common Name": "High Memory 256", + "Disk GB": 4200, + "Flags": "cc", + "Instance Family": "High Memory", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 6.102, + "RAM GiB": 256, + "vCPUs": 32 + }, + { + "API Name": "g3-highcpu-1.75-kvm", + "AWS Windows Price": 0.225, + "Common Name": "High CPU 1.75", + "Disk GB": 75, + "Instance Family": "High CPU", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 0.127, + "RAM GiB": 1.75, + "vCPUs": 2 + }, + { + "API Name": "g3-highcpu-7-kvm", + "AWS Windows Price": 0.9, + "Common Name": "High CPU 7", + "Disk GB": 263, + "Instance Family": "High CPU", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 0.508, + "RAM GiB": 7, + "vCPUs": 7 + }, + { + "API Name": "g3-highcpu-16-kvm", + "Common Name": "High CPU 16", + "Disk GB": 600, + "Instance Family": "High CPU", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 1.16, + "RAM GiB": 16, + "vCPUs": 16 + }, + { + "API Name": "g3-highcpu-24-kvm", + "Common Name": "High CPU 24", + "Disk GB": 900, + "Instance Family": "High CPU", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 1.739, + "RAM GiB": 24, + "vCPUs": 24 + }, + { + "API Name": "g3-highcpu-32-kvm-cc", + "Common Name": "High CPU 32", + "Disk GB": 1200, + "Flags": "cc", + "Instance Family": "High CPU", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 2.319, + "RAM GiB": 32, + "vCPUs": 32 + }, + { + "API Name": "g3-highio-60.5-kvm", + "AWS Windows Price": 3.58, + "Common Name": "High I/O 60.5", + "Disk GB": 1452, + "Instance Family": "High I/O", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 3.067, + "RAM GiB": 60.5, + "vCPUs": 8 + }, + { + "API Name": "g3-highio-128-kvm", + "Common Name": "High I/O 128", + "Disk GB": 3072, + "Instance Family": "High I/O", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 6.488, + "RAM GiB": 128, + "vCPUs": 16 + }, + { + "API Name": "g3-highio-256-kvm-cc", + "Common Name": "High I/O 256", + "Disk GB": 6144, + "Flags": "cc", + "Instance Family": "High I/O", + "Network": "1 Gbit/s", + "OS": "Hardware VM", + "Price": 12.976, + "RAM GiB": 256, + "vCPUs": 32 + } + ], + "Portfolio Description": { + "Created": "2015-12-08T09:18:44.090384", + "Notes": "JPC-1553", + "SchemaVersion": "1.0.0.0", + "Status": "Pre-Production", + "Type": "Joyent Compute Portfolio", + "Version": "1.0.0.0" + } + }, + "storage": { + "Per Individual Copy": { + "type": "storage", + "price_per_month_gb": "0.043" + }, + "Per 2 Copies (default)": { + "type": "storage", + "price_per_month_gb": "0.086" + } + } + } +} \ No newline at end of file diff --git a/test/fixtures/factories/provider-rackspace.json b/test/fixtures/factories/provider-rackspace.json new file mode 100644 index 0000000..ec0aa62 --- /dev/null +++ b/test/fixtures/factories/provider-rackspace.json @@ -0,0 +1,166 @@ +{ + "pricelist": { + "compute": { + "General1-1": { + "type": "compute", + "mem_gb": "1.0", + "cores": "1", + "price_per_hour": "0.037", + "price_per_month": "27.0", + "bandwidth_mbps": "200.0" + }, + "General1-2": { + "type": "compute", + "mem_gb": "2.0", + "cores": "2", + "price_per_hour": "0.074", + "price_per_month": "54.0", + "bandwidth_mbps": "400.0" + }, + "General1-4": { + "type": "compute", + "mem_gb": "4.0", + "cores": "4", + "price_per_hour": "0.148", + "price_per_month": "108.0", + "bandwidth_mbps": "800.0" + }, + "General1-8": { + "type": "compute", + "mem_gb": "8.0", + "cores": "8", + "price_per_hour": "0.296", + "price_per_month": "216.0", + "bandwidth_mbps": "1600.0" + }, + "Compute1-4": { + "type": "compute", + "mem_gb": "3.75", + "cores": "2", + "price_per_hour": "0.099", + "price_per_month": "72.0", + "bandwidth_mbps": "312.5" + }, + "Compute1-8": { + "type": "compute", + "mem_gb": "7.5", + "cores": "4", + "price_per_hour": "0.198", + "price_per_month": "145.0", + "bandwidth_mbps": "625.0" + }, + "Compute1-15": { + "type": "compute", + "mem_gb": "15.0", + "cores": "8", + "price_per_hour": "0.395", + "price_per_month": "289.0", + "bandwidth_mbps": "1250.0" + }, + "Compute1-30": { + "type": "compute", + "mem_gb": "30.0", + "cores": "16", + "price_per_hour": "0.79", + "price_per_month": "577.0", + "bandwidth_mbps": "2500.0" + }, + "Compute1-60": { + "type": "compute", + "mem_gb": "60.0", + "cores": "32", + "price_per_hour": "1.58", + "price_per_month": "1153.0", + "bandwidth_mbps": "5000.0" + }, + "I/O1-15": { + "type": "compute", + "mem_gb": "15.0", + "cores": "4", + "price_per_hour": "0.555", + "price_per_month": "405.0", + "bandwidth_mbps": "1250.0" + }, + "I/O1-30": { + "type": "compute", + "mem_gb": "30.0", + "cores": "8", + "price_per_hour": "1.11", + "price_per_month": "811.0", + "bandwidth_mbps": "2500.0" + }, + "I/O1-60": { + "type": "compute", + "mem_gb": "60.0", + "cores": "16", + "price_per_hour": "2.22", + "price_per_month": "1621.0", + "bandwidth_mbps": "5000.0" + }, + "I/O1-90": { + "type": "compute", + "mem_gb": "90.0", + "cores": "24", + "price_per_hour": "3.33", + "price_per_month": "2431.0", + "bandwidth_mbps": "7500.0" + }, + "I/O1-120": { + "type": "compute", + "mem_gb": "120.0", + "cores": "32", + "price_per_hour": "4.44", + "price_per_month": "3241.0", + "bandwidth_mbps": "10000.0" + }, + "Memory1-15": { + "type": "compute", + "mem_gb": "15.0", + "cores": "2", + "price_per_hour": "0.218", + "price_per_month": "159.0", + "bandwidth_mbps": "625.0" + }, + "Memory1-30": { + "type": "compute", + "mem_gb": "30.0", + "cores": "4", + "price_per_hour": "0.435", + "price_per_month": "318.0", + "bandwidth_mbps": "1250.0" + }, + "Memory1-60": { + "type": "compute", + "mem_gb": "60.0", + "cores": "8", + "price_per_hour": "0.87", + "price_per_month": "636.0", + "bandwidth_mbps": "2500.0" + }, + "Memory1-120": { + "type": "compute", + "mem_gb": "120.0", + "cores": "16", + "price_per_hour": "1.74", + "price_per_month": "1270.0", + "bandwidth_mbps": "5000.0" + }, + "Memory1-240": { + "type": "compute", + "mem_gb": "240.0", + "cores": "32", + "price_per_hour": "3.48", + "price_per_month": "2540.0", + "bandwidth_mbps": "10000.0" + } + }, + "storage": { + "type": "storage", + "price_per_hour_gb": "0.000137", + "price_per_month_gb": "0.1" + } + }, + "sla": { + "uri": "https://www.rackspace.com/information/legal/cloud/sla" + } +} \ No newline at end of file diff --git a/test/integration/provider_stories_test.rb b/test/integration/provider_stories_test.rb index 6e00535..41d1ecf 100644 --- a/test/integration/provider_stories_test.rb +++ b/test/integration/provider_stories_test.rb @@ -2,7 +2,7 @@ class ProviderStoriesTest < ActionDispatch::IntegrationTest test 'listing providers' do - aws = create(:aws_provider) + amazon = create(:amazon_provider) visit providers_path assert page.has_content? 'Amazon' From 788e53e2e8cbf2474b67cc1d2f8b8454df824f07 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sun, 20 Mar 2016 15:59:31 +0100 Subject: [PATCH 31/34] Fix wrong back-reference of components for rails app factory --- test/factories.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/factories.rb b/test/factories.rb index 7273907..7a26108 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -81,10 +81,14 @@ body 'A traditional wep application, let\'s say a web shop with * Rails as the application server * PostgreSQL as database' + association :blueprint, factory: :mt_blueprint after(:create) do |rails_cloud_app| - cc = [create(:concrete_component, :webrick, cloud_application: rails_cloud_app), - create(:concrete_component, :postgres, cloud_application: rails_cloud_app)] + # Ensure that the back-references are set correctly (i.e., components refer to the same blueprint) + app_component = rails_cloud_app.blueprint.components.where(name: build_stubbed(:app_component).name).first + db_component = rails_cloud_app.blueprint.components.where(name: build_stubbed(:db_component).name).first + cc = [create(:concrete_component, :webrick, component: app_component, cloud_application: rails_cloud_app), + create(:concrete_component, :postgres, component: db_component, cloud_application: rails_cloud_app)] rails_cloud_app.concrete_components = cc rails_cloud_app.save end From 84262555fdbcbe32827255d31e3d608fb7d14782 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sun, 20 Mar 2016 21:13:23 +0100 Subject: [PATCH 32/34] Add `reload_page` IT helper --- test/test_helper.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_helper.rb b/test/test_helper.rb index d336e89..e6fdcea 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -28,6 +28,10 @@ class ActionDispatch::IntegrationTest # when using `save_and_open_page` Capybara.asset_host = 'http://localhost:3000' + def reload_page page + page.evaluate_script('window.location.reload()') + end + # Saving a page to public (for debugging) allows to serve it authentical (with assets) via `rails s` def show_page file = capybara_file From 33c76b35fb4f1af30ee9706866b418f46974a501 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sun, 20 Mar 2016 21:17:07 +0100 Subject: [PATCH 33/34] Add 'find deployment recommendations' IT Do not use this test helper: `include ActiveJob::TestHelper` It allows to use `assert_enqueued_jobs 1` but the jobs won't be present in the database then (i.e., Delayed::Job.all always returns []) --- test/factories.rb | 65 +++++++++++++++++-- .../cloud_application_stories_test.rb | 27 ++++++++ 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/test/factories.rb b/test/factories.rb index 7a26108..554138c 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -84,12 +84,18 @@ association :blueprint, factory: :mt_blueprint after(:create) do |rails_cloud_app| - # Ensure that the back-references are set correctly (i.e., components refer to the same blueprint) + # Ensure that the back-references are set correctly (e.g., components refer to the same blueprint) app_component = rails_cloud_app.blueprint.components.where(name: build_stubbed(:app_component).name).first db_component = rails_cloud_app.blueprint.components.where(name: build_stubbed(:db_component).name).first - cc = [create(:concrete_component, :webrick, component: app_component, cloud_application: rails_cloud_app), - create(:concrete_component, :postgres, component: db_component, cloud_application: rails_cloud_app)] - rails_cloud_app.concrete_components = cc + webrick_cc = build(:concrete_component, :webrick, component: app_component, cloud_application: rails_cloud_app) + webrick_slo = create(:slo_set, :webrick, concrete_component: webrick_cc) + webrick_cc.slo_sets = [webrick_slo] + webrick_cc.save + postgres_cc = build(:concrete_component, :postgres, component: db_component, cloud_application: rails_cloud_app) + postgres_slo = create(:slo_set, :postgres, concrete_component: postgres_cc) + postgres_cc.slo_sets = [postgres_slo] + postgres_cc.save + rails_cloud_app.concrete_components = [webrick_cc, postgres_cc] rails_cloud_app.save end end @@ -129,9 +135,9 @@ concrete_component :webrick end - trait :sqlite do + trait :postgres do more_attributes '{"availability":{"$gte":"0.999"},"costs":{"$lte":"200","currency":"$","interval":"month"}}' - concrete_component :sqlite + concrete_component :postgres end trait :nginx do @@ -146,6 +152,15 @@ factory :amazon_provider do name 'Amazon' more_attributes FactoryHelpers::hash_from_json('provider-amazon.json') + after(:create) do |amazon_provider| + amazon_provider.resources = [ + create(:resource, :amazon_c1, provider: amazon_provider), + create(:resource, :amazon_c2, provider: amazon_provider), + create(:resource, :amazon_s1, provider: amazon_provider), + create(:resource, :amazon_s2, provider: amazon_provider), + ] + amazon_provider.save + end end factory :azure_provider do @@ -156,6 +171,14 @@ factory :google_provider do name 'Google' more_attributes FactoryHelpers::hash_from_json('provider-google.json') + after(:create) do |google_provider| + google_provider.resources = [ + create(:resource, :google_c1, provider: google_provider), + create(:resource, :google_c2, provider: google_provider), + create(:resource, :google_s1, provider: google_provider), + ] + google_provider.save + end end factory :joyent_provider do @@ -172,6 +195,36 @@ factory :resource do sequence(:name) { |n| "Resource#{n}"} resource_type 'compute' + + trait :amazon_c1 do + name 't2.nano' + more_attributes(JSON.parse '{"cores":"1","mem_gb":"0.5","price_per_hour":"0.0065"}') + end + trait :amazon_c2 do + name 't2.micro' + more_attributes(JSON.parse '{"cores":"1", "mem_gb":"1.0", "price_per_hour":"0.013"}') + end + trait :amazon_s1 do + name 'storage' + more_attributes(JSON.parse '{"price_per_gb":"0.0300"}') + end + trait :amazon_s2 do + name 'infrequentAccessStorage' + more_attributes(JSON.parse '{"price_per_gb":"0.0125"}') + end + + trait :google_c1 do + name 'f1-micro' + more_attributes(JSON.parse '{"price_per_hour":0.009,"price_per_month":"4.6872","cores":"shared","mem_gb":"0.6"}') + end + trait :google_c2 do + name 'g1-small' + more_attributes(JSON.parse '{"price_per_hour":0.03,"price_per_month":"15.624","cores":"shared","mem_gb":"1.7"}') + end + trait :google_s1 do + name 'CP-BIGSTORE-STORAGE' + more_attributes(JSON.parse '{"price_per_month_gb":0.026}') + end end factory :deployment_recommendation do diff --git a/test/integration/cloud_application_stories_test.rb b/test/integration/cloud_application_stories_test.rb index 48f1ebf..65feaf0 100644 --- a/test/integration/cloud_application_stories_test.rb +++ b/test/integration/cloud_application_stories_test.rb @@ -22,4 +22,31 @@ class CloudApplicationStoriesTest < ActionDispatch::IntegrationTest assert page.has_content? webrick.name assert page.has_content? postgres.name end + + test 'find deployment recommendations' do + amazon = create(:amazon_provider) + google = create(:google_provider) + cloud_app = create(:rails_cloud_application) + + visit cloud_application_path cloud_app + assert_equal 0, Delayed::Job.count + click_link 'Find optimal deployments' + page.accept_alert + + assert_equal 1, Delayed::Job.count + assert_equal 0, ApplicationDeploymentRecommendation.count + Delayed::Job.first.invoke_job # NOTE: does not remove it. Would require `job.destroy` + assert_equal 2, ApplicationDeploymentRecommendation.count + reload_page page + + assert page.has_content? 'Deployment Costs:' + assert page.has_content? amazon.name + assert page.has_content? google.name + # TODO: Activate when deployment recommendations are fixed + # assert page.has_no_content? '$0.00', 'Deployment costs must be > 0' + + click_link amazon.name + assert page.has_content? cloud_app.concrete_components.first.name + assert page.has_content? cloud_app.concrete_components.last.name + end end From 696aefba344f87d4378f1de3b688341e46363bc3 Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Sun, 20 Mar 2016 21:32:00 +0100 Subject: [PATCH 34/34] Complete 'listing providers' IT Fix `resource_type` for storage resource factories The reason why no resources were displayed was because no resources were attached to the provider. The more_attributes field of provider had no effect. --- test/factories.rb | 3 +++ test/integration/provider_stories_test.rb | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/test/factories.rb b/test/factories.rb index 554138c..543ae5e 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -207,10 +207,12 @@ trait :amazon_s1 do name 'storage' more_attributes(JSON.parse '{"price_per_gb":"0.0300"}') + resource_type 'storage' end trait :amazon_s2 do name 'infrequentAccessStorage' more_attributes(JSON.parse '{"price_per_gb":"0.0125"}') + resource_type 'storage' end trait :google_c1 do @@ -224,6 +226,7 @@ trait :google_s1 do name 'CP-BIGSTORE-STORAGE' more_attributes(JSON.parse '{"price_per_month_gb":0.026}') + resource_type 'storage' end end diff --git a/test/integration/provider_stories_test.rb b/test/integration/provider_stories_test.rb index 41d1ecf..0778df3 100644 --- a/test/integration/provider_stories_test.rb +++ b/test/integration/provider_stories_test.rb @@ -3,10 +3,15 @@ class ProviderStoriesTest < ActionDispatch::IntegrationTest test 'listing providers' do amazon = create(:amazon_provider) + google = create(:google_provider) visit providers_path - assert page.has_content? 'Amazon' - # TODO: The provider price list does not show up at all using PhantomJS or Selenium Javascript driver - # assert page.has_content? 't2.nano' + assert page.has_content? amazon.name + assert page.has_content? 't2.nano' + assert page.has_content? 't2.micro' + assert page.has_no_content?('storage'), 'Only show compute resources' + + assert page.has_content? google.name + assert page.has_content? 'f1-micro' end end