From 5b3922e3ec0222215e41bde1d77a205e40ac2dda Mon Sep 17 00:00:00 2001 From: Nick Maludy Date: Mon, 24 Feb 2020 12:18:27 -0500 Subject: [PATCH 1/4] Initial work on spec testing for plans --- .fixtures.yml | 4 +-- .sync.yml | 15 ++++++++--- Gemfile | 4 +++ spec/plans/puppet_facts_spec.rb | 45 +++++++++++++++++++++++++++++++++ spec/spec_helper.rb | 7 +++++ 5 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 spec/plans/puppet_facts_spec.rb diff --git a/.fixtures.yml b/.fixtures.yml index 2296adb..105c3ed 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -2,5 +2,5 @@ # See https://github.com/puppetlabs/puppetlabs_spec_helper#using-fixtures for details --- fixtures: - forge_modules: -# stdlib: "puppetlabs/stdlib" + symlinks: + patching: "#{source_dir}" diff --git a/.sync.yml b/.sync.yml index 8da36be..cce9e1a 100644 --- a/.sync.yml +++ b/.sync.yml @@ -18,6 +18,9 @@ appveyor.yml: Gemfile: required: ':development': + - gem: 'rake' + - gem: 'bolt' + version: '>= 1.48.0' - gem: 'puppet-lint-absolute_template_path' version: '>= 1.0.1' - gem: 'puppet-lint-alias-check' @@ -40,15 +43,19 @@ Gemfile: version: '>= 0.3.0' - gem: 'puppet-lint-version_comparison-check' version: '>= 0.2.1' + - gem: 'puppetlabs_spec_helper' - gem: 'r10k' version: '>= 3.0.0' + - gem: 'rspec-puppet-facts' # cri is needed by r10k, but due to a bug in the cri gem v2.15.7 it breaks r10k # see: https://github.com/puppetlabs/r10k/issues/930 - gem: 'cri' version: '2.15.6' - gem: 'yaml-lint' version: '>= 0.0.10' -# Rakefile: -# extras: -# - "# exclude plans because puppet-syntax doesn't support them yet: https://github.com/voxpupuli/puppet-syntax/issues/95" -# - 'PuppetSyntax.exclude_paths = ["plans/**/*", "vendor/**/*"]' +spec/spec_helper.rb: + mock_with: ':rspec' + spec_overrides: + - "require 'bolt_spec/plans'" + - 'BoltSpec::Plans.init' + - 'include BoltSpec::Plans' diff --git a/Gemfile b/Gemfile index a661681..79b9968 100644 --- a/Gemfile +++ b/Gemfile @@ -28,6 +28,8 @@ group :development do gem "puppet-module-posix-dev-r#{minor_version}", '~> 0.4', require: false, platforms: [:ruby] gem "puppet-module-win-default-r#{minor_version}", '~> 0.4', require: false, platforms: [:mswin, :mingw, :x64_mingw] gem "puppet-module-win-dev-r#{minor_version}", '~> 0.4', require: false, platforms: [:mswin, :mingw, :x64_mingw] + gem "rake", require: false + gem "bolt", '>= 1.48.0', require: false gem "puppet-lint-absolute_template_path", '>= 1.0.1', require: false gem "puppet-lint-alias-check", '>= 0.1.1', require: false gem "puppet-lint-classes_and_types_beginning_with_digits-check", '>= 0.1.2', require: false @@ -39,7 +41,9 @@ group :development do gem "puppet-lint-trailing_comma-check", '>= 0.3.2', require: false gem "puppet-lint-unquoted_string-check", '>= 0.3.0', require: false gem "puppet-lint-version_comparison-check", '>= 0.2.1', require: false + gem "puppetlabs_spec_helper", require: false gem "r10k", '>= 3.0.0', require: false + gem "rspec-puppet-facts", require: false gem "cri", '2.15.6', require: false gem "yaml-lint", '>= 0.0.10', require: false end diff --git a/spec/plans/puppet_facts_spec.rb b/spec/plans/puppet_facts_spec.rb new file mode 100644 index 0000000..4441147 --- /dev/null +++ b/spec/plans/puppet_facts_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'patching::puppet_facts' do + def modulepath + File.join(__dir__, '../fixtures/modules') + end + + let(:targets_name) { ['foo', 'bar'] } + let(:targets) { targets_name.map { |t| Bolt::Target.new(t) } } + let(:targets_obj) do + h = {} + targets_name.each { |t| h[t] = Bolt::Target.new(t) } + h + end + let(:plan_name) { 'patching::puppet_facts' } + + def results(values) + Bolt::ResultSet.new( + values.map { |t, v| Bolt::Result.new(targets_obj[t], value: v) }, + ) + end + + context 'with nodes passed' do + it 'returns a default value' do + expect_task('patching::puppet_facts').with_targets(targets).return_for_targets( + 'foo' => { 'values' => { 'fact1' => 1 } }, + 'bar' => { 'values' => { 'fact2' => 2 } }, + ) + expect(inventory).to receive(:add_facts) + .with(targets_obj['foo'], 'fact1' => 1) + .and_return('fact1' => 1) + expect(inventory).to receive(:add_facts) + .with(targets_obj['bar'], 'fact2' => 2) + .and_return('fact2' => 2) + + result = run_plan(plan_name, 'nodes' => targets_name) + expect(result).to be_ok + expect(result.value.class).to eq(Bolt::ResultSet) + expect(result.value).to eq(results('foo' => { 'values' => { 'fact1' => 1 } }, + 'bar' => { 'values' => { 'fact2' => 2 } })) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 35a1408..421dfd9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,9 @@ # frozen_string_literal: true +RSpec.configure do |c| + c.mock_with :rspec +end + require 'puppetlabs_spec_helper/module_spec_helper' require 'rspec-puppet-facts' @@ -54,3 +58,6 @@ def ensure_module_defined(module_name) end # 'spec_overrides' from sync.yml will appear below this line +require 'bolt_spec/plans' +BoltSpec::Plans.init +include BoltSpec::Plans From bd48feae74f014ab972aba14d1c8bba2aa2fb9dc Mon Sep 17 00:00:00 2001 From: Nick Maludy Date: Mon, 24 Feb 2020 12:24:22 -0500 Subject: [PATCH 2/4] Remove puppet 5.x testing for Bolt --- .sync.yml | 6 ++++++ .travis.yml | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.sync.yml b/.sync.yml index cce9e1a..f168f82 100644 --- a/.sync.yml +++ b/.sync.yml @@ -12,6 +12,12 @@ .gitlab-ci.yml: # we don't use GitLab unmanaged: true +.travis.yml: + # removed Puppet 5 checks here + remove_includes: + - env: PUPPET_GEM_VERSION="~> 5.0" CHECK=parallel_spec + rvm: 2.4.5 + stage: spec appveyor.yml: # we don't use Appveyor unmanaged: true diff --git a/.travis.yml b/.travis.yml index cc78f7d..3274504 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,10 +29,6 @@ matrix: - env: CHECK="check:symlinks check:git_ignore check:dot_underscore check:test_file rubocop syntax lint metadata_lint" stage: static - - - env: PUPPET_GEM_VERSION="~> 5.0" CHECK=parallel_spec - rvm: 2.4.5 - stage: spec - env: PUPPET_GEM_VERSION="~> 6.0" CHECK=parallel_spec rvm: 2.5.3 From 9e367140a55d57e2f22d7f5932204e9d83878df2 Mon Sep 17 00:00:00 2001 From: Nick Maludy Date: Mon, 24 Feb 2020 13:00:41 -0500 Subject: [PATCH 3/4] Adding more spec tests to plans --- .fixtures.yml | 3 +++ .sync.yml | 10 ++++++++++ spec/plans/available_updates_spec.rb | 30 ++++++++++++++++++++++++++++ spec/plans/check_online_spec.rb | 23 +++++++++++++++++++++ spec/plans/puppet_facts_spec.rb | 26 ++++++------------------ spec/spec_helper.rb | 9 +++++++++ spec/spec_helper_local.rb | 30 ++++++++++++++++++++++++++++ 7 files changed, 111 insertions(+), 20 deletions(-) create mode 100644 spec/plans/available_updates_spec.rb create mode 100644 spec/plans/check_online_spec.rb create mode 100644 spec/spec_helper_local.rb diff --git a/.fixtures.yml b/.fixtures.yml index 105c3ed..c333bb1 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -2,5 +2,8 @@ # See https://github.com/puppetlabs/puppetlabs_spec_helper#using-fixtures for details --- fixtures: + forge_modules: + puppet_agent: 'puppetlabs/puppet_agent' + stdlib: 'puppetlabs/stdlib' symlinks: patching: "#{source_dir}" diff --git a/.sync.yml b/.sync.yml index f168f82..2f7290a 100644 --- a/.sync.yml +++ b/.sync.yml @@ -62,6 +62,16 @@ Gemfile: spec/spec_helper.rb: mock_with: ':rspec' spec_overrides: + - '# setup for Bolt spec tests' - "require 'bolt_spec/plans'" - 'BoltSpec::Plans.init' - 'include BoltSpec::Plans' + - '' + - '# setup the proper modulepath for Bolt' + - 'base_dir = File.dirname(File.expand_path(__FILE__))' + - 'RSpec.configure do |c|' + - ' # should we just use rspec_puppet' + - ' c.add_setting :module_path' + - " c.module_path = File.join(base_dir, 'fixtures', 'modules')" + - 'end' + diff --git a/spec/plans/available_updates_spec.rb b/spec/plans/available_updates_spec.rb new file mode 100644 index 0000000..3ff7c1c --- /dev/null +++ b/spec/plans/available_updates_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'patching::available_updates' do + let(:targets_names) { ['foo', 'bar'] } + let(:targets) { bolt_targets_arr(targets_names) } + let(:targets_obj) { bolt_targets_obj(targets_names) } + let(:plan_name) { 'patching::available_updates' } + + context 'with nodes passed' do + it 'returns a default value' do + expect_task('patching::available_updates') + .with_targets(targets) + .with_params('_noop' => false) + .return_for_targets( + 'foo' => { 'values' => { 'updates' => ['a'] } }, + 'bar' => { 'values' => { 'updates' => ['b'] } }, + ) + + result = run_plan(plan_name, + 'nodes' => targets_names, + 'format' => 'none') + expect(result).to be_ok + expect(result.value.class).to eq(Bolt::ResultSet) + expect(result.value).to eq(bolt_results('foo' => { 'values' => { 'updates' => ['a'] } }, + 'bar' => { 'values' => { 'updates' => ['b'] } })) + end + end +end diff --git a/spec/plans/check_online_spec.rb b/spec/plans/check_online_spec.rb new file mode 100644 index 0000000..9fc9192 --- /dev/null +++ b/spec/plans/check_online_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'patching::check_online' do + let(:targets_names) { ['foo', 'bar'] } + let(:targets) { bolt_targets_arr(targets_names) } + let(:targets_obj) { bolt_targets_obj(targets_names) } + let(:plan_name) { 'patching::check_online' } + + context 'with nodes passed' do + it 'returns a default value' do + expect_task('puppet_agent::version') + .with_targets(targets) + .with_params('_catch_errors' => true) + expect_out_message.with_params('All nodes succeeded!') + + result = run_plan(plan_name, 'nodes' => targets_names) + expect(result).to be_ok + expect(result.value).to be_nil + end + end +end diff --git a/spec/plans/puppet_facts_spec.rb b/spec/plans/puppet_facts_spec.rb index 4441147..65cc540 100644 --- a/spec/plans/puppet_facts_spec.rb +++ b/spec/plans/puppet_facts_spec.rb @@ -3,25 +3,11 @@ require 'spec_helper' describe 'patching::puppet_facts' do - def modulepath - File.join(__dir__, '../fixtures/modules') - end - - let(:targets_name) { ['foo', 'bar'] } - let(:targets) { targets_name.map { |t| Bolt::Target.new(t) } } - let(:targets_obj) do - h = {} - targets_name.each { |t| h[t] = Bolt::Target.new(t) } - h - end + let(:targets_names) { ['foo', 'bar'] } + let(:targets) { bolt_targets_arr(targets_names) } + let(:targets_obj) { bolt_targets_obj(targets_names) } let(:plan_name) { 'patching::puppet_facts' } - def results(values) - Bolt::ResultSet.new( - values.map { |t, v| Bolt::Result.new(targets_obj[t], value: v) }, - ) - end - context 'with nodes passed' do it 'returns a default value' do expect_task('patching::puppet_facts').with_targets(targets).return_for_targets( @@ -35,11 +21,11 @@ def results(values) .with(targets_obj['bar'], 'fact2' => 2) .and_return('fact2' => 2) - result = run_plan(plan_name, 'nodes' => targets_name) + result = run_plan(plan_name, 'nodes' => targets_names) expect(result).to be_ok expect(result.value.class).to eq(Bolt::ResultSet) - expect(result.value).to eq(results('foo' => { 'values' => { 'fact1' => 1 } }, - 'bar' => { 'values' => { 'fact2' => 2 } })) + expect(result.value).to eq(bolt_results('foo' => { 'values' => { 'fact1' => 1 } }, + 'bar' => { 'values' => { 'fact2' => 2 } })) end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 421dfd9..9d5ab09 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -58,6 +58,15 @@ def ensure_module_defined(module_name) end # 'spec_overrides' from sync.yml will appear below this line +# setup for Bolt spec tests require 'bolt_spec/plans' BoltSpec::Plans.init include BoltSpec::Plans + +# setup the proper modulepath for Bolt +base_dir = File.dirname(File.expand_path(__FILE__)) +RSpec.configure do |c| + # should we just use rspec_puppet + c.add_setting :module_path + c.module_path = File.join(base_dir, 'fixtures', 'modules') +end diff --git a/spec/spec_helper_local.rb b/spec/spec_helper_local.rb new file mode 100644 index 0000000..345d36b --- /dev/null +++ b/spec/spec_helper_local.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' + +# bolt_results({ +# 'foo' => { 'values' => { 'fact1' => 1 } }, +# 'bar' => { 'values' => { 'fact2' => 2 } })) +# +# - This accepts a Hash +# - Each key is the name of a target +# - Each value is a hash with a key 'values'. The 'values' key then contains +# a hash of the result data for that target. +def bolt_results(targets_values) + Bolt::ResultSet.new( + targets_values.map { |t, v| Bolt::Result.new(targets_obj[t], value: v) }, + ) +end + +# Converts an array of names into an array of Bolt::Target objects +def bolt_targets_arr(target_names_a) + target_names_a.map { |t| Bolt::Target.new(t) } +end + +# Converts an array of names into a hash where the keys are target names +# and the values are Bolt::Target objects +def bolt_targets_obj(target_names_a) + h = {} + target_names_a.each { |t| h[t] = Bolt::Target.new(t) } + h +end From 2a8596079d4b416287f40047f0236edbec3af792 Mon Sep 17 00:00:00 2001 From: Nick Maludy Date: Tue, 25 Feb 2020 10:49:40 -0500 Subject: [PATCH 4/4] Working on spec tests for sub plans --- .fixtures.yml | 1 + plans/check_puppet.pp | 2 +- spec/plans/check_puppet_spec.rb | 31 +++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 spec/plans/check_puppet_spec.rb diff --git a/.fixtures.yml b/.fixtures.yml index c333bb1..4db6cdb 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -3,6 +3,7 @@ --- fixtures: forge_modules: + facts: 'puppetlabs/facts' puppet_agent: 'puppetlabs/puppet_agent' stdlib: 'puppetlabs/stdlib' symlinks: diff --git a/plans/check_puppet.pp b/plans/check_puppet.pp index 73401cf..79331bf 100644 --- a/plans/check_puppet.pp +++ b/plans/check_puppet.pp @@ -80,7 +80,7 @@ if !$targets_no_puppet.empty() { # run `facter` if it's available otherwise get basic facts run_plan('facts', - nodes => $targets_no_puppet) + targets => $targets_no_puppet) } return({ diff --git a/spec/plans/check_puppet_spec.rb b/spec/plans/check_puppet_spec.rb new file mode 100644 index 0000000..89b8c3b --- /dev/null +++ b/spec/plans/check_puppet_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'patching::check_puppet' do + let(:targets_names) { ['foo', 'bar'] } + let(:targets) { bolt_targets_arr(targets_names) } + let(:targets_obj) { bolt_targets_obj(targets_names) } + let(:plan_name) { 'patching::check_puppet' } + + context 'with nodes passed' do + it 'returns a default value' do + expect_task('puppet_agent::version') + .with_targets(targets) + .with_params('_catch_errors' => false) + .return_for_targets( + 'foo' => { 'values' => { 'version' => '6.5.4' } }, + 'bar' => { 'values' => { 'version' => :undef } }, + ) + + # expect_plan('patching::puppet_facts') + # .with_params('nodes' => [targets_obj['foo']]) + expect_plan('facts') + .with_params('targets' => [targets_obj['foo'], targets_obj['bar']]) + + result = run_plan(plan_name, 'nodes' => targets_names) + puts "result = #{result}" + expect(result).to be_ok + end + end +end