summaryrefslogtreecommitdiff
path: root/chef-utils/spec
diff options
context:
space:
mode:
Diffstat (limited to 'chef-utils/spec')
-rw-r--r--chef-utils/spec/spec_helper.rb93
-rw-r--r--chef-utils/spec/unit/dsl/architecture_spec.rb139
-rw-r--r--chef-utils/spec/unit/dsl/dsl_spec.rb33
-rw-r--r--chef-utils/spec/unit/dsl/introspection_spec.rb168
-rw-r--r--chef-utils/spec/unit/dsl/os_spec.rb174
-rw-r--r--chef-utils/spec/unit/dsl/path_sanity_spec.rb85
-rw-r--r--chef-utils/spec/unit/dsl/platform_family_spec.rb192
-rw-r--r--chef-utils/spec/unit/dsl/platform_spec.rb234
-rw-r--r--chef-utils/spec/unit/dsl/service_spec.rb116
-rw-r--r--chef-utils/spec/unit/dsl/which_spec.rb168
-rw-r--r--chef-utils/spec/unit/mash_spec.rb50
11 files changed, 1452 insertions, 0 deletions
diff --git a/chef-utils/spec/spec_helper.rb b/chef-utils/spec/spec_helper.rb
new file mode 100644
index 0000000000..20d49fd766
--- /dev/null
+++ b/chef-utils/spec/spec_helper.rb
@@ -0,0 +1,93 @@
+require "chef-utils"
+
+# FIXME: dynamically generate this for accuracy
+HELPER_MODULES = [
+ ChefUtils::DSL::Architecture,
+ ChefUtils::DSL::Introspection,
+ ChefUtils::DSL::OS,
+ ChefUtils::DSL::PathSanity,
+ ChefUtils::DSL::Platform,
+ ChefUtils::DSL::PlatformFamily,
+ ChefUtils::DSL::Service,
+ ChefUtils::DSL::Which,
+].freeze
+
+ARCH_HELPERS = (ChefUtils::DSL::Architecture.methods - Module.methods).freeze
+OS_HELPERS = (ChefUtils::DSL::OS.methods - Module.methods).freeze
+PLATFORM_HELPERS = (ChefUtils::DSL::Platform.methods - Module.methods).freeze
+PLATFORM_FAMILY_HELPERS = (ChefUtils::DSL::PlatformFamily.methods - Module.methods).freeze
+INTROSPECTION_HELPERS = (ChefUtils::DSL::Introspection.methods - Module.methods).freeze
+
+# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+RSpec.configure do |config|
+ # rspec-expectations config goes here. You can use an alternate
+ # assertion/expectation library such as wrong or the stdlib/minitest
+ # assertions if you prefer.
+ config.expect_with :rspec do |expectations|
+ # This option will default to `true` in RSpec 4. It makes the `description`
+ # and `failure_message` of custom matchers include text for helper methods
+ # defined using `chain`, e.g.:
+ # be_bigger_than(2).and_smaller_than(4).description
+ # # => "be bigger than 2 and smaller than 4"
+ # ...rather than:
+ # # => "be bigger than 2"
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ # rspec-mocks config goes here. You can use an alternate test double
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
+ config.mock_with :rspec do |mocks|
+ # Prevents you from mocking or stubbing a method that does not exist on
+ # a real object. This is generally recommended, and will default to
+ # `true` in RSpec 4.
+ mocks.verify_partial_doubles = true
+ end
+
+ # These two settings work together to allow you to limit a spec run
+ # to individual examples or groups you care about by tagging them with
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
+ # get run.
+ config.filter_run :focus
+ config.run_all_when_everything_filtered = true
+
+ config.filter_run_excluding windows_only: true unless ChefUtils.windows?
+ config.filter_run_excluding unix_only: true if ChefUtils.windows?
+
+ # Limits the available syntax to the non-monkey patched syntax that is
+ # recommended. For more details, see:
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
+ config.disable_monkey_patching!
+
+ # This setting enables warnings. It's recommended, but in some cases may
+ # be too noisy due to issues in dependencies.
+ config.warnings = true
+
+ # Many RSpec users commonly either run the entire suite or an individual
+ # file, and it's useful to allow more verbose output when running an
+ # individual spec file.
+ if config.files_to_run.one?
+ # Use the documentation formatter for detailed output,
+ # unless a formatter has already been configured
+ # (e.g. via a command-line flag).
+ config.default_formatter = "doc"
+ end
+
+ # Print the 10 slowest examples and example groups at the
+ # end of the spec run, to help surface which specs are running
+ # particularly slow.
+ # config.profile_examples = 10
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ config.order = :random
+
+ # Seed global randomization in this process using the `--seed` CLI option.
+ # Setting this allows you to use `--seed` to deterministically reproduce
+ # test failures related to randomization by passing the same `--seed` value
+ # as the one that triggered the failure.
+ Kernel.srand config.seed
+end
diff --git a/chef-utils/spec/unit/dsl/architecture_spec.rb b/chef-utils/spec/unit/dsl/architecture_spec.rb
new file mode 100644
index 0000000000..a2ce300fe0
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/architecture_spec.rb
@@ -0,0 +1,139 @@
+#
+# Copyright:: Copyright 2018-2019, 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"
+
+def arch_reports_true_for(*args)
+ args.each do |method|
+ it "reports true for #{method}" do
+ expect(described_class.send(method, node)).to be true
+ end
+ end
+ (ARCH_HELPERS - args).each do |method|
+ it "reports false for #{method}" do
+ expect(described_class.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::Architecture do
+ let(:node) { { "kernel" => { "machine" => arch } } }
+
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & ARCH_HELPERS).to be_empty
+ end
+ end
+
+ ARCH_HELPERS.each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+
+ context "on x86_64" do
+ let(:arch) { "x86_64" }
+
+ arch_reports_true_for(:intel?, :_64_bit?)
+ end
+
+ context "on amd64" do
+ let(:arch) { "amd64" }
+
+ arch_reports_true_for(:intel?, :_64_bit?)
+ end
+ context "on ppc64" do
+ let(:arch) { "ppc64" }
+
+ arch_reports_true_for(:ppc64?, :_64_bit?)
+ end
+ context "on ppc64le" do
+ let(:arch) { "ppc64le" }
+
+ arch_reports_true_for(:ppc64le?, :_64_bit?)
+ end
+ context "on s390x" do
+ let(:arch) { "s390x" }
+
+ arch_reports_true_for(:s390x?, :_64_bit?)
+ end
+ context "on ia64" do
+ let(:arch) { "ia64" }
+
+ arch_reports_true_for(:_64_bit?)
+ end
+ context "on sparc64" do
+ let(:arch) { "sparc64" }
+
+ arch_reports_true_for(:_64_bit?)
+ end
+ context "on aarch64" do
+ let(:arch) { "aarch64" }
+
+ arch_reports_true_for(:_64_bit?)
+ end
+ context "on arch64" do
+ let(:arch) { "arch64" }
+
+ arch_reports_true_for(:_64_bit?)
+ end
+ context "on arm64" do
+ let(:arch) { "arm64" }
+
+ arch_reports_true_for(:_64_bit?)
+ end
+ context "on sun4v" do
+ let(:arch) { "sun4v" }
+
+ arch_reports_true_for(:sparc?, :_64_bit?)
+ end
+ context "on sun4u" do
+ let(:arch) { "sun4u" }
+
+ arch_reports_true_for(:sparc?, :_64_bit?)
+ end
+ context "on i86pc" do
+ let(:arch) { "i86pc" }
+
+ arch_reports_true_for(:i386?, :intel?, :_32_bit?)
+ end
+ context "on i386" do
+ let(:arch) { "i386" }
+
+ arch_reports_true_for(:i386?, :intel?, :_32_bit?)
+ end
+ context "on i686" do
+ let(:arch) { "i686" }
+
+ arch_reports_true_for(:i386?, :intel?, :_32_bit?)
+ end
+ context "on powerpc" do
+ let(:arch) { "powerpc" }
+
+ arch_reports_true_for(:powerpc?, :_32_bit?)
+ end
+ context "on armhf" do
+ let(:arch) { "armhf" }
+
+ arch_reports_true_for(:armhf?, :_32_bit?)
+ end
+ context "on s390" do
+ let(:arch) { "s390" }
+
+ arch_reports_true_for(:s390?, :_32_bit?)
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/dsl_spec.rb b/chef-utils/spec/unit/dsl/dsl_spec.rb
new file mode 100644
index 0000000000..f05be11d4a
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/dsl_spec.rb
@@ -0,0 +1,33 @@
+#
+# Copyright:: Copyright 2018-2019, 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"
+
+RSpec.describe ChefUtils do
+ class ThingWithDSL
+ extend ChefUtils
+ end
+
+ (OS_HELPERS + ARCH_HELPERS + PLATFORM_HELPERS + PLATFORM_FAMILY_HELPERS + INTROSPECTION_HELPERS).each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ThingWithDSL).to respond_to(helper)
+ end
+ it "has the #{helper} class method in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/introspection_spec.rb b/chef-utils/spec/unit/dsl/introspection_spec.rb
new file mode 100644
index 0000000000..d45a3c6000
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/introspection_spec.rb
@@ -0,0 +1,168 @@
+#
+# Copyright:: Copyright 2018-2019, 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"
+
+RSpec.describe ChefUtils::DSL::Introspection do
+ class IntrospectionTestClass
+ include ChefUtils::DSL::Introspection
+ attr_accessor :node
+ def initialize(node)
+ @node = node
+ end
+ end
+
+ let(:node) { double("node") }
+
+ let(:test_instance) { IntrospectionTestClass.new(node) }
+
+ context "#docker?" do
+ # FIXME: use a real VividMash for these tests insted of stubbing
+ it "is false by default" do
+ expect(node).to receive(:read).with("virtualization", "systems", "docker").and_return(nil)
+ expect(ChefUtils.docker?(node)).to be false
+ end
+ it "is true when ohai reports a docker guest" do
+ expect(node).to receive(:read).with("virtualization", "systems", "docker").and_return("guest")
+ expect(ChefUtils.docker?(node)).to be true
+ end
+ it "is false for any other value other than guest" do
+ expect(node).to receive(:read).with("virtualization", "systems", "docker").and_return("some nonsense")
+ expect(ChefUtils.docker?(node)).to be false
+ end
+ end
+
+ context "#systemd?" do
+ # FIXME: somehow test the train helpers
+ it "returns false if /proc/1/comm does not exist" do
+ expect(File).to receive(:exist?).with("/proc/1/comm").and_return(false)
+ expect(ChefUtils.systemd?(node)).to be false
+ end
+
+ it "returns false if /proc/1/comm is not systemd" do
+ expect(File).to receive(:exist?).with("/proc/1/comm").and_return(true)
+ expect(File).to receive(:open).with("/proc/1/comm").and_return(StringIO.new("upstart\n"))
+ expect(ChefUtils.systemd?(node)).to be false
+ end
+
+ it "returns true if /proc/1/comm is systemd" do
+ expect(File).to receive(:exist?).with("/proc/1/comm").and_return(true)
+ expect(File).to receive(:open).with("/proc/1/comm").and_return(StringIO.new("systemd\n"))
+ expect(ChefUtils.systemd?(node)).to be true
+ end
+ end
+
+ context "#kitchen?" do
+ before do
+ @saved = ENV["TEST_KITCHEN"]
+ end
+ after do
+ ENV["TEST_KITCHEN"] = @saved
+ end
+
+ it "return true if ENV['TEST_KITCHEN'] is not set" do
+ ENV.delete("TEST_KITCHEN")
+ expect(ChefUtils.kitchen?(node)).to be false
+ end
+
+ it "return true if ENV['TEST_KITCHEN'] is nil" do
+ ENV["TEST_KITCHEN"] = nil
+ expect(ChefUtils.kitchen?(node)).to be false
+ end
+
+ it "return true if ENV['TEST_KITCHEN'] is set" do
+ ENV["TEST_KITCHEN"] = "1"
+ expect(ChefUtils.kitchen?(node)).to be true
+ end
+ end
+
+ context "#ci?" do
+ before do
+ @saved = ENV["CI"]
+ end
+ after do
+ ENV["CI"] = @saved
+ end
+
+ it "return true if ENV['CI'] is not set" do
+ ENV.delete("CI")
+ expect(ChefUtils.ci?(node)).to be false
+ end
+
+ it "return true if ENV['CI'] is nil" do
+ ENV["CI"] = nil
+ expect(ChefUtils.ci?(node)).to be false
+ end
+
+ it "return true if ENV['CI'] is set" do
+ ENV["CI"] = "1"
+ expect(ChefUtils.ci?(node)).to be true
+ end
+ end
+
+ context "#has_systemd_service_unit?" do
+ # FIXME: test through train helpers
+
+ before do
+ %w{ /etc /usr/lib /lib /run }.each do |base|
+ allow(File).to receive(:exist?).with("#{base}/systemd/system/example.service").and_return(false)
+ allow(File).to receive(:exist?).with("#{base}/systemd/system/example@.service").and_return(false)
+ end
+ end
+
+ it "is false if no unit is present" do
+ expect(ChefUtils.has_systemd_service_unit?("example")).to be false
+ end
+
+ it "is false if no template is present" do
+ expect(ChefUtils.has_systemd_service_unit?("example@instance1")).to be false
+ end
+
+ %w{ /etc /usr/lib /lib /run }.each do |base|
+ it "finds a unit in #{base}" do
+ expect(File).to receive(:exist?).with("#{base}/systemd/system/example.service").and_return(true)
+ expect(ChefUtils.has_systemd_service_unit?("example")).to be true
+ end
+
+ it "finds a template in #{base}" do
+ expect(File).to receive(:exist?).with("#{base}/systemd/system/example@.service").and_return(true)
+ expect(ChefUtils.has_systemd_service_unit?("example@instance1")).to be true
+ end
+ end
+ end
+
+ context "#has_systemd_unit?" do
+ # FIXME: test through train helpers
+
+ before do
+ %w{ /etc /usr/lib /lib /run }.each do |base|
+ allow(File).to receive(:exist?).with("#{base}/systemd/system/example.mount").and_return(false)
+ end
+ end
+
+ it "is false if no unit is present" do
+ expect(ChefUtils.has_systemd_unit?("example.mount")).to be false
+ end
+
+ %w{ /etc /usr/lib /lib /run }.each do |base|
+ it "finds a unit in #{base}" do
+ expect(File).to receive(:exist?).with("#{base}/systemd/system/example.mount").and_return(true)
+ expect(ChefUtils.has_systemd_unit?("example.mount")).to be true
+ end
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/os_spec.rb b/chef-utils/spec/unit/dsl/os_spec.rb
new file mode 100644
index 0000000000..d5d8c7d89e
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/os_spec.rb
@@ -0,0 +1,174 @@
+#
+# Copyright:: Copyright 2018-2019, 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"
+require "fauxhai"
+
+def os_reports_true_for(*args)
+ args.each do |method|
+ it "reports true for #{method}" do
+ expect(described_class.send(method, node)).to be true
+ end
+ end
+ (OS_HELPERS - args).each do |method|
+ it "reports false for #{method}" do
+ expect(described_class.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::OS do
+ let(:node) { Fauxhai.mock(options).data }
+
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & OS_HELPERS).to be_empty
+ end
+ end
+
+ OS_HELPERS.each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+
+ context "on ubuntu" do
+ let(:options) { { platform: "ubuntu" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on raspbian" do
+ let(:options) { { platform: "raspbian" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on linuxmint" do
+ let(:options) { { platform: "linuxmint" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on debian" do
+ let(:options) { { platform: "debian" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on amazon" do
+ let(:options) { { platform: "amazon" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on arch" do
+ let(:options) { { platform: "arch" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on centos" do
+ let(:options) { { platform: "centos" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on clearos" do
+ let(:options) { { platform: "clearos" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on dragonfly4" do
+ let(:options) { { platform: "dragonfly4" } }
+
+ os_reports_true_for
+ end
+
+ context "on fedora" do
+ let(:options) { { platform: "fedora" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on freebsd" do
+ let(:options) { { platform: "freebsd" } }
+
+ os_reports_true_for
+ end
+
+ context "on gentoo" do
+ let(:options) { { platform: "gentoo" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on mac_os_x" do
+ let(:options) { { platform: "mac_os_x" } }
+
+ os_reports_true_for(:darwin?)
+ end
+
+ context "on openbsd" do
+ let(:options) { { platform: "openbsd" } }
+
+ os_reports_true_for
+ end
+
+ context "on opensuse" do
+ let(:options) { { platform: "opensuse" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on oracle" do
+ let(:options) { { platform: "oracle" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on redhat" do
+ let(:options) { { platform: "redhat" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on smartos" do
+ let(:options) { { platform: "smartos" } }
+
+ os_reports_true_for
+ end
+
+ context "on solaris2" do
+ let(:options) { { platform: "solaris2" } }
+
+ os_reports_true_for
+ end
+
+ context "on suse" do
+ let(:options) { { platform: "suse" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on windows" do
+ let(:options) { { platform: "windows" } }
+
+ os_reports_true_for
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/path_sanity_spec.rb b/chef-utils/spec/unit/dsl/path_sanity_spec.rb
new file mode 100644
index 0000000000..dc841f296b
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/path_sanity_spec.rb
@@ -0,0 +1,85 @@
+#
+# Copyright:: Copyright 2018-2019, 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"
+
+RSpec.describe ChefUtils::DSL::PathSanity do
+ class PathSanityTestClass
+ include ChefUtils::DSL::PathSanity
+ end
+
+ before do
+ allow(Gem).to receive(:bindir).and_return("/opt/ruby/bin/bundle")
+ allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return("/opt/ruby/bin")
+ end
+
+ context "on unix" do
+ before do
+ allow(ChefUtils).to receive(:windows?).and_return(false)
+ end
+
+ let(:test_instance) { PathSanityTestClass.new }
+
+ it "works with no path" do
+ env = {}
+ expect(test_instance.sanitized_path(env)).to eql("#{Gem.bindir}:#{RbConfig::CONFIG["bindir"]}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ end
+
+ it "works with nil path" do
+ env = { "PATH" => nil }
+ expect(test_instance.sanitized_path(env)).to eql("#{Gem.bindir}:#{RbConfig::CONFIG["bindir"]}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ end
+
+ it "works with empty path" do
+ env = { "PATH" => "" }
+ expect(test_instance.sanitized_path(env)).to eql("#{Gem.bindir}:#{RbConfig::CONFIG["bindir"]}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ end
+
+ it "appends the sane_paths to the end of the path, preserving any that already exist, in the same order" do
+ env = { "PATH" => "/bin:/opt/app/bin:/sbin" }
+ expect(test_instance.sanitized_path(env)).to eql("#{Gem.bindir}:#{RbConfig::CONFIG["bindir"]}:/bin:/opt/app/bin:/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin")
+ end
+ end
+
+ context "on windows" do
+ before do
+ allow(ChefUtils).to receive(:windows?).and_return(true)
+ end
+
+ let(:test_instance) { PathSanityTestClass.new }
+
+ it "works with no path" do
+ env = {}
+ expect(test_instance.sanitized_path(env)).to eql("#{Gem.bindir};#{RbConfig::CONFIG["bindir"]}")
+ end
+
+ it "works with nil path" do
+ env = { "PATH" => nil }
+ expect(test_instance.sanitized_path(env)).to eql("#{Gem.bindir};#{RbConfig::CONFIG["bindir"]}")
+ end
+
+ it "works with empty path" do
+ env = { "PATH" => "" }
+ expect(test_instance.sanitized_path(env)).to eql("#{Gem.bindir};#{RbConfig::CONFIG["bindir"]}")
+ end
+
+ it "prepends to an existing path" do
+ env = { "PATH" => '%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\\' }
+ expect(test_instance.sanitized_path(env)).to eql("#{Gem.bindir};#{RbConfig::CONFIG["bindir"]};%SystemRoot%\\system32;%SystemRoot%;%SystemRoot%\\System32\\Wbem;%SYSTEMROOT%\\System32\\WindowsPowerShell\\v1.0\\")
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/platform_family_spec.rb b/chef-utils/spec/unit/dsl/platform_family_spec.rb
new file mode 100644
index 0000000000..fa33deaf37
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/platform_family_spec.rb
@@ -0,0 +1,192 @@
+#
+# Copyright:: Copyright 2018-2019, 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"
+require "fauxhai"
+
+def pf_reports_true_for(*args)
+ args.each do |method|
+ it "reports true for #{method}" do
+ expect(described_class.send(method, node)).to be true
+ end
+ end
+ (PLATFORM_FAMILY_HELPERS - [ :windows_ruby_platform? ] - args).each do |method|
+ it "reports false for #{method}" do
+ expect(described_class.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::PlatformFamily do
+ let(:node) { Fauxhai.mock(options).data }
+
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & PLATFORM_FAMILY_HELPERS).to be_empty
+ end
+ end
+
+ ( PLATFORM_FAMILY_HELPERS - [ :windows_ruby_platform? ]).each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+
+ context "on ubuntu" do
+ let(:options) { { platform: "ubuntu" } }
+
+ pf_reports_true_for(:debian?)
+ end
+
+ context "on raspbian" do
+ let(:options) { { platform: "raspbian" } }
+
+ pf_reports_true_for(:debian?)
+ end
+
+ context "on linuxmint" do
+ let(:options) { { platform: "linuxmint" } }
+
+ pf_reports_true_for(:debian?)
+ end
+
+ context "on debian" do
+ let(:options) { { platform: "debian" } }
+
+ pf_reports_true_for(:debian?)
+ end
+
+ context "on aix" do
+ let(:options) { { platform: "aix" } }
+
+ pf_reports_true_for(:aix?)
+ end
+
+ context "on amazon" do
+ let(:options) { { platform: "amazon" } }
+
+ pf_reports_true_for(:amazon?, :amazon_linux?, :rpm_based?, :fedora_derived?)
+ end
+
+ context "on arch" do
+ let(:options) { { platform: "arch" } }
+
+ pf_reports_true_for(:arch?, :arch_linux?)
+ end
+
+ context "on centos" do
+ let(:options) { { platform: "centos" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?)
+ end
+
+ context "on clearos" do
+ let(:options) { { platform: "clearos" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?)
+ end
+
+ context "on dragonfly4" do
+ let(:options) { { platform: "dragonfly4" } }
+
+ pf_reports_true_for(:dragonflybsd?)
+ end
+
+ context "on fedora" do
+ let(:options) { { platform: "fedora" } }
+
+ pf_reports_true_for(:fedora?, :rpm_based?, :fedora_derived?, :redhat_based?)
+ end
+
+ context "on freebsd" do
+ let(:options) { { platform: "freebsd" } }
+
+ pf_reports_true_for(:freebsd?, :bsd_based?)
+ end
+
+ context "on gentoo" do
+ let(:options) { { platform: "gentoo" } }
+
+ pf_reports_true_for(:gentoo?)
+ end
+
+ context "on mac_os_x" do
+ let(:options) { { platform: "mac_os_x" } }
+
+ pf_reports_true_for(:mac_os_x?, :mac?, :osx?, :macos?)
+ end
+
+ context "on openbsd" do
+ let(:options) { { platform: "openbsd" } }
+
+ pf_reports_true_for(:openbsd?, :bsd_based?)
+ end
+
+ context "on opensuse" do
+ let(:options) { { platform: "opensuse" } }
+
+ pf_reports_true_for(:suse?, :rpm_based?)
+ end
+
+ context "on oracle" do
+ let(:options) { { platform: "oracle" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?)
+ end
+
+ context "on redhat" do
+ let(:options) { { platform: "redhat" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?)
+ end
+
+ context "on smartos" do
+ let(:options) { { platform: "smartos" } }
+
+ pf_reports_true_for(:smartos?, :solaris_based?)
+ end
+
+ context "on solaris2" do
+ let(:options) { { platform: "solaris2" } }
+
+ pf_reports_true_for(:solaris?, :solaris2?, :solaris_based?)
+ end
+
+ context "on suse" do
+ let(:options) { { platform: "suse" } }
+
+ pf_reports_true_for(:suse?, :rpm_based?)
+ end
+
+ context "on windows" do
+ let(:options) { { platform: "windows" } }
+
+ pf_reports_true_for(:windows?)
+ end
+
+ context "node-independent windows APIs" do
+ if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ it "reports true for :windows_ruby_platform?" do
+ expect(described_class.windows_ruby_platform?).to be true
+ end
+ else
+ it "reports false for :windows_ruby_platform?" do
+ expect(described_class.windows_ruby_platform?).to be false
+ end
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/platform_spec.rb b/chef-utils/spec/unit/dsl/platform_spec.rb
new file mode 100644
index 0000000000..dade8b0495
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/platform_spec.rb
@@ -0,0 +1,234 @@
+#
+# Copyright:: Copyright 2018-2019, 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"
+require "fauxhai"
+
+def platform_reports_true_for(*args)
+ args.each do |method|
+ it "reports true for #{method} on the module given a node" do
+ expect(described_class.send(method, node)).to be true
+ end
+ it "reports true for #{method} when mixed into a class with a node" do
+ expect(thing_with_a_node.send(method, node)).to be true
+ end
+ it "reports true for #{method} when mixed into a class with a run_context" do
+ expect(thing_with_a_run_context.send(method, node)).to be true
+ end
+ it "reports true for #{method} when mixed into a class with the dsl" do
+ expect(thing_with_the_dsl.send(method, node)).to be true
+ end
+ it "reports true for #{method} on the main class give a node" do
+ expect(ChefUtils.send(method, node)).to be true
+ end
+ end
+ (PLATFORM_HELPERS - args).each do |method|
+ it "reports false for #{method} on the module given a node" do
+ expect(described_class.send(method, node)).to be false
+ end
+ it "reports false for #{method} when mixed into a class with a node" do
+ expect(thing_with_a_node.send(method, node)).to be false
+ end
+ it "reports false for #{method} when mixed into a class with the dsl" do
+ expect(thing_with_the_dsl.send(method, node)).to be false
+ end
+ it "reports false for #{method} on the main class give a node" do
+ expect(ChefUtils.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::Platform do
+ let(:node) { Fauxhai.mock(options).data }
+
+ class ThingWithANode
+ include ChefUtils::DSL::Platform
+ attr_accessor :node
+ def initialize(node)
+ @node = node
+ end
+ end
+
+ class ThingWithARunContext
+ include ChefUtils::DSL::Platform
+ class RunContext
+ attr_accessor :node
+ end
+ attr_accessor :run_context
+ def initialize(node)
+ @run_context = RunContext.new
+ run_context.node = node
+ end
+ end
+
+ class ThingWithTheDSL
+ include ChefUtils
+ attr_accessor :node
+ def initialize(node)
+ @node = node
+ end
+ end
+
+ let(:thing_with_a_node) { ThingWithANode.new(node) }
+ let(:thing_with_a_run_context) { ThingWithARunContext.new(node) }
+ let(:thing_with_the_dsl) { ThingWithTheDSL.new(node) }
+
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & PLATFORM_HELPERS).to be_empty
+ end
+ end
+
+ context "on ubuntu" do
+ let(:options) { { platform: "ubuntu" } }
+
+ platform_reports_true_for(:ubuntu?, :ubuntu_platform?)
+ end
+
+ context "on raspbian" do
+ let(:options) { { platform: "raspbian" } }
+
+ platform_reports_true_for(:raspbian?, :raspbian_platform?)
+ end
+
+ context "on linuxmint" do
+ let(:options) { { platform: "linuxmint" } }
+
+ platform_reports_true_for(:mint?, :linux_mint?, :linuxmint?, :linuxmint_platform?)
+ end
+
+ context "on debian" do
+ let(:options) { { platform: "debian" } }
+
+ platform_reports_true_for(:debian_platform?)
+ end
+
+ context "on aix" do
+ let(:options) { { platform: "aix" } }
+
+ platform_reports_true_for(:aix_platform?)
+ end
+
+ context "on amazon" do
+ let(:options) { { platform: "amazon" } }
+
+ platform_reports_true_for(:amazon_platform?)
+ end
+
+ context "on arch" do
+ let(:options) { { platform: "arch" } }
+
+ platform_reports_true_for(:arch_platform?)
+ end
+
+ context "on centos" do
+ let(:options) { { platform: "centos" } }
+
+ platform_reports_true_for(:centos?, :centos_platform?)
+ end
+
+ context "on clearos" do
+ let(:options) { { platform: "clearos" } }
+
+ platform_reports_true_for(:clearos?, :clearos_platform?)
+ end
+
+ context "on dragonfly4" do
+ let(:options) { { platform: "dragonfly4" } }
+
+ platform_reports_true_for(:dragonfly_platform?)
+ end
+
+ context "on fedora" do
+ let(:options) { { platform: "fedora" } }
+
+ platform_reports_true_for(:fedora_platform?)
+ end
+
+ context "on freebsd" do
+ let(:options) { { platform: "freebsd" } }
+
+ platform_reports_true_for(:freebsd_platform?)
+ end
+
+ context "on gentoo" do
+ let(:options) { { platform: "gentoo" } }
+
+ platform_reports_true_for(:gentoo_platform?)
+ end
+
+ context "on mac_os_x" do
+ let(:options) { { platform: "mac_os_x" } }
+
+ platform_reports_true_for(:mac_os_x_platform?, :macos_platform?)
+ end
+
+ context "on openbsd" do
+ let(:options) { { platform: "openbsd" } }
+
+ platform_reports_true_for(:openbsd_platform?)
+ end
+
+ context "on oracle" do
+ let(:options) { { platform: "oracle" } }
+
+ platform_reports_true_for(:oracle?, :oracle_linux?, :oracle_platform?)
+ end
+
+ context "on redhat" do
+ let(:options) { { platform: "redhat" } }
+
+ platform_reports_true_for(:redhat?, :redhat_enterprise_linux?, :redhat_enterprise?, :redhat_platform?)
+ end
+
+ context "on smartos" do
+ let(:options) { { platform: "smartos" } }
+
+ platform_reports_true_for(:smartos_platform?)
+ end
+
+ context "on solaris2" do
+ let(:options) { { platform: "solaris2" } }
+
+ platform_reports_true_for(:solaris2_platform?)
+ end
+
+ context "on suse" do
+ let(:options) { { platform: "suse" } }
+
+ platform_reports_true_for(:suse_platform?)
+ end
+
+ context "on windows" do
+ let(:options) { { platform: "windows" } }
+
+ platform_reports_true_for(:windows_platform?)
+ end
+
+ context "on opensuseleap" do
+ let(:node) { { "platform" => "opensuseleap", "platform_version" => "15.1", "platform_family" => "suse", "os" => "linux" } }
+
+ platform_reports_true_for(:opensuse_platform?, :opensuseleap_platform?, :opensuse?, :leap_platform?)
+ end
+
+ context "on opensuse" do
+ let(:node) { { "platform" => "opensuse", "platform_version" => "11.0", "platform_family" => "suse", "os" => "linux" } }
+
+ platform_reports_true_for(:opensuse_platform?, :opensuseleap_platform?, :opensuse?, :leap_platform?)
+ end
+
+end
diff --git a/chef-utils/spec/unit/dsl/service_spec.rb b/chef-utils/spec/unit/dsl/service_spec.rb
new file mode 100644
index 0000000000..35e6b10e77
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/service_spec.rb
@@ -0,0 +1,116 @@
+#
+# Copyright:: Copyright 2018-2019, 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"
+
+RSpec.describe ChefUtils::DSL::Service do
+ class ServiceTestClass
+ include ChefUtils::DSL::Service
+ end
+
+ let(:test_instance) { ServiceTestClass.new }
+
+ context "#debianrcd?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/usr/sbin/update-rc.d").and_return(true)
+ expect(test_instance.debianrcd?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/usr/sbin/update-rc.d").and_return(false)
+ expect(test_instance.debianrcd?).to be false
+ end
+ end
+
+ context "#invokercd?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/usr/sbin/invoke-rc.d").and_return(true)
+ expect(test_instance.invokercd?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/usr/sbin/invoke-rc.d").and_return(false)
+ expect(test_instance.invokercd?).to be false
+ end
+ end
+
+ context "#upstart?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/sbin/initctl").and_return(true)
+ expect(test_instance.upstart?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/sbin/initctl").and_return(false)
+ expect(test_instance.upstart?).to be false
+ end
+ end
+
+ context "#insserv?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/sbin/insserv").and_return(true)
+ expect(test_instance.insserv?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/sbin/insserv").and_return(false)
+ expect(test_instance.insserv?).to be false
+ end
+ end
+
+ context "#redhatrcd?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/sbin/chkconfig").and_return(true)
+ expect(test_instance.redhatrcd?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/sbin/chkconfig").and_return(false)
+ expect(test_instance.redhatrcd?).to be false
+ end
+ end
+
+ context "#service_script_exist?" do
+ it "is true if the type is :initd and /etc/init.d script exists" do
+ expect(File).to receive(:exist?).with("/etc/init.d/example").and_return(true)
+ expect(test_instance.service_script_exist?(:initd, "example")).to be true
+ end
+ it "is false if the type is :initd and /etc/init.d script does not exist" do
+ expect(File).to receive(:exist?).with("/etc/init.d/example").and_return(false)
+ expect(test_instance.service_script_exist?(:initd, "example")).to be false
+ end
+ it "is true if the type is :upstart and /etc/init script exists" do
+ expect(File).to receive(:exist?).with("/etc/init/example.conf").and_return(true)
+ expect(test_instance.service_script_exist?(:upstart, "example")).to be true
+ end
+ it "is false if the type is :upstart and /etc/init script does not exist" do
+ expect(File).to receive(:exist?).with("/etc/init/example.conf").and_return(false)
+ expect(test_instance.service_script_exist?(:upstart, "example")).to be false
+ end
+ it "is true if the type is :xinetd and /etc/xinetd.d script exists" do
+ expect(File).to receive(:exist?).with("/etc/xinetd.d/example").and_return(true)
+ expect(test_instance.service_script_exist?(:xinetd, "example")).to be true
+ end
+ it "is false if the type is :xinetd and /etc/xinetd.d script does not exist" do
+ expect(File).to receive(:exist?).with("/etc/xinetd.d/example").and_return(false)
+ expect(test_instance.service_script_exist?(:xinetd, "example")).to be false
+ end
+ it "is true if the type is :etc_rcd and /etc/rc.d script exists" do
+ expect(File).to receive(:exist?).with("/etc/rc.d/example").and_return(true)
+ expect(test_instance.service_script_exist?(:etc_rcd, "example")).to be true
+ end
+ it "is false if the type is :etc_rcd and /etc/rc.d script does not exist" do
+ expect(File).to receive(:exist?).with("/etc/rc.d/example").and_return(false)
+ expect(test_instance.service_script_exist?(:etc_rcd, "example")).to be false
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/which_spec.rb b/chef-utils/spec/unit/dsl/which_spec.rb
new file mode 100644
index 0000000000..74a4e5638e
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/which_spec.rb
@@ -0,0 +1,168 @@
+#
+# Copyright:: Copyright 2018-2019, 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"
+
+RSpec.describe ChefUtils::DSL::Which do
+
+ class WhichTestClass
+ include ChefUtils::DSL::Which
+ end
+
+ let(:test) { WhichTestClass.new }
+
+ describe "#which" do
+ def self.test_which(description, *args, path: ["/dir1", "/dir2" ].join(File::PATH_SEPARATOR), finds: nil, others: [], directory: false, &block)
+ it description do
+ # stub the ENV['PATH']
+ expect(ENV).to receive(:[]).with("PATH").and_return(path)
+
+ # most files should not be found
+ allow(File).to receive(:executable?).and_return(false)
+ allow(File).to receive(:directory?).and_return(false)
+
+ # stub the expectation
+ expect(File).to receive(:executable?).with(finds).and_return(true) if finds
+
+ # if the file we find is a directory
+ expect(File).to receive(:directory?).with(finds).and_return(true) if finds && directory
+
+ # allow for stubbing other paths to exist that we should not find
+ others.each do |other|
+ allow(File).to receive(:executable?).with(other).and_return(true)
+ end
+
+ # setup the actual expectation on the return value
+ if finds && !directory
+ expect(test.which(*args, &block)).to eql(finds)
+ else
+ expect(test.which(*args, &block)).to eql(false)
+ end
+ end
+ end
+
+ context "simple usage" do
+ test_which("returns false when it does not find anything", "foo1")
+
+ ["/dir1", "/dir2" ].each do |dir|
+ test_which("finds `foo1` in #{dir} when it is stubbed", "foo1", finds: "#{dir}/foo1")
+ end
+
+ test_which("does not find an executable directory", "foo1", finds: "/dir1/foo1", directory: true)
+ end
+
+ context "with an array of args" do
+ test_which("finds the first arg", "foo1", "foo2", finds: "/dir2/foo1")
+
+ test_which("finds the second arg", "foo1", "foo2", finds: "/dir2/foo2")
+
+ test_which("finds the first arg when there's both", "foo1", "foo2", finds: "/dir2/foo1", others: [ "/dir1/foo2" ])
+
+ test_which("and the directory order can be reversed", "foo1", "foo2", finds: "/dir1/foo1", others: [ "/dir2/foo2" ])
+
+ test_which("or be the same", "foo1", "foo2", finds: "/dir1/foo1", others: [ "/dir1/foo2" ])
+ end
+
+ context "with a block" do
+ test_which("doesnt find it if its false", "foo1", others: [ "/dir1/foo1" ]) do |f|
+ false
+ end
+
+ test_which("finds it if its true", "foo1", finds: "/dir1/foo1") do |f|
+ true
+ end
+
+ test_which("passes in the filename as the arg", "foo1", finds: "/dir1/foo1") do |f|
+ raise "bad arg to block" unless f == "/dir1/foo1"
+
+ true
+ end
+
+ test_which("arrays with blocks", "foo1", "foo2", finds: "/dir2/foo1", others: [ "/dir1/foo2" ]) do |f|
+ raise "bad arg to block" unless f == "/dir2/foo1" || f == "/dir1/foo2"
+
+ true
+ end
+ end
+
+ context "nil path" do
+ test_which("returns false when it does not find anything", "foo1", path: nil)
+ end
+ end
+
+ describe "#where" do
+ def self.test_where(description, *args, path: ["/dir1", "/dir2" ].join(File::PATH_SEPARATOR), finds: [], others: [], &block)
+ it description do
+ # stub the ENV['PATH']
+ expect(ENV).to receive(:[]).with("PATH").and_return(path)
+
+ # most files should not be found
+ allow(File).to receive(:executable?).and_return(false)
+ allow(File).to receive(:directory?).and_return(false)
+
+ # allow for stubbing other paths to exist that we should not return
+ others.each do |other|
+ allow(File).to receive(:executable?).with(other).and_return(true)
+ end
+
+ # stub the expectation
+ finds.each do |path|
+ expect(File).to receive(:executable?).with(path).and_return(true)
+ end
+
+ # setup the actual expectation on the return value
+ expect(test.where(*args, &block)).to eql(finds)
+ end
+ end
+
+ context "simple usage" do
+ test_where("returns empty array when it doesn't find anything", "foo1")
+
+ ["/dir1", "/dir2" ].each do |dir|
+ test_where("finds `foo1` in #{dir} when it is stubbed", "foo1", finds: [ "#{dir}/foo1" ])
+ end
+
+ test_where("finds `foo1` in all directories", "foo1", finds: [ "/dir1/foo1", "/dir2/foo1" ])
+ end
+
+ context "with an array of args" do
+ test_where("finds the first arg", "foo1", "foo2", finds: [ "/dir2/foo1" ])
+
+ test_where("finds the second arg", "foo1", "foo2", finds: [ "/dir2/foo2" ])
+
+ test_where("finds foo1 before foo2 if the dirs are reversed", "foo1", "foo2", finds: [ "/dir1/foo1", "/dir2/foo2" ])
+
+ test_where("finds them both in the same directory", "foo1", "foo2", finds: [ "/dir1/foo1", "/dir1/foo2" ])
+
+ test_where("finds foo2 first if they're reversed", "foo2", "foo1", finds: [ "/dir1/foo2", "/dir1/foo1" ])
+ end
+
+ context "with a block do" do
+ test_where("finds foo1 and foo2 if they exist and the block is true", "foo1", "foo2", finds: [ "/dir1/foo2", "/dir2/foo2" ]) do
+ true
+ end
+
+ test_where("does not finds foo1 and foo2 if they exist and the block is false", "foo1", "foo2", others: [ "/dir1/foo2", "/dir2/foo2" ]) do
+ false
+ end
+ end
+
+ context "with a nil path" do
+ test_where("returns empty array when it doesn't find anything", "foo1", path: nil)
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/mash_spec.rb b/chef-utils/spec/unit/mash_spec.rb
new file mode 100644
index 0000000000..e8d321147e
--- /dev/null
+++ b/chef-utils/spec/unit/mash_spec.rb
@@ -0,0 +1,50 @@
+#
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright 2011-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"
+
+RSpec.describe ChefUtils::Mash do
+ it "should duplicate a simple key/value mash to a new mash" do
+ data = { x: "one", y: "two", z: "three" }
+ @orig = ChefUtils::Mash.new(data)
+ @copy = @orig.dup
+ expect(@copy.to_hash).to eq(ChefUtils::Mash.new(data).to_hash)
+ @copy[:x] = "four"
+ expect(@orig[:x]).to eq("one")
+ end
+
+ it "should duplicate a mash with an array to a new mash" do
+ data = { x: "one", y: "two", z: [1, 2, 3] }
+ @orig = ChefUtils::Mash.new(data)
+ @copy = @orig.dup
+ expect(@copy.to_hash).to eq(ChefUtils::Mash.new(data).to_hash)
+ @copy[:z] << 4
+ expect(@orig[:z]).to eq([1, 2, 3])
+ end
+
+ it "should duplicate a nested mash to a new mash" do
+ data = { x: "one", y: "two", z: ChefUtils::Mash.new({ a: [1, 2, 3] }) }
+ @orig = ChefUtils::Mash.new(data)
+ @copy = @orig.dup
+ expect(@copy.to_hash).to eq(ChefUtils::Mash.new(data).to_hash)
+ @copy[:z][:a] << 4
+ expect(@orig[:z][:a]).to eq([1, 2, 3])
+ end
+
+ # add more!
+end