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