lib/bolt_spec/plans.rb in bolt-1.3.0 vs lib/bolt_spec/plans.rb in bolt-1.4.0
- old
+ new
@@ -36,11 +36,10 @@
# an otherwise empty bolt config and inventory. To create your own values for
# these override the modulepath, config, or inventory methods.
#
#
# TODO:
-# - allow stubbing for commands, scripts and file uploads
# - Allow description based stub matching
# - Better testing of plan errors
# - Better error collection around call counts. Show what stubs exists and more than a single failure
# - Allow stubbing with a block(at the double level? As a matched stub?)
# - package code so that it can be used for testing modules outside of this repo
@@ -50,10 +49,40 @@
# - allow stubbing for subplans
# - validate call expectations at the end of the example instead of in run_plan
# - resultset matchers to help testing canary like plans?
# - inventory matchers to help testing plans that change inventory
#
+# Stubs:
+# - allow_command(cmd), expect_command(cmd): expect the exact command
+# - allow_script(script), expect_script(script): expect the script as <module>/path/to/file
+# - allow_task(task), expect_task(task): expect the named task
+# - allow_upload(file), expect_upload(file): expect the identified source file
+# - allow_apply_prep: allows `apply_prep` to be invoked in the plan but does not allow modifiers
+# - allow_apply: allows `apply` to be invoked in the plan but does not allow modifiers
+#
+# Stub modifiers:
+# - be_called_times(n): if allowed, fail if the action is called more than 'n' times
+# if expected, fail unless the action is called 'n' times
+# - not_be_called: fail if the action is called
+# - with_targets(targets): target or list of targets that you expect to be passed to the action
+# - with_params(params): list of params and metaparams (or options) that you expect to be passed to the action.
+# Corresponds to the action's last argument.
+# - with_destination(dest): for upload_file, the expected destination path
+# - always_return(value): return a Bolt::ResultSet of Bolt::Result objects with the specified value Hash
+# command and script: only accept 'stdout' and 'stderr' keys
+# upload: does not support this modifier
+# - return_for_targets(targets_to_values): return a Bolt::ResultSet of Bolt::Result objects from the Hash mapping
+# targets to their value Hashes
+# command and script: only accept 'stdout' and 'stderr' keys
+# upload: does not support this modifier
+# - return(&block): invoke the block to construct a Bolt::ResultSet. The blocks parameters differ based on action
+# command: `{ |targets:, command:, params:| ... }`
+# script: `{ |targets:, script:, params:| ... }`
+# task: `{ |targets:, task:, params:| ... }`
+# upload: `{ |targets:, source:, destination:, params:| ... }`
+# - error_with(err): return a failing Bolt::ResultSet, with Bolt::Result objects with the identified err hash
+#
# Example:
# describe "my_plan" do
# it 'should return' do
# allow_task('my_task').always_return({'result_key' => 10})
# expect(run_plan('my_plan', { 'param1' => 10 })).to be
@@ -85,12 +114,20 @@
# expect_task('my_task').return_for_targets({
# 'node1' => {'result_key' => 20},
# 'node2' => {'result_key' => 6} })
# expect(run_plan('my_plan', { 'param1' => 10 })).to eq(13)
# end
+#
+# it 'should construct a custom return value' do
+# expect_task('my_task').return do |targets:, task:, params:|
+# Bolt::ResultSet.new(targets.map { |targ| Bolt::Result.new(targ, {'result_key' => 10'})})
+# end
+# expect(run_plan('my_plan', { 'param1' => 10 })).to eq(10)
+# end
# end
#
+# See spec/bolt_spec/plan_spec.rb for more examples.
module BoltSpec
module Plans
def self.init
# Ensure tasks are enabled when rspec-puppet sets up an environment so we get task loaders.
# Note that this is probably not safe to do in modules that also test Puppet manifest code.
@@ -108,56 +145,77 @@
raise "RSpec.configuration.module_path not defined set up rspec puppet or define modulepath for this test"
end
# Override in your tests
def config
- config = Bolt::Config.new(Bolt::Boltdir.new('.'), {})
- config.modulepath = modulepath
- config
+ @config ||= begin
+ conf = Bolt::Config.new(Bolt::Boltdir.new('.'), {})
+ conf.modulepath = [modulepath].flatten
+ conf
+ end
end
# Override in your tests
def inventory
@inventory ||= Bolt::Inventory.new({})
end
+ # Provided as a class so expectations can be placed on it.
+ class MockPuppetDBClient; end
+
def puppetdb_client
- @puppetdb_client ||= mock('puppetdb_client')
+ @puppetdb_client ||= MockPuppetDBClient.new
end
def run_plan(name, params)
pal = Bolt::PAL.new(config.modulepath, config.hiera_config)
result = pal.run_plan(name, params, executor, inventory, puppetdb_client)
if executor.error_message
raise executor.error_message
end
- executor.assert_call_expectations
+ begin
+ executor.assert_call_expectations
+ rescue StandardError => e
+ raise "#{e.message}\nPlan result: #{result}"
+ end
result
end
- # Allowed task stubs can be called up to be_called_times number
- # of times
- def allow_task(task_name)
- executor.stub_task(task_name).add_stub
+ MOCKED_ACTIONS.each do |action|
+ # Allowed action stubs can be called up to be_called_times number of times
+ define_method :"allow_#{action}" do |object|
+ executor.send(:"stub_#{action}", object).add_stub
+ end
+
+ # Expected action stubs must be called exactly the expected number of times
+ # or at least once without be_called_times
+ define_method :"expect_#{action}" do |object|
+ send(:"allow_#{action}", object).expect_call
+ end
+
+ # This stub will catch any action call if there are no stubs specifically for that task
+ define_method :"allow_any_#{action}" do
+ executor.send(:"stub_#{action}", :default).add_stub
+ end
end
- # Expected task stubs must be called exactly the expected number of times
- # or at least once without be_called_times
- def expect_task(task_name)
- allow_task(task_name).expect_call
+ def allow_apply_prep
+ allow_task('puppet_agent::version').always_return('version' => '6.0')
+ allow_task('apply_helpers::custom_facts')
+ nil
end
- # This stub will catch any task call if there are no stubs specifically for that task
- def allow_any_task
- executor.stub_task(:default).add_stub
+ def allow_apply
+ executor.stub_apply
+ nil
end
# Example helpers to mock other run functions
- # The with_targets method makes sense for all stubs
+ # The with_targets method makes sense for all stubs
# with_params could be reused for options
# They probably need special stub methods for other arguments through
# Scripts can be mocked like tasks by their name
# arguments is an array instead of a hash though
@@ -178,9 +236,9 @@
# will be a separate effort.
# def allow_plan(plan_name)
# intended to be private below here
def executor
- @executor ||= BoltSpec::Plans::MockExecutor.new
+ @executor ||= BoltSpec::Plans::MockExecutor.new(modulepath)
end
end
end