diff options
author | Lamont Granquist <lamont@scriptkiddie.org> | 2019-06-17 21:07:08 -0700 |
---|---|---|
committer | Lamont Granquist <lamont@scriptkiddie.org> | 2019-08-12 11:29:16 -0700 |
commit | 35a63ddc192e50c45f6e94a3b270ed0e75c93668 (patch) | |
tree | fed93952978c3b75febae5d90f80e1f0cd5fcc8b /spec/integration | |
parent | 84da5f7a45d7ccba250d160626e6da8762a7f222 (diff) | |
download | chef-35a63ddc192e50c45f6e94a3b270ed0e75c93668.tar.gz |
Add unified_mode switch for resources
This is inspired by "use_inline_resources".
Setting `unified_mode false` in a resource would be the existing
behavior with separate compile/converge phases.
Setting `unified_mode true` in a resource will eliminate the converge
phase. Reverse notifications and delayed notifications will still
fire. The resource action will behave like all resources are executing
at compile time.
As a aside, notifications have never worked for resources firing at
compile time. This implementation gets that behavior correct so
that notifications will work.
Of course forward immediate notifications to resources not yet declared will not
be possible.
Setting `resource_unified_mode_default true` in `Chef::Config` would
turn off the split compile/converge mode for every custom resource.
NOTE: This does not affect recipe mode at all.
Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
Diffstat (limited to 'spec/integration')
-rw-r--r-- | spec/integration/recipes/unified_mode_spec.rb | 876 |
1 files changed, 876 insertions, 0 deletions
diff --git a/spec/integration/recipes/unified_mode_spec.rb b/spec/integration/recipes/unified_mode_spec.rb new file mode 100644 index 0000000000..944319f7bf --- /dev/null +++ b/spec/integration/recipes/unified_mode_spec.rb @@ -0,0 +1,876 @@ +require "support/shared/integration/integration_helper" +require "chef/mixin/shell_out" + +describe "Unified Mode" do + include IntegrationSupport + include Chef::Mixin::ShellOut + + let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) } + + let(:chef_client) { "bundle exec chef-client --minimal-ohai" } + + when_the_repository "has a cookbook with a unified_mode resource with a delayed notification from the second block to the first block" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + action :nothing + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + notifies :run, "ruby_block[first block]", :delayed + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the "second block" runs first after "bar" is set + expect(result.stdout).to include("second: bar") + # then the "first block" runs after "baz" in the delayed phase + expect(result.stdout).to include("first: baz") + # nothing else should fire + expect(result.stdout).not_to include("first: foo") + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified_mode resource with a delayed notification from the first block to the second block" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :delayed + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default' -l debug", cwd: chef_dir) + # the first block should fire first + expect(result.stdout).to include("first: foo") + # the second block should fire in delayed phase + expect(result.stdout).to include("second: baz") + # nothing else should fire + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: bar") + result.error! + end + end + + when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the second block to the first block" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + action :nothing + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + notifies :run, "ruby_block[first block]", :immediate + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the second resource should fire first when it is parsed + expect(result.stdout).to include("second: bar") + # the first resource should then immediately fire + expect(result.stdout).to include("first: bar") + # no other resources should fire + expect(result.stdout).not_to include("second: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("first: foo") + expect(result.stdout).not_to include("first: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the first block to the second block" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :immediate + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default' -l debug", cwd: chef_dir) + # both blocks should run when they're declared + expect(result.stdout).to include("first: foo") + expect(result.stdout).to include("second: bar") + # nothing else should run + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the first block to a block that does not exist" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :immediate + end + var = "bar" + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should fail the run" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # both blocks should run when they're declared + expect(result.stdout).to include("first: foo") + # nothing else should run + expect(result.stdout).not_to include("second: bar") + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + expect(result.stdout).to include("Chef::Exceptions::ResourceNotFound") + expect(result.error?).to be true + end + end + + when_the_repository "has a cookbook with a normal resource with an delayed notification with global resource unified mode on" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + resource_name :unified_mode + provides :unified_mode + + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + action :nothing + end + var = "bar" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :delayed + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + resource_unified_mode_default true + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the "first block" resource runs before the assignment to baz in compile time + expect(result.stdout).to include("first: bar") + # we should not run the "first block" at compile time + expect(result.stdout).not_to include("first: baz") + # (and certainly should run it this early) + expect(result.stdout).not_to include("first: foo") + # the delayed notification should still fire and run after everything else + expect(result.stdout).to include("second: baz") + # the action :nothing should suppress any other running of the second block + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: bar") + result.error! + end + end + + when_the_repository "has a cookbook with a normal resource with an immediate notification with global resource unified mode on" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + action :nothing + end + var = "bar" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :immediate + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + resource_unified_mode_default true + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the "first block" resource runs before the assignment to baz in compile time + expect(result.stdout).to include("first: bar") + # we should not run the "first block" at compile time + expect(result.stdout).not_to include("first: baz") + # (and certainly should run it this early) + expect(result.stdout).not_to include("first: foo") + # the immediate notifiation fires immediately + expect(result.stdout).to include("second: bar") + # the action :nothing should suppress any other running of the second block + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with an immediate subscribes from the second resource to the first" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + subscribes :run, "ruby_block[first block]", :immediate + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the first resource fires + expect(result.stdout).to include("first: foo") + # the second resource fires when it is parsed + expect(result.stdout).to include("second: bar") + # no other actions should run + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with an immediate subscribes from the first resource to the second" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + subscribes :run, "ruby_block[second block]", :immediate + action :nothing + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the second resource fires first after bar is set + expect(result.stdout).to include("second: bar") + # the first resource then has its immediate subscribes fire at that location + expect(result.stdout).to include("first: bar") + # no other actions should run + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("first: foo") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with an delayed subscribes from the second resource to the first" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + subscribes :run, "ruby_block[first block]", :delayed + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the first resource fires as it is parsed + expect(result.stdout).to include("first: foo") + # the second resource then fires in the delayed notifications phase + expect(result.stdout).to include("second: baz") + # no other actions should run + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: bar") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with an delayed subscribes from the first resource to the second" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + subscribes :run, "ruby_block[second block]", :delayed + action :nothing + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the second resource fires first after bar is set + expect(result.stdout).to include("second: bar") + # the first resource then fires in the delayed notifications phase + expect(result.stdout).to include("first: baz") + # no other actions should run + expect(result.stdout).not_to include("first: foo") + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with a correct before notification" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "notified block" do + block do + puts "\nnotified: \#\{var\}" + end + action :nothing + end + var = "bar" + whyrun_safe_ruby_block "notifying block" do + block do + puts "\nnotifying: \#\{var\}" + end + notifies :run, "ruby_block[notified block]", :before + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + expect(result.stdout.scan(/notifying: bar/).length).to eql(2) + expect(result.stdout).to include("Would execute the whyrun_safe_ruby_block notifying block") + expect(result.stdout).to include("notified: bar") + # no other actions should run + expect(result.stdout).not_to include("notified: foo") + expect(result.stdout).not_to include("notified: baz") + expect(result.stdout).not_to include("notifying: foo") + expect(result.stdout).not_to include("notifying: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with a correct before subscribes" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "notified block" do + block do + puts "\nnotified: \#\{var\}" + end + subscribes :run, "whyrun_safe_ruby_block[notifying block]", :before + action :nothing + end + var = "bar" + whyrun_safe_ruby_block "notifying block" do + block do + puts "\nnotifying: \#\{var\}" + end + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + expect(result.stdout.scan(/notifying: bar/).length).to eql(2) + expect(result.stdout).to include("Would execute the whyrun_safe_ruby_block notifying block") + expect(result.stdout).to include("notified: bar") + # no other actions should run + expect(result.stdout).not_to include("notified: foo") + expect(result.stdout).not_to include("notified: baz") + expect(result.stdout).not_to include("notifying: foo") + expect(result.stdout).not_to include("notifying: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with a broken/reversed before notification" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + whyrun_safe_ruby_block "notifying block" do + block do + puts "\nnotifying: \#\{var\}" + end + notifies :run, "ruby_block[notified block]", :before + end + var = "bar" + ruby_block "notified block" do + block do + puts "\nnotified: \#\{var\}" + end + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should fail the run" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default' -l debug", cwd: chef_dir) + # this doesn't work and we can't tell the difference between it and if we were trying to do a correct :before notification but typo'd the name + # so Chef::Exceptions::ResourceNotFound is the best we can do + expect(result.stdout).to include("Chef::Exceptions::ResourceNotFound") + expect(result.error?).to be true + end + end + + when_the_repository "has a cookbook with a unified resource with a broken/reversed before subscribes" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + whyrun_safe_ruby_block "notifying block" do + block do + puts "\nnotifying: \#\{var\}" + end + end + var = "bar" + ruby_block "notified block" do + block do + puts "\nnotified: \#\{var\}" + end + subscribes :run, "whyrun_safe_ruby_block[notifying block]", :before + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should fail the run" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # this fires first normally before the error + expect(result.stdout).to include("notifying: foo") + # everything else does not run + expect(result.stdout).not_to include("notified: foo") + expect(result.stdout).not_to include("notified: bar") + expect(result.stdout).not_to include("notified: baz") + expect(result.stdout).not_to include("notifying: bar") + expect(result.stdout).not_to include("notifying: baz") + expect(result.stdout).to include("Chef::Exceptions::UnifiedModeBeforeSubscriptionEarlierResource") + expect(result.error?).to be true + end + end + + when_the_repository "has global resource unified mode on" do + before do + directory "cookbooks/x" do + + file "recipes/default.rb", <<-EOM + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + end + var = "bar" + EOM + + end # directory 'cookbooks/x' + end + + it "recipes should still have a compile/converge mode" do + file "config/client.rb", <<~EOM + resource_unified_mode_default true + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # in recipe mode we should still run normally with a compile/converge mode + expect(result.stdout).to include("first: bar") + expect(result.stdout).not_to include("first: foo") + result.error! + end + end +end |