summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/functional/resource/group_spec.rb1
-rw-r--r--spec/functional/resource/user/useradd_spec.rb6
-rw-r--r--spec/integration/knife/data_bag_create_spec.rb3
-rw-r--r--spec/integration/knife/environment_show_spec.rb28
-rw-r--r--spec/integration/knife/node_environment_set_spec.rb5
-rw-r--r--spec/integration/recipes/accumulator_spec.rb232
-rw-r--r--spec/integration/recipes/resource_action_spec.rb2
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/support/shared/integration/app_server_support.rb2
-rw-r--r--spec/support/shared/integration/knife_support.rb5
-rw-r--r--spec/unit/http_spec.rb2
-rw-r--r--spec/unit/knife/core/gem_glob_loader_spec.rb2
-rw-r--r--spec/unit/knife/core/ui_spec.rb10
-rw-r--r--spec/unit/knife_spec.rb31
-rw-r--r--spec/unit/mixin/powershell_out_spec.rb26
-rw-r--r--spec/unit/node/vivid_mash_spec.rb116
-rw-r--r--spec/unit/node_spec.rb137
-rw-r--r--spec/unit/provider/deploy_spec.rb2
-rw-r--r--spec/unit/provider/group/suse_spec.rb90
-rw-r--r--spec/unit/provider/package/apt_spec.rb22
-rw-r--r--spec/unit/provider/package/windows/msi_spec.rb17
-rw-r--r--spec/unit/provider/package/windows_spec.rb6
-rw-r--r--spec/unit/provider/package/yum_spec.rb18
-rw-r--r--spec/unit/provider/package/zypper_spec.rb64
-rw-r--r--spec/unit/provider/package_spec.rb58
-rw-r--r--spec/unit/provider/remote_file/content_spec.rb2
-rw-r--r--spec/unit/provider/user/solaris_spec.rb45
-rw-r--r--spec/unit/provider/user_spec.rb6
-rw-r--r--spec/unit/resource/apt_repository_spec.rb2
29 files changed, 815 insertions, 129 deletions
diff --git a/spec/functional/resource/group_spec.rb b/spec/functional/resource/group_spec.rb
index 31f9933546..7effd386a4 100644
--- a/spec/functional/resource/group_spec.rb
+++ b/spec/functional/resource/group_spec.rb
@@ -425,6 +425,7 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
end
it "does not raise an error on manage" do
+ allow(Etc).to receive(:getpwnam).and_return(double("User"))
expect { group_resource.run_action(:manage) }.not_to raise_error
end
end
diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb
index 79d62436f5..874155c107 100644
--- a/spec/functional/resource/user/useradd_spec.rb
+++ b/spec/functional/resource/user/useradd_spec.rb
@@ -637,8 +637,10 @@ describe Chef::Provider::User::Useradd, metadata do
context "and has no password" do
# TODO: platform_family should be setup in spec_helper w/ tags
- if %w{suse opensuse}.include?(OHAI_SYSTEM["platform_family"])
- # suse gets this right:
+ if %w{opensuse}.include?(OHAI_SYSTEM["platform_family"]) ||
+ (%w{suse}.include?(OHAI_SYSTEM["platform_family"]) &&
+ OHAI_SYSTEM["platform_version"].to_f < 12.1)
+ # suse 11.x gets this right:
it "errors out trying to unlock the user" do
expect(@error).to be_a(Mixlib::ShellOut::ShellCommandFailed)
expect(@error.message).to include("Cannot unlock the password")
diff --git a/spec/integration/knife/data_bag_create_spec.rb b/spec/integration/knife/data_bag_create_spec.rb
index 0a07792dbc..dc61d55fd5 100644
--- a/spec/integration/knife/data_bag_create_spec.rb
+++ b/spec/integration/knife/data_bag_create_spec.rb
@@ -34,12 +34,10 @@ describe "knife data bag create", :workstation do
end
it "creates a new data bag and item" do
- pending "Deprecation warning must get fixed"
knife("data bag create foo bar").should_succeed stdout: out, stderr: err
end
it "adds a new item to an existing bag" do
- pending "Deprecation warning must get fixed"
knife("data bag create foo").should_succeed stderr: err
knife("data bag create foo bar").should_succeed stdout: out, stderr: exists
end
@@ -50,7 +48,6 @@ describe "knife data bag create", :workstation do
end
it "fails to add an existing item" do
- pending "Deprecation warning must get fixed"
knife("data bag create foo bar").should_succeed stdout: out, stderr: err
expect { knife("data bag create foo bar") }.to raise_error(Net::HTTPServerException)
end
diff --git a/spec/integration/knife/environment_show_spec.rb b/spec/integration/knife/environment_show_spec.rb
index e74bf6d05d..56422dc1a5 100644
--- a/spec/integration/knife/environment_show_spec.rb
+++ b/spec/integration/knife/environment_show_spec.rb
@@ -26,7 +26,7 @@ describe "knife environment show", :workstation do
when_the_chef_server "has some environments" do
before do
environment "b", {
- "default_attributes" => { "foo" => "bar" },
+ "default_attributes" => { "foo" => "bar", "baz" => { "raz.my" => "mataz" } },
}
end
@@ -36,6 +36,8 @@ describe "knife environment show", :workstation do
chef_type: environment
cookbook_versions:
default_attributes:
+ baz:
+ raz.my: mataz
foo: bar
description:
json_class: Chef::Environment
@@ -46,11 +48,29 @@ EOM
# rubocop:enable Style/TrailingWhitespace
it "shows the requested attribute of an environment" do
- pending "KnifeSupport doesn't appear to pass this through correctly"
- knife("environment show b -a foo").should_succeed <<EOM
+ knife("environment show b -a default_attributes").should_succeed <<EOM
b:
- foo: bar
+ default_attributes:
+ baz:
+ raz.my: mataz
+ foo: bar
EOM
end
+
+ it "shows the requested nested attribute of an environment" do
+ knife("environment show b -a default_attributes.baz").should_succeed <<EON
+b:
+ default_attributes.baz:
+ raz.my: mataz
+EON
+ end
+
+ it "shows the requested attribute of an environment with custom field separator" do
+ knife("environment show b -S: -a default_attributes:baz").should_succeed <<EOT
+b:
+ default_attributes:baz:
+ raz.my: mataz
+EOT
+ end
end
end
diff --git a/spec/integration/knife/node_environment_set_spec.rb b/spec/integration/knife/node_environment_set_spec.rb
index 6dadecf76a..10fec5723f 100644
--- a/spec/integration/knife/node_environment_set_spec.rb
+++ b/spec/integration/knife/node_environment_set_spec.rb
@@ -31,7 +31,10 @@ describe "knife node environment set", :workstation do
it "sets an environment on a node" do
knife("node environment set cons lisp").should_succeed /chef_environment:.*lisp/
- knife("node show cons -a chef_environment").should_succeed /Environment:.*lisp/
+ knife("node show cons -a chef_environment").should_succeed <<EOM
+cons:
+ chef_environment: lisp
+EOM
end
it "with no environment" do
diff --git a/spec/integration/recipes/accumulator_spec.rb b/spec/integration/recipes/accumulator_spec.rb
new file mode 100644
index 0000000000..e6afe09b8c
--- /dev/null
+++ b/spec/integration/recipes/accumulator_spec.rb
@@ -0,0 +1,232 @@
+require "support/shared/integration/integration_helper"
+require "chef/mixin/shell_out"
+
+describe "Accumulators" do
+ include IntegrationSupport
+ include Chef::Mixin::ShellOut
+
+ let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
+
+ # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
+ # following constraints are satisfied:
+ # * Windows: windows can only run batch scripts as bare executables. Rubygems
+ # creates batch wrappers for installed gems, but we don't have batch wrappers
+ # in the source tree.
+ # * Other `chef-client` in PATH: A common case is running the tests on a
+ # machine that has omnibus chef installed. In that case we need to ensure
+ # we're running `chef-client` from the source tree and not the external one.
+ # cf. CHEF-4914
+ let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
+
+ let(:aliases_temppath) do
+ t = Tempfile.new("chef_accumulator_test")
+ path = t.path
+ t.close
+ t.unlink
+ path
+ end
+
+ when_the_repository "edit_resource-based delayed accumulators work" do
+ before do
+ directory "cookbooks/x" do
+ file "resources/email_alias.rb", <<-EOM
+ provides :email_alias
+ resource_name :email_alias
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ with_run_context :root do
+ edit_resource(:template, "#{aliases_temppath}") do |new_resource|
+ source "aliases.erb"
+ variables[:aliases] ||= {}
+ variables[:aliases][new_resource.address] ||= []
+ variables[:aliases][new_resource.address] += new_resource.recipients
+ action :nothing
+ delayed_action :create
+ end
+ end
+ end
+ EOM
+
+ file "resources/nested.rb", <<-EOM
+ provides :nested
+ resource_name :nested
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ email_alias address do
+ recipients new_resource.recipients
+ end
+ end
+ EOM
+
+ file "resources/doubly_nested.rb", <<-EOM
+ provides :doubly_nested
+ resource_name :doubly_nested
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ nested address do
+ recipients new_resource.recipients
+ end
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ email_alias "outer1" do
+ recipients [ "out1a", "out1b" ]
+ end
+
+ nested "nested1" do
+ recipients [ "nested1a", "nested1b" ]
+ end
+
+ email_alias "outer2" do
+ recipients [ "out2a", "out2b" ]
+ end
+
+ doubly_nested "nested2" do
+ recipients [ "nested2a", "nested2b" ]
+ end
+
+ email_alias "outer3" do
+ recipients [ "out3a", "out3b" ]
+ end
+ EOM
+
+ file "templates/aliases.erb", <<-EOM.gsub(/^\s+/, "")
+ <%= pp @aliases %>
+ 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)
+ result.error!
+ # runs only a single template resource (in the outer run context, as a delayed resource)
+ expect(result.stdout.scan(/template\S+ action create/).size).to eql(1)
+ # hash order is insertion order in ruby >= 1.9, so this next line does test that all calls were in the correct order
+ expect(IO.read(aliases_temppath).chomp).to eql('{"outer1"=>["out1a", "out1b"], "nested1"=>["nested1a", "nested1b"], "outer2"=>["out2a", "out2b"], "nested2"=>["nested2a", "nested2b"], "outer3"=>["out3a", "out3b"]}')
+ end
+ end
+
+ when_the_repository "find_resource-based delayed accumulators work" do
+ before do
+ directory "cookbooks/x" do
+ file "resources/email_alias.rb", <<-EOM
+ provides :email_alias
+ resource_name :email_alias
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ r = with_run_context :root do
+ find_resource(:template, "#{aliases_temppath}") do
+ source "aliases.erb"
+ variables[:aliases] = {}
+ action :nothing
+ delayed_action :create
+ end
+ end
+ r.variables[:aliases][address] ||= []
+ r.variables[:aliases][address] += new_resource.recipients
+ end
+ EOM
+
+ file "resources/nested.rb", <<-EOM
+ provides :nested
+ resource_name :nested
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ email_alias address do
+ recipients new_resource.recipients
+ end
+ end
+ EOM
+
+ file "resources/doubly_nested.rb", <<-EOM
+ provides :doubly_nested
+ resource_name :doubly_nested
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ nested address do
+ recipients new_resource.recipients
+ end
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ email_alias "outer1" do
+ recipients [ "out1a", "out1b" ]
+ end
+
+ nested "nested1" do
+ recipients [ "nested1a", "nested1b" ]
+ end
+
+ email_alias "outer2" do
+ recipients [ "out2a", "out2b" ]
+ end
+
+ doubly_nested "nested2" do
+ recipients [ "nested2a", "nested2b" ]
+ end
+
+ email_alias "outer3" do
+ recipients [ "out3a", "out3b" ]
+ end
+ EOM
+
+ file "templates/aliases.erb", <<-EOM.gsub(/^\s+/, "")
+ <%= pp @aliases %>
+ 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)
+ result.error!
+ # runs only a single template resource (in the outer run context, as a delayed resource)
+ expect(result.stdout.scan(/template\S+ action create/).size).to eql(1)
+ # hash order is insertion order in ruby >= 1.9, so this next line does test that all calls were in the correct order
+ expect(IO.read(aliases_temppath).chomp).to eql('{"outer1"=>["out1a", "out1b"], "nested1"=>["nested1a", "nested1b"], "outer2"=>["out2a", "out2b"], "nested2"=>["nested2a", "nested2b"], "outer3"=>["out3a", "out3b"]}')
+ end
+ end
+end
diff --git a/spec/integration/recipes/resource_action_spec.rb b/spec/integration/recipes/resource_action_spec.rb
index 60d5831a50..f11696bae4 100644
--- a/spec/integration/recipes/resource_action_spec.rb
+++ b/spec/integration/recipes/resource_action_spec.rb
@@ -498,7 +498,7 @@ module ResourceActionSpec
it "Raises an error when attempting to use a template in the action" do
expect_converge do
has_property_named_template "hi"
- end.to raise_error(/Property template of has_property_named_template\[hi\] cannot be passed a block! If you meant to create a resource named template instead, you'll need to first rename the property./)
+ end.to raise_error(/Property `template` of `has_property_named_template\[hi\]` was incorrectly passed a block. Possible property-resource collision. To call a resource named `template` either rename the property or else use `declare_resource\(:template, ...\)`/)
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 47a5ec7f9f..7559e797bc 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -174,13 +174,13 @@ RSpec.configure do |config|
running_platform_arch = `uname -m`.strip unless windows?
- config.filter_run_excluding :arch => lambda {|target_arch|
+ config.filter_run_excluding :arch => lambda { |target_arch|
running_platform_arch != target_arch
}
# Functional Resource tests that are provider-specific:
# context "on platforms that use useradd", :provider => {:user => Chef::Provider::User::Useradd}} do #...
- config.filter_run_excluding :provider => lambda {|criteria|
+ config.filter_run_excluding :provider => lambda { |criteria|
type, target_provider = criteria.first
node = TEST_NODE.dup
diff --git a/spec/support/shared/integration/app_server_support.rb b/spec/support/shared/integration/app_server_support.rb
index 4dfa3fa155..e2bb3812ea 100644
--- a/spec/support/shared/integration/app_server_support.rb
+++ b/spec/support/shared/integration/app_server_support.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, 2013-2015 Chef Software, Inc.
+# Copyright:: Copyright 2012-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/support/shared/integration/knife_support.rb b/spec/support/shared/integration/knife_support.rb
index 1a374e1b84..4efa30a003 100644
--- a/spec/support/shared/integration/knife_support.rb
+++ b/spec/support/shared/integration/knife_support.rb
@@ -64,8 +64,11 @@ module KnifeSupport
subcommand_class.load_deps
instance = subcommand_class.new(args)
+ # Load configs
+ instance.merge_configs
+
# Capture stdout/stderr
- instance.ui = Chef::Knife::UI.new(stdout, stderr, stdin, disable_editing: true)
+ instance.ui = Chef::Knife::UI.new(stdout, stderr, stdin, instance.config.merge(disable_editing: true))
# Don't print stuff
Chef::Config[:verbosity] = ( DEBUG ? 2 : 0 )
diff --git a/spec/unit/http_spec.rb b/spec/unit/http_spec.rb
index d99f03aac9..d58f07c417 100644
--- a/spec/unit/http_spec.rb
+++ b/spec/unit/http_spec.rb
@@ -74,7 +74,7 @@ describe Chef::HTTP do
expect(http.create_url("///api/endpoint?url=http://foo.bar")).to eql(URI.parse("http://www.getchef.com/organization/org/api/endpoint?url=http://foo.bar"))
end
- # As per: https://github.com/opscode/chef/issues/2500
+ # As per: https://github.com/chef/chef/issues/2500
it "should treat scheme part of the URI in a case-insensitive manner" do
http = Chef::HTTP.allocate # Calling Chef::HTTP::new sets @url, don't want that.
expect { http.create_url("HTTP://www1.chef.io/") }.not_to raise_error
diff --git a/spec/unit/knife/core/gem_glob_loader_spec.rb b/spec/unit/knife/core/gem_glob_loader_spec.rb
index 69a40ebaed..2f9e04769e 100644
--- a/spec/unit/knife/core/gem_glob_loader_spec.rb
+++ b/spec/unit/knife/core/gem_glob_loader_spec.rb
@@ -78,7 +78,7 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
expect(loader.site_subcommands).to include(expected_command)
end
- # https://github.com/opscode/chef-dk/issues/227
+ # https://github.com/chef/chef-dk/issues/227
#
# `knife` in ChefDK isn't from a gem install, it's directly run from a clone
# of the source, but there can be one or more versions of chef also installed
diff --git a/spec/unit/knife/core/ui_spec.rb b/spec/unit/knife/core/ui_spec.rb
index be77fd8501..38c72161e5 100644
--- a/spec/unit/knife/core/ui_spec.rb
+++ b/spec/unit/knife/core/ui_spec.rb
@@ -28,6 +28,7 @@ describe Chef::Knife::UI do
:verbosity => 0,
:yes => nil,
:format => "summary",
+ :field_separator => ".",
}
@ui = Chef::Knife::UI.new(@out, @err, @in, @config)
Chef::Config[:treat_deprecation_warnings_as_errors] = false
@@ -410,6 +411,15 @@ EOM
@ui.config[:attribute] = non_existing_path
expect(@ui.format_for_display(input)).to eq({ "sample-data-bag-item" => { non_existing_path => nil } })
end
+
+ describe "when --field-separator is passed" do
+ it "honors that separator" do
+ input = { "keys" => { "with spaces" => { "open" => { "doors" => { "with many.dots" => "when asked" } } } } }
+ @ui.config[:field_separator] = ";"
+ @ui.config[:attribute] = "keys;with spaces;open;doors;with many.dots"
+ expect(@ui.format_for_display(input)).to eq({ nil => { "keys;with spaces;open;doors;with many.dots" => "when asked" } })
+ end
+ end
end
describe "with --run-list passed" do
diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb
index f0ec45d59a..9569526b2a 100644
--- a/spec/unit/knife_spec.rb
+++ b/spec/unit/knife_spec.rb
@@ -349,6 +349,37 @@ describe Chef::Knife do
expect { knife.run_with_pretty_exceptions }.to raise_error(Exception)
end
end
+
+ describe "setting arbitrary configuration with --config-option" do
+
+ let(:stdout) { StringIO.new }
+
+ let(:stderr) { StringIO.new }
+
+ let(:stdin) { StringIO.new }
+
+ let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, disable_editing: true) }
+
+ let(:subcommand) do
+ KnifeSpecs::TestYourself.options = Chef::Application::Knife.options.merge(KnifeSpecs::TestYourself.options)
+ KnifeSpecs::TestYourself.new(%w{--config-option badly_formatted_arg}).tap do |cmd|
+ cmd.ui = ui
+ end
+ end
+
+ it "sets arbitrary configuration via --config-option" do
+ Chef::Knife.run(%w{test yourself --config-option arbitrary_config_thing=hello}, Chef::Application::Knife.options)
+ expect(Chef::Config[:arbitrary_config_thing]).to eq("hello")
+ end
+
+ it "handles errors in arbitrary configuration" do
+ expect(subcommand).to receive(:exit).with(1)
+ subcommand.configure_chef
+ expect(stderr.string).to include("ERROR: Unparsable config option \"badly_formatted_arg\"")
+ expect(stdout.string).to include(subcommand.opt_parser.to_s)
+ end
+ end
+
end
describe "when first created" do
diff --git a/spec/unit/mixin/powershell_out_spec.rb b/spec/unit/mixin/powershell_out_spec.rb
index 8e5f3588ce..4ecdcb8325 100644
--- a/spec/unit/mixin/powershell_out_spec.rb
+++ b/spec/unit/mixin/powershell_out_spec.rb
@@ -18,7 +18,7 @@
require "spec_helper"
require "chef/mixin/powershell_out"
-describe Chef::Mixin::PowershellOut do
+describe Chef::Mixin::PowershellOut, :windows_only do
let(:shell_out_class) { Class.new { include Chef::Mixin::PowershellOut } }
subject(:object) { shell_out_class.new }
let(:architecture) { "something" }
@@ -44,6 +44,18 @@ describe Chef::Mixin::PowershellOut do
).and_return(ret)
expect(object.powershell_out("Get-Process", timeout: 600)).to eql(ret)
end
+
+ context "when double quote is passed in the powershell command" do
+ it "passes if double quote is appended with single escape" do
+ result = object.powershell_out("Write-Verbose \"Some String\" -Verbose")
+ expect(result.stderr).to be == ""
+ expect(result.stdout).to be == "VERBOSE: Some String\n"
+ end
+
+ it "suppresses error if double quote is passed with double escape characters" do
+ expect { object.powershell_out("Write-Verbose \\\"Some String\\\" -Verbose") }.not_to raise_error
+ end
+ end
end
describe "#powershell_out!" do
@@ -66,5 +78,17 @@ describe Chef::Mixin::PowershellOut do
expect(mixlib_shellout).to receive(:error!)
expect(object.powershell_out!("Get-Process", timeout: 600)).to eql(mixlib_shellout)
end
+
+ context "when double quote is passed in the powershell command" do
+ it "passes if double quote is appended with single escape" do
+ result = object.powershell_out!("Write-Verbose \"Some String\" -Verbose")
+ expect(result.stderr).to be == ""
+ expect(result.stdout).to be == "VERBOSE: Some String\n"
+ end
+
+ it "raises error if double quote is passed with double escape characters" do
+ expect { object.powershell_out!("Write-Verbose \\\"Some String\\\" -Verbose") }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
+ end
+ end
end
end
diff --git a/spec/unit/node/vivid_mash_spec.rb b/spec/unit/node/vivid_mash_spec.rb
index 206b15ef6c..017e6206fc 100644
--- a/spec/unit/node/vivid_mash_spec.rb
+++ b/spec/unit/node/vivid_mash_spec.rb
@@ -19,36 +19,46 @@ require "spec_helper"
require "chef/node/attribute_collections"
describe Chef::Node::VividMash do
- class Root
- attr_accessor :top_level_breadcrumb
- end
-
- let(:root) { Root.new }
+ let(:root) { instance_double(Chef::Node::Attribute) }
let(:vivid) do
- expect(root).to receive(:reset_cache).at_least(:once).with(nil)
- Chef::Node::VividMash.new(root,
- { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
+ Chef::Node::VividMash.new(
+ { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil },
+ root
)
end
- def with_breadcrumb(key)
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
- expect(root).to receive(:top_level_breadcrumb=).with(key).at_least(:once).and_call_original
+ context "without a root node" do
+ let(:vivid) do
+ Chef::Node::VividMash.new(
+ { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
+ )
+ end
+
+ it "sets the root to the root object" do
+ expect(vivid["one"]["two"].__root__).to eql(vivid)
+ end
+
+ it "does not send reset cache" do
+ # if we setup the expectation here then the object winds up responding to :reset_cache and then it fails...
+ # expect(vivid).not_to receive(:reset_cache)
+ # but even so we expect to blow up here with NoMethodError if we screw up and send :reset_cache to a root VividMash
+ vivid["one"]["foo"] = "bar"
+ end
end
context "#[]=" do
it "deep converts values through arrays" do
- allow(root).to receive(:reset_cache)
- vivid[:foo] = [ { :bar => true } ]
+ expect(root).to receive(:reset_cache).with("foo")
+ vivid["foo"] = [ { :bar => true } ]
expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
expect(vivid["foo"][0].class).to eql(Chef::Node::VividMash)
expect(vivid["foo"][0]["bar"]).to be true
end
it "deep converts values through nested arrays" do
- allow(root).to receive(:reset_cache)
- vivid[:foo] = [ [ { :bar => true } ] ]
+ expect(root).to receive(:reset_cache).with("foo")
+ vivid["foo"] = [ [ { :bar => true } ] ]
expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
expect(vivid["foo"][0].class).to eql(Chef::Node::AttrArray)
expect(vivid["foo"][0][0].class).to eql(Chef::Node::VividMash)
@@ -56,8 +66,8 @@ describe Chef::Node::VividMash do
end
it "deep converts values through hashes" do
- allow(root).to receive(:reset_cache)
- vivid[:foo] = { baz: { :bar => true } }
+ expect(root).to receive(:reset_cache).with("foo")
+ vivid["foo"] = { baz: { :bar => true } }
expect(vivid["foo"]).to be_an_instance_of(Chef::Node::VividMash)
expect(vivid["foo"]["baz"]).to be_an_instance_of(Chef::Node::VividMash)
expect(vivid["foo"]["baz"]["bar"]).to be true
@@ -66,182 +76,144 @@ describe Chef::Node::VividMash do
context "#read" do
before do
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
- vivid
expect(root).not_to receive(:reset_cache)
end
it "reads hashes deeply" do
- with_breadcrumb("one")
expect(vivid.read("one", "two", "three")).to eql("four")
end
it "does not trainwreck when hitting hash keys that do not exist" do
- with_breadcrumb("one")
expect(vivid.read("one", "five", "six")).to eql(nil)
end
it "does not trainwreck when hitting an array with an out of bounds index" do
- with_breadcrumb("array")
expect(vivid.read("array", 5, "one")).to eql(nil)
end
it "does not trainwreck when hitting an array with a string key" do
- with_breadcrumb("array")
expect(vivid.read("array", "one", "two")).to eql(nil)
end
it "does not trainwreck when traversing a nil" do
- with_breadcrumb("nil")
expect(vivid.read("nil", "one", "two")).to eql(nil)
end
end
context "#exist?" do
before do
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
- vivid
expect(root).not_to receive(:reset_cache)
end
it "true if there's a hash key there" do
- with_breadcrumb("one")
expect(vivid.exist?("one", "two", "three")).to be true
end
it "true for intermediate hashes" do
- with_breadcrumb("one")
expect(vivid.exist?("one")).to be true
end
it "true for arrays that exist" do
- with_breadcrumb("array")
expect(vivid.exist?("array", 1)).to be true
end
it "true when the value of the key is nil" do
- with_breadcrumb("nil")
expect(vivid.exist?("nil")).to be true
end
it "false when attributes don't exist" do
- with_breadcrumb("one")
expect(vivid.exist?("one", "five", "six")).to be false
end
it "false when traversing a non-container" do
- with_breadcrumb("one")
expect(vivid.exist?("one", "two", "three", "four")).to be false
end
it "false when an array index does not exist" do
- with_breadcrumb("array")
expect(vivid.exist?("array", 3)).to be false
end
it "false when traversing a nil" do
- with_breadcrumb("nil")
expect(vivid.exist?("nil", "foo", "bar")).to be false
end
end
context "#read!" do
before do
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
- vivid
expect(root).not_to receive(:reset_cache)
end
it "reads hashes deeply" do
- with_breadcrumb("one")
expect(vivid.read!("one", "two", "three")).to eql("four")
end
it "reads arrays deeply" do
- with_breadcrumb("array")
expect(vivid.read!("array", 1)).to eql(1)
end
it "throws an exception when attributes do not exist" do
- with_breadcrumb("one")
expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
end
it "throws an exception when traversing a non-container" do
- with_breadcrumb("one")
expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
end
it "throws an exception when an array element does not exist" do
- with_breadcrumb("array")
expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute)
end
end
context "#write" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should write into hashes" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "five", "six")
expect(vivid["one"]["five"]).to eql("six")
end
it "should deeply autovivify" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "five", "six", "seven", "eight", "nine", "ten")
expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
end
it "should raise an exception if you overwrite an array with a hash" do
- with_breadcrumb("array")
expect(root).to receive(:reset_cache).at_least(:once).with("array")
vivid.write("array", "five", "six")
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => "six" }, "nil" => nil })
end
it "should raise an exception if you traverse through an array with a hash" do
- with_breadcrumb("array")
expect(root).to receive(:reset_cache).at_least(:once).with("array")
vivid.write("array", "five", "six", "seven")
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => { "six" => "seven" } }, "nil" => nil })
end
it "should raise an exception if you overwrite a string with a hash" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "two", "three", "four", "five")
expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => "five" } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you traverse through a string with a hash" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "two", "three", "four", "five", "six")
expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => { "five" => "six" } } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you overwrite a nil with a hash" do
- with_breadcrumb("nil")
expect(root).to receive(:reset_cache).at_least(:once).with("nil")
vivid.write("nil", "one", "two")
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => "two" } })
end
it "should raise an exception if you traverse through a nil with a hash" do
- with_breadcrumb("nil")
expect(root).to receive(:reset_cache).at_least(:once).with("nil")
vivid.write("nil", "one", "two", "three")
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => { "two" => "three" } } })
end
it "writes with a block" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "five") { "six" }
expect(vivid["one"]["five"]).to eql("six")
@@ -249,69 +221,55 @@ describe Chef::Node::VividMash do
end
context "#write!" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should write into hashes" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write!("one", "five", "six")
expect(vivid["one"]["five"]).to eql("six")
end
it "should deeply autovivify" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write!("one", "five", "six", "seven", "eight", "nine", "ten")
expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
end
it "should raise an exception if you overwrite an array with a hash" do
- with_breadcrumb("array")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("array", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you traverse through an array with a hash" do
- with_breadcrumb("array")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("array", "five", "six", "seven") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you overwrite a string with a hash" do
- with_breadcrumb("one")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("one", "two", "three", "four", "five") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you traverse through a string with a hash" do
- with_breadcrumb("one")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("one", "two", "three", "four", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you overwrite a nil with a hash" do
- with_breadcrumb("nil")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("nil", "one", "two") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you traverse through a nil with a hash" do
- with_breadcrumb("nil")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("nil", "one", "two", "three") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "writes with a block" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write!("one", "five") { "six" }
expect(vivid["one"]["five"]).to eql("six")
@@ -319,41 +277,31 @@ describe Chef::Node::VividMash do
end
context "#unlink" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should return nil if the keys don't already exist" do
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
expect(root).not_to receive(:reset_cache)
expect(vivid.unlink("five", "six", "seven", "eight")).to eql(nil)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should unlink hashes" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
expect( vivid.unlink("one") ).to eql({ "two" => { "three" => "four" } })
expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should unlink array elements" do
- with_breadcrumb("array")
expect(root).to receive(:reset_cache).at_least(:once).with("array")
expect(vivid.unlink("array", 2)).to eql(2)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
end
it "should unlink nil" do
- with_breadcrumb("nil")
expect(root).to receive(:reset_cache).at_least(:once).with("nil")
expect(vivid.unlink("nil")).to eql(nil)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
end
it "should traverse a nil and safely do nothing" do
- with_breadcrumb("nil")
expect(root).not_to receive(:reset_cache)
expect(vivid.unlink("nil", "foo")).to eql(nil)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
@@ -361,41 +309,31 @@ describe Chef::Node::VividMash do
end
context "#unlink!" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should raise an exception if the keys don't already exist" do
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
expect(root).not_to receive(:reset_cache)
expect { vivid.unlink!("five", "six", "seven", "eight") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should unlink! hashes" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
expect( vivid.unlink!("one") ).to eql({ "two" => { "three" => "four" } })
expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should unlink! array elements" do
- with_breadcrumb("array")
expect(root).to receive(:reset_cache).at_least(:once).with("array")
expect(vivid.unlink!("array", 2)).to eql(2)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
end
it "should unlink! nil" do
- with_breadcrumb("nil")
expect(root).to receive(:reset_cache).at_least(:once).with("nil")
expect(vivid.unlink!("nil")).to eql(nil)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
end
it "should raise an exception if it traverses a nil" do
- with_breadcrumb("nil")
expect(root).not_to receive(:reset_cache)
expect { vivid.unlink!("nil", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index 2c8fc4408b..cfc19db480 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -243,6 +243,34 @@ describe Chef::Node do
expect { node.sunshine = "is bright" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
end
+ it "does not allow modification of node attributes via hash methods" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ node.default["h4sh"] = { foo: "bar" }
+ expect { node["h4sh"].delete("foo") }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
+ expect { node.h4sh.delete("foo") }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
+ end
+
+ it "does not allow modification of node attributes via array methods" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ node.default["array"] = []
+ expect { node["array"] << "boom" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
+ expect { node.array << "boom" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
+ end
+
+ it "returns merged immutable attributes for arrays" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ node.default["array"] = []
+ expect( node["array"].class ).to eql(Chef::Node::ImmutableArray)
+ expect( node.array.class ).to eql(Chef::Node::ImmutableArray)
+ end
+
+ it "returns merged immutable attributes for hashes" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ node.default["h4sh"] = {}
+ expect( node["h4sh"].class ).to eql(Chef::Node::ImmutableMash)
+ expect( node.h4sh.class ).to eql(Chef::Node::ImmutableMash)
+ end
+
it "should allow you get get an attribute via method_missing" do
Chef::Config[:treat_deprecation_warnings_as_errors] = false
node.default.sunshine = "is bright"
@@ -756,9 +784,9 @@ describe Chef::Node do
# In Chef-12.0 there is a deep_merge cache on the top level attribute which had a bug
# where it cached node[:foo] separate from node['foo']. These tests exercise those edge conditions.
#
- # https://github.com/opscode/chef/issues/2700
- # https://github.com/opscode/chef/issues/2712
- # https://github.com/opscode/chef/issues/2745
+ # https://github.com/chef/chef/issues/2700
+ # https://github.com/chef/chef/issues/2712
+ # https://github.com/chef/chef/issues/2745
#
describe "deep merge attribute cache edge conditions" do
it "does not error with complicated attribute substitution" do
@@ -1681,4 +1709,107 @@ describe Chef::Node do
end
end
+ describe "path tracking via __path__" do
+ it "works through hash keys" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ expect(node["foo"]["bar"].__path__).to eql(%w{foo bar})
+ end
+
+ it "works through the default level" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ expect(node.default["foo"]["bar"].__path__).to eql(%w{foo bar})
+ end
+
+ it "works through arrays" do
+ node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ]
+ expect(node["foo"][0].__path__).to eql(["foo", 0])
+ expect(node["foo"][0]["bar"].__path__).to eql(["foo", 0, "bar"])
+ end
+
+ it "works through arrays at the default level" do
+ node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ]
+ expect(node.default["foo"][0].__path__).to eql(["foo", 0])
+ expect(node.default["foo"][0]["bar"].__path__).to eql(["foo", 0, "bar"])
+ end
+
+ # if we set __path__ in the initializer we'd get this wrong, this is why we
+ # update the path on every #[] or #[]= operator
+ it "works on access when the node has been rearranged" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ a = node.default["foo"]
+ node.default["fizz"] = a
+ expect(node["fizz"]["bar"].__path__).to eql(%w{fizz bar})
+ expect(node["foo"]["bar"].__path__).to eql(%w{foo bar})
+ end
+
+ # We have a problem because the __path__ is stored on in each node, but the
+ # node can be wired up at multiple locations in the tree via pointers. One
+ # solution would be to deep-dup the value in `#[]=(key, value)` and fix the
+ # __path__ on all the dup'd nodes. The problem is that this would create an
+ # unusual situation where after assignment, you couldn't mutate the thing you
+ # hand a handle on. I'm not entirely positive this behavior is the correct
+ # thing to support, but it is more hash-like (although if we start with a hash
+ # then convert_value does its thing and we *do* get dup'd on assignment). This
+ # behavior likely makes any implementation of a deep merge cache built over the
+ # top of __path__ tracking have edge conditions where it will fail.
+ #
+ # Removing this support would be a breaking change. The test is included here
+ # because it seems most likely that someone would break this behavior while trying
+ # to fix __path__ behavior.
+ it "does not dup in the background when a node is assigned" do
+ # get a handle on a vividmash (can't be a hash or else we convert_value it)
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ a = node.default["foo"]
+ # assign that somewhere else in the tree
+ node.default["fizz"] = a
+ # now upate the source
+ a["duptest"] = true
+ # the tree should have been updated
+ expect(node.default["fizz"]["duptest"]).to be true
+ expect(node["fizz"]["duptest"]).to be true
+ end
+ end
+
+ describe "root tracking via __root__" do
+ it "works through hash keys" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ expect(node["foo"]["bar"].__root__).to eql(node.attributes)
+ end
+
+ it "works through the default level" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ expect(node.default["foo"]["bar"].__root__).to eql(node.attributes)
+ end
+
+ it "works through arrays" do
+ node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ]
+ expect(node["foo"][0].__root__).to eql(node.attributes)
+ expect(node["foo"][0]["bar"].__root__).to eql(node.attributes)
+ end
+
+ it "works through arrays at the default level" do
+ node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ]
+ expect(node.default["foo"][0].__root__).to eql(node.attributes)
+ expect(node.default["foo"][0]["bar"].__root__).to eql(node.attributes)
+ end
+ end
+
+ describe "ways of abusing Chef 12 node state" do
+ # these tests abuse the top_level_breadcrumb state in Chef 12
+ it "derived attributes work correctly" do
+ node.default["v1"] = 1
+ expect(node["a"]).to eql(nil)
+ node.default["a"] = node["v1"]
+ expect(node["a"]).to eql(1)
+ end
+
+ it "works when saving nodes to variables" do
+ a = node.default["a"]
+ expect(node["a"]).to eql({})
+ node.default["b"] = 0
+ a["key"] = 1
+
+ expect(node["a"]["key"]).to eql(1)
+ end
+ end
end
diff --git a/spec/unit/provider/deploy_spec.rb b/spec/unit/provider/deploy_spec.rb
index e69714280c..b30ddb736a 100644
--- a/spec/unit/provider/deploy_spec.rb
+++ b/spec/unit/provider/deploy_spec.rb
@@ -556,7 +556,7 @@ describe Chef::Provider::Deploy do
@resource.deploy_to("/my/app")
expect(mock_execution).to receive(:user).with("notCoolMan")
expect(mock_execution).to receive(:group).with("Ggroup")
- expect(mock_execution).to receive(:cwd) {|*args|
+ expect(mock_execution).to receive(:cwd) { |*args|
if args.empty?
nil
else
diff --git a/spec/unit/provider/group/suse_spec.rb b/spec/unit/provider/group/suse_spec.rb
new file mode 100644
index 0000000000..da4e8e9155
--- /dev/null
+++ b/spec/unit/provider/group/suse_spec.rb
@@ -0,0 +1,90 @@
+#
+# Author:: Tom Duffield (<tom@chef.io>)
+# Copyright:: Copyright 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Provider::Group::Suse do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_members) { %w{root new_user} }
+ let(:new_resource) do
+ Chef::Resource::Group.new("new_group").tap do |r|
+ r.gid 50
+ r.members new_members
+ r.system false
+ r.non_unique false
+ end
+ end
+ let(:current_resource) do
+ Chef::Resource::Group.new("new_group").tap do |r|
+ r.gid 50
+ r.members %w{root}
+ r.system false
+ r.non_unique false
+ end
+ end
+ let(:provider) do
+ described_class.new(new_resource, run_context).tap do |p|
+ p.current_resource = current_resource
+ end
+ end
+
+ describe "when determining the current group state" do
+ before(:each) do
+ allow(File).to receive(:exists?).and_return(true)
+ provider.action = :create
+ provider.define_resource_requirements
+ end
+
+ # Checking for required binaries is already done in the spec
+ # for Chef::Provider::Group - no need to repeat it here. We'll
+ # include only what's specific to this provider.
+ it "should raise an error if the required binary /usr/sbin/groupmod doesn't exist" do
+ expect(File).to receive(:exists?).with("/usr/sbin/groupmod").and_return(false)
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
+ end
+
+ it "should raise error if one of the member users does not exist" do
+ expect(Etc).to receive(:getpwnam).with("new_user").and_raise ArgumentError
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
+ end
+ end
+
+ describe "#set_members" do
+ it "should add missing members and remove deleted members" do
+ expect(provider).not_to receive(:remove_member)
+ expect(provider).to receive(:add_member).with("new_user")
+ provider.set_members(new_members)
+ end
+ end
+
+ describe "#add_member" do
+ it "should call out to groupmod to add user" do
+ expect(provider).to receive(:shell_out!).with("groupmod -A new_user new_group")
+ provider.add_member("new_user")
+ end
+ end
+
+ describe "#remove_member" do
+ it "should call out to groupmod to remove user" do
+ expect(provider).to receive(:shell_out!).with("groupmod -R new_user new_group")
+ provider.remove_member("new_user")
+ end
+ end
+end
diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb
index a549ecc72e..cf31c3d7cc 100644
--- a/spec/unit/provider/package/apt_spec.rb
+++ b/spec/unit/provider/package/apt_spec.rb
@@ -395,6 +395,28 @@ mpg123 1.12.1-0ubuntu1
end
end
+ describe "when locking a package" do
+ it "should run apt-mark hold package" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-mark hold irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.lock_package("irssi", "0.8.12-7")
+ end
+ end
+
+ describe "when unlocking a package" do
+ it "should run apt-mark unhold package" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-mark unhold irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.unlock_package("irssi", "0.8.12-7")
+ end
+ end
+
describe "when installing a virtual package" do
it "should install the package without specifying a version" do
@provider.package_data["libmysqlclient15-dev"][:virtual] = true
diff --git a/spec/unit/provider/package/windows/msi_spec.rb b/spec/unit/provider/package/windows/msi_spec.rb
index e29508ca7b..c8099c38d0 100644
--- a/spec/unit/provider/package/windows/msi_spec.rb
+++ b/spec/unit/provider/package/windows/msi_spec.rb
@@ -121,7 +121,7 @@ describe Chef::Provider::Package::Windows::MSI do
end
it "removes installed package" do
- expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid} \/q/, kind_of(Hash))
provider.remove_package
end
@@ -140,8 +140,8 @@ describe Chef::Provider::Package::Windows::MSI do
end
it "removes both installed package" do
- expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
- expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid2} \/Q/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid} \/q/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid2} \/q/, kind_of(Hash))
provider.remove_package
end
end
@@ -150,7 +150,16 @@ describe Chef::Provider::Package::Windows::MSI do
before { new_resource.options("/Q") }
it "does not duplicate quiet switch" do
- expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid} \/Q/, kind_of(Hash))
+ provider.remove_package
+ end
+ end
+
+ context "custom options includes /qn" do
+ before { new_resource.options("/qn") }
+
+ it "does not duplicate quiet switch" do
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid} \/qn/, kind_of(Hash))
provider.remove_package
end
end
diff --git a/spec/unit/provider/package/windows_spec.rb b/spec/unit/provider/package/windows_spec.rb
index d1d717bdbe..53cbbc1da1 100644
--- a/spec/unit/provider/package/windows_spec.rb
+++ b/spec/unit/provider/package/windows_spec.rb
@@ -26,9 +26,9 @@ describe Chef::Provider::Package::Windows, :windows_only do
allow(Chef::FileCache).to receive(:create_cache_path).with("package/").and_return(cache_path)
end
- let(:node) { double("Chef::Node") }
- let(:events) { double("Chef::Events").as_null_object } # mock all the methods
- let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:resource_source) { "calculator.msi" }
let(:resource_name) { "calculator" }
let(:installer_type) { nil }
diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb
index e9aec933e2..23a28c6d37 100644
--- a/spec/unit/provider/package/yum_spec.rb
+++ b/spec/unit/provider/package/yum_spec.rb
@@ -785,6 +785,24 @@ describe Chef::Provider::Package::Yum do
end
end
+ describe "when locking a package" do
+ it "should run yum versionlock add with the package name" do
+ expect(@provider).to receive(:yum_command).with(
+ "-d0 -e0 -y versionlock add emacs"
+ )
+ @provider.lock_package("emacs", nil)
+ end
+ end
+
+ describe "when unlocking a package" do
+ it "should run yum versionlock delete with the package name" do
+ expect(@provider).to receive(:yum_command).with(
+ "-d0 -e0 -y versionlock delete emacs"
+ )
+ @provider.unlock_package("emacs", nil)
+ end
+ end
+
describe "when running yum" do
it "should run yum once if it exits with a return code of 0" do
@status = double("Status", :exitstatus => 0, :stdout => "", :stderr => "")
diff --git a/spec/unit/provider/package/zypper_spec.rb b/spec/unit/provider/package/zypper_spec.rb
index 8838c26b71..d2800575bc 100644
--- a/spec/unit/provider/package/zypper_spec.rb
+++ b/spec/unit/provider/package/zypper_spec.rb
@@ -231,6 +231,70 @@ describe Chef::Provider::Package::Zypper do
end
end
+ describe "lock_package" do
+ it "should run zypper addlock with the package name" do
+ allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
+ shell_out_expectation!(
+ "zypper --non-interactive addlock emacs"
+ )
+ provider.lock_package(["emacs"], [nil])
+ end
+ it "should run zypper addlock without gpg checks" do
+ allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks addlock emacs"
+ )
+ provider.lock_package(["emacs"], [nil])
+ end
+ it "should warn about gpg checks on zypper addlock" do
+ expect(Chef::Log).to receive(:warn).with(
+ /All packages will be installed without gpg signature checks/
+ )
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks addlock emacs"
+ )
+ provider.lock_package(["emacs"], [nil])
+ end
+ it "should run zypper addlock without gpg checks" do
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks addlock emacs"
+ )
+ provider.lock_package(["emacs"], [nil])
+ end
+ end
+
+ describe "unlock_package" do
+ it "should run zypper removelock with the package name" do
+ allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
+ shell_out_expectation!(
+ "zypper --non-interactive removelock emacs"
+ )
+ provider.unlock_package(["emacs"], [nil])
+ end
+ it "should run zypper removelock without gpg checks" do
+ allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks removelock emacs"
+ )
+ provider.unlock_package(["emacs"], [nil])
+ end
+ it "should warn about gpg checks on zypper removelock" do
+ expect(Chef::Log).to receive(:warn).with(
+ /All packages will be installed without gpg signature checks/
+ )
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks removelock emacs"
+ )
+ provider.unlock_package(["emacs"], [nil])
+ end
+ it "should run zypper removelock without gpg checks" do
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks removelock emacs"
+ )
+ provider.unlock_package(["emacs"], [nil])
+ end
+ end
+
describe "on an older zypper" do
before(:each) do
allow(provider).to receive(:`).and_return("0.11.6")
diff --git a/spec/unit/provider/package_spec.rb b/spec/unit/provider/package_spec.rb
index 40b7516b5c..27d28c698c 100644
--- a/spec/unit/provider/package_spec.rb
+++ b/spec/unit/provider/package_spec.rb
@@ -321,6 +321,56 @@ describe Chef::Provider::Package do
end
end
+ describe "When locking the package" do
+ before(:each) do
+ allow(provider).to receive(:lock_package).and_return(true)
+ end
+
+ it "should lock the package if it is unlocked" do
+ allow(provider).to receive(:package_locked).and_return(false)
+ expect(provider).to receive(:lock_package)
+ provider.run_action(:lock)
+ end
+
+ it "should not lock the package if it is already locked" do
+ allow(provider).to receive(:package_locked).and_return(true)
+ expect(provider).not_to receive(:lock_package)
+ provider.run_action(:lock)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "should set the resource to updated if it locks the package" do
+ allow(provider).to receive(:package_locked).and_return(false)
+ provider.run_action(:lock)
+ expect(new_resource).to be_updated
+ end
+ end
+
+ describe "When unlocking the package" do
+ before(:each) do
+ allow(provider).to receive(:unlock_package).and_return(true)
+ end
+
+ it "should unlock the package if it is locked" do
+ allow(provider).to receive(:package_locked).and_return(true)
+ expect(provider).to receive(:unlock_package)
+ provider.run_action(:unlock)
+ end
+
+ it "should not unlock the package if it is already unlocked" do
+ allow(provider).to receive(:package_locked).and_return(false)
+ expect(provider).not_to receive(:unlock_package)
+ provider.run_action(:unlock)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "should set the resource to updated if it unlocks the package" do
+ allow(provider).to receive(:package_locked).and_return(true)
+ provider.run_action(:unlock)
+ expect(new_resource).to be_updated
+ end
+ end
+
describe "when running commands to be implemented by subclasses" do
it "should raises UnsupportedAction for install" do
expect { provider.install_package("emacs", "1.4.2") }.to raise_error(Chef::Exceptions::UnsupportedAction)
@@ -346,6 +396,14 @@ describe Chef::Provider::Package do
it "should raise UnsupportedAction for reconfig" do
expect { provider.reconfig_package("emacs", "1.4.2") }.to raise_error(Chef::Exceptions::UnsupportedAction)
end
+
+ it "should raise UnsupportedAction for lock" do
+ expect { provider.lock_package("emacs", nil) }.to raise_error(Chef::Exceptions::UnsupportedAction)
+ end
+
+ it "should raise UnsupportedAction for unlock" do
+ expect { provider.unlock_package("emacs", nil) }.to raise_error(Chef::Exceptions::UnsupportedAction)
+ end
end
describe "when given a response file" do
diff --git a/spec/unit/provider/remote_file/content_spec.rb b/spec/unit/provider/remote_file/content_spec.rb
index db9a75458d..307eb98187 100644
--- a/spec/unit/provider/remote_file/content_spec.rb
+++ b/spec/unit/provider/remote_file/content_spec.rb
@@ -159,7 +159,7 @@ describe Chef::Provider::RemoteFile::Content do
describe "when there is an array of sources and the first fails" do
- # https://github.com/opscode/chef/pull/1358#issuecomment-40853299
+ # https://github.com/chef/chef/pull/1358#issuecomment-40853299
def create_exception(exception_class)
if [ Net::HTTPServerException, Net::HTTPFatalError ].include? exception_class
exception_class.new("message", { "something" => 1 })
diff --git a/spec/unit/provider/user/solaris_spec.rb b/spec/unit/provider/user/solaris_spec.rb
index 8a5e654a0d..1935336308 100644
--- a/spec/unit/provider/user/solaris_spec.rb
+++ b/spec/unit/provider/user/solaris_spec.rb
@@ -99,20 +99,43 @@ describe Chef::Provider::User::Solaris do
end
describe "when managing user locked status" do
+ let(:user_lock) { "adam:FOO:::::::" }
+ let(:shadow_file_contents) do
+ %W{
+ user1:LK:::::::
+ #{user_lock}
+ user2:NP:::::::
+ }
+ end
+
describe "when determining if the user is locked" do
+ before do
+ allow(IO).to receive(:read).and_return(shadow_file_contents.join("\n"))
+ end
+
+ context "when user does not exist" do
+ let(:user_lock) { "other_user:FOO:::::::" }
+
+ it "should raise a sensible error" do
+ expect { provider.check_lock }.to raise_error(Chef::Exceptions::User)
+ end
+ end
# locked shadow lines
[
- "adam:LK:::::::",
"adam:*LK*:::::::",
"adam:*LK*foobar:::::::",
"adam:*LK*bahamas10:::::::",
+ "adam:*LK*goonawaLK:::::::",
+ "adam:*LK*LKgir:::::::",
"adam:*LK*L....:::::::",
].each do |shadow|
- it "should return true if user is locked with #{shadow}" do
- shell_return = shellcmdresult.new(shadow + "\n", "", 0)
- expect(provider).to receive(:shell_out!).with("getent", "shadow", "adam").and_return(shell_return)
- expect(provider.check_lock).to eql(true)
+ context "for user 'adam' with entry '#{shadow}'" do
+ let(:user_lock) { shadow }
+
+ it "should return true" do
+ expect(provider.check_lock).to eql(true)
+ end
end
end
@@ -122,12 +145,16 @@ describe Chef::Provider::User::Solaris do
"adam:*NP*:::::::",
"adam:foobar:::::::",
"adam:bahamas10:::::::",
+ "adam:goonawaLK:::::::",
+ "adam:LKgir:::::::",
"adam:L...:::::::",
].each do |shadow|
- it "should return false if user is unlocked with #{shadow}" do
- shell_return = shellcmdresult.new(shadow + "\n", "", 0)
- expect(provider).to receive(:shell_out!).with("getent", "shadow", "adam").and_return(shell_return)
- expect(provider.check_lock).to eql(false)
+ context "for user 'adam' with entry '#{shadow}'" do
+ let(:user_lock) { shadow }
+
+ it "should return false" do
+ expect(provider.check_lock).to eql(false)
+ end
end
end
end
diff --git a/spec/unit/provider/user_spec.rb b/spec/unit/provider/user_spec.rb
index 1a8ad6ad1b..719dc8d492 100644
--- a/spec/unit/provider/user_spec.rb
+++ b/spec/unit/provider/user_spec.rb
@@ -221,6 +221,12 @@ describe Chef::Provider::User do
it "should return false if the objects are identical" do
expect(@provider.compare_user).to eql(false)
end
+
+ it "should ignore differences in trailing slash in home paths" do
+ @new_resource.home "/home/adam"
+ @current_resource.home "/home/adam/"
+ expect(@provider.compare_user).to eql(false)
+ end
end
describe "action_create" do
diff --git a/spec/unit/resource/apt_repository_spec.rb b/spec/unit/resource/apt_repository_spec.rb
index 0b0c0c5d26..69cf94ae56 100644
--- a/spec/unit/resource/apt_repository_spec.rb
+++ b/spec/unit/resource/apt_repository_spec.rb
@@ -24,7 +24,7 @@ describe Chef::Resource::AptRepository do
let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:resource) { Chef::Resource::AptRepository.new("multiverse", run_context) }
- it "should create a new Chef::Resource::AptUpdate" do
+ it "should create a new Chef::Resource::AptRepository" do
expect(resource).to be_a_kind_of(Chef::Resource)
expect(resource).to be_a_kind_of(Chef::Resource::AptRepository)
end