diff options
Diffstat (limited to 'chef-utils/spec')
-rw-r--r-- | chef-utils/spec/spec_helper.rb | 93 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/architecture_spec.rb | 139 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/dsl_spec.rb | 33 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/introspection_spec.rb | 168 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/os_spec.rb | 174 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/path_sanity_spec.rb | 85 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/platform_family_spec.rb | 192 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/platform_spec.rb | 234 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/service_spec.rb | 116 | ||||
-rw-r--r-- | chef-utils/spec/unit/dsl/which_spec.rb | 168 | ||||
-rw-r--r-- | chef-utils/spec/unit/mash_spec.rb | 50 |
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 |