diff options
author | Jay Mundrawala <jdmundrawala@gmail.com> | 2015-03-24 10:21:14 -0700 |
---|---|---|
committer | Jay Mundrawala <jdmundrawala@gmail.com> | 2015-03-24 10:21:14 -0700 |
commit | d3e4e565fdf7c210acfd8efd0de6b674883a5e0f (patch) | |
tree | 5fd2f1b237e39c81d8a7b54e0cdc0279e7528435 /spec | |
parent | 17014504259f8ccb19d782bd3b63b24657e4c9ca (diff) | |
parent | 157852156703daf96366dd773903147baf0292d9 (diff) | |
download | chef-7296c5ece82c77334c7fce2787f267b79c4e8736.tar.gz |
Merge pull request #3128 from chef/jdm/prepare-12.2.012.2.0.rc.0
prepare 12.2.0 RC 0
Diffstat (limited to 'spec')
38 files changed, 1038 insertions, 169 deletions
diff --git a/spec/functional/file_content_management/deploy_strategies_spec.rb b/spec/functional/file_content_management/deploy_strategies_spec.rb index 03a6c504c1..8a995d0e41 100644 --- a/spec/functional/file_content_management/deploy_strategies_spec.rb +++ b/spec/functional/file_content_management/deploy_strategies_spec.rb @@ -43,7 +43,7 @@ shared_examples_for "a content deploy strategy" do ## # UNIX Context - let(:default_mode) { normalize_mode(0100666 - File.umask) } + let(:default_mode) { normalize_mode(0666 & ~File.umask) } it "touches the file to create it (UNIX)", :unix_only do content_deployer.create(target_file_path) diff --git a/spec/functional/resource/cookbook_file_spec.rb b/spec/functional/resource/cookbook_file_spec.rb index 7797ed0041..6d4c5b4a8f 100644 --- a/spec/functional/resource/cookbook_file_spec.rb +++ b/spec/functional/resource/cookbook_file_spec.rb @@ -32,7 +32,7 @@ describe Chef::Resource::CookbookFile do content end - let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) } + let(:default_mode) { (0666 & ~File.umask).to_s(8) } it_behaves_like "a securable resource with reporting" diff --git a/spec/functional/resource/deploy_revision_spec.rb b/spec/functional/resource/deploy_revision_spec.rb index e5f5341fcd..bd45e32771 100644 --- a/spec/functional/resource/deploy_revision_spec.rb +++ b/spec/functional/resource/deploy_revision_spec.rb @@ -201,6 +201,41 @@ describe Chef::Resource::DeployRevision, :unix_only => true do end end + describe "setting default parameters to nil" do + before do + FileUtils.mkdir_p(rel_path("releases")) + FileUtils.mkdir_p(rel_path("shared")) + end + + it "supports setting symlink_before_migrate to nil" do + deploy_to_latest_rev.symlink_before_migrate(nil) + expect(deploy_to_latest_rev.symlink_before_migrate).to eql(nil) + deploy_to_latest_rev.run_action(:deploy) + expect(deploy_to_latest_rev).to be_updated_by_last_action + end + + it "supports setting symlinks to nil" do + deploy_to_latest_rev.symlinks(nil) + expect(deploy_to_latest_rev.symlinks).to eql(nil) + deploy_to_latest_rev.run_action(:deploy) + expect(deploy_to_latest_rev).to be_updated_by_last_action + end + + it "supports setting purge_before_symlink to nil" do + deploy_to_latest_rev.purge_before_symlink(nil) + expect(deploy_to_latest_rev.purge_before_symlink).to eql(nil) + deploy_to_latest_rev.run_action(:deploy) + expect(deploy_to_latest_rev).to be_updated_by_last_action + end + + it "supports setting create_dirs_before_symlink to nil" do + deploy_to_latest_rev.create_dirs_before_symlink(nil) + expect(deploy_to_latest_rev.create_dirs_before_symlink).to eql(nil) + deploy_to_latest_rev.run_action(:deploy) + expect(deploy_to_latest_rev).to be_updated_by_last_action + end + end + describe "back to a previously deployed revision, with the directory structure precreated" do before do FileUtils.mkdir_p(rel_path("releases")) diff --git a/spec/functional/resource/directory_spec.rb b/spec/functional/resource/directory_spec.rb index 2c4025f83e..88a810964f 100644 --- a/spec/functional/resource/directory_spec.rb +++ b/spec/functional/resource/directory_spec.rb @@ -23,7 +23,7 @@ describe Chef::Resource::Directory do let(:directory_base) { "directory_spec" } - let(:default_mode) { ((0100777 - File.umask) & 07777).to_s(8) } + let(:default_mode) { (0777 & ~File.umask).to_s(8) } def create_resource events = Chef::EventDispatch::Dispatcher.new diff --git a/spec/functional/resource/dsc_resource_spec.rb b/spec/functional/resource/dsc_resource_spec.rb new file mode 100644 index 0000000000..6f453eeb9f --- /dev/null +++ b/spec/functional/resource/dsc_resource_spec.rb @@ -0,0 +1,93 @@ +# +# Author:: Jay Mundrawala (<jdm@chef.io>) +# Copyright:: Copyright (c) 2015 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Resource::DscResource, :windows_powershell_dsc_only do + before(:all) do + @ohai = Ohai::System.new + @ohai.all_plugins(['platform', 'os', 'languages/powershell']) + end + + let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new } + + let(:node) { + Chef::Node.new.tap do |n| + n.consume_external_attrs(@ohai.data, {}) + end + } + + let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) } + + let(:new_resource) { + Chef::Resource::DscResource.new("dsc_resource_test", run_context) + } + + context 'when Powershell does not support Invoke-DscResource' + context 'when Powershell supports Invoke-DscResource' do + before do + if !Chef::Platform.supports_dsc_invoke_resource?(node) + skip 'Requires Powershell >= 5.0.10018.0' + end + end + context 'with an invalid dsc resource' do + it 'raises an exception if the resource is not found' do + new_resource.resource 'thisdoesnotexist' + expect { new_resource.run_action(:run) }.to raise_error( + Chef::Exceptions::ResourceNotFound) + end + end + + context 'with a valid dsc resource' do + let(:tmp_file_name) { Dir::Tmpname.create('tmpfile') {} } + let(:test_text) { "'\"!@#$%^&*)(}{][\u2713~n"} + + before do + new_resource.resource :File + new_resource.property :Contents, test_text + new_resource.property :DestinationPath, tmp_file_name + end + + after do + File.delete(tmp_file_name) if File.exists? tmp_file_name + end + + it 'converges the resource if it is not converged' do + new_resource.run_action(:run) + contents = File.open(tmp_file_name, 'rb:bom|UTF-16LE') do |f| + f.read.encode('UTF-8') + end + expect(contents).to eq(test_text) + expect(new_resource).to be_updated + end + + it 'does not converge the resource if it is already converged' do + new_resource.run_action(:run) + expect(new_resource).to be_updated + reresource = + Chef::Resource::DscResource.new("dsc_resource_retest", run_context) + reresource.resource :File + reresource.property :Contents, test_text + reresource.property :DestinationPath, tmp_file_name + reresource.run_action(:run) + expect(reresource).not_to be_updated + end + end + + end +end diff --git a/spec/functional/resource/env_spec.rb b/spec/functional/resource/env_spec.rb index 16caec14bf..b9dcd7b33a 100755 --- a/spec/functional/resource/env_spec.rb +++ b/spec/functional/resource/env_spec.rb @@ -29,14 +29,15 @@ describe Chef::Resource::Env, :windows_only do let(:env_value_expandable) { '%SystemRoot%' } let(:test_run_context) { node = Chef::Node.new + node.default['os'] = 'windows' node.default['platform'] = 'windows' node.default['platform_version'] = '6.1' empty_events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, {}, empty_events) } - let(:test_resource) { - Chef::Resource::Env.new('unknown', test_run_context) - } + let(:test_resource) { + Chef::Resource::Env.new('unknown', test_run_context) + } before(:each) do resource_lower = Chef::Resource::Env.new(chef_env_test_lower_case, test_run_context) diff --git a/spec/functional/resource/file_spec.rb b/spec/functional/resource/file_spec.rb index cf70c349fb..f1a290dea4 100644 --- a/spec/functional/resource/file_spec.rb +++ b/spec/functional/resource/file_spec.rb @@ -64,7 +64,7 @@ describe Chef::Resource::File do provider.current_resource end - let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) } + let(:default_mode) { (0666 & ~File.umask).to_s(8) } it_behaves_like "a file resource" diff --git a/spec/functional/resource/powershell_spec.rb b/spec/functional/resource/powershell_spec.rb index 1b3ac844e0..56a905efe7 100644 --- a/spec/functional/resource/powershell_spec.rb +++ b/spec/functional/resource/powershell_spec.rb @@ -56,7 +56,8 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do resource.run_action(:run) end - it "returns the -27 for a powershell script that exits with -27" do + it "returns the -27 for a powershell script that exits with -27", :windows_powershell_dsc_only do + # This is broken on Powershell < 4.0 file = Tempfile.new(['foo', '.ps1']) begin file.write "exit -27" diff --git a/spec/functional/resource/remote_directory_spec.rb b/spec/functional/resource/remote_directory_spec.rb index bcafca7399..37ffbbc971 100644 --- a/spec/functional/resource/remote_directory_spec.rb +++ b/spec/functional/resource/remote_directory_spec.rb @@ -22,7 +22,7 @@ describe Chef::Resource::RemoteDirectory do include_context Chef::Resource::Directory let(:directory_base) { "directory_spec" } - let(:default_mode) { ((0100777 - File.umask) & 07777).to_s(8) } + let(:default_mode) { (0777 & ~File.umask).to_s(8) } def create_resource cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) diff --git a/spec/functional/resource/remote_file_spec.rb b/spec/functional/resource/remote_file_spec.rb index 29091fd785..4fbcd2d24b 100644 --- a/spec/functional/resource/remote_file_spec.rb +++ b/spec/functional/resource/remote_file_spec.rb @@ -52,7 +52,7 @@ describe Chef::Resource::RemoteFile do create_resource end - let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) } + let(:default_mode) { (0666 & ~File.umask).to_s(8) } context "when fetching files over HTTP" do before(:all) do diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb index d7b35e7450..35c5166e31 100644 --- a/spec/functional/resource/template_spec.rb +++ b/spec/functional/resource/template_spec.rb @@ -58,7 +58,7 @@ describe Chef::Resource::Template do create_resource end - let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) } + let(:default_mode) { (0666 & ~File.umask).to_s(8) } it_behaves_like "a file resource" diff --git a/spec/functional/resource/user/dscl_spec.rb b/spec/functional/resource/user/dscl_spec.rb index 45b6754453..5d960daf11 100644 --- a/spec/functional/resource/user/dscl_spec.rb +++ b/spec/functional/resource/user/dscl_spec.rb @@ -19,9 +19,8 @@ require 'spec_helper' require 'chef/mixin/shell_out' metadata = { - :unix_only => true, + :mac_osx_only => true, :requires_root => true, - :provider => {:user => Chef::Provider::User::Dscl}, :not_supported_on_mac_osx_106 => true, } diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb index 9ac88d7b60..3e4e4e7604 100644 --- a/spec/functional/resource/user/useradd_spec.rb +++ b/spec/functional/resource/user/useradd_spec.rb @@ -32,6 +32,7 @@ end metadata = { :unix_only => true, :requires_root => true, + :not_supported_on_mac_osx => true, :provider => {:user => user_provider_for_platform} } @@ -76,9 +77,22 @@ describe Chef::Provider::User::Useradd, metadata do end end + def try_cleanup + ['/home/cheftestfoo', '/home/cheftestbar'].each do |f| + FileUtils.rm_rf(f) if File.exists? f + end + + ['cf-test'].each do |u| + r = Chef::Resource::User.new("DELETE USER", run_context) + r.username('cf-test') + r.run_action(:remove) + end + end + before do # Silence shell_out live stream Chef::Log.level = :warn + try_cleanup end after do @@ -386,18 +400,18 @@ describe Chef::Provider::User::Useradd, metadata do end context "and home directory is updated" do - let(:existing_home) { "/home/foo" } - let(:home) { "/home/bar" } + let(:existing_home) { "/home/cheftestfoo" } + let(:home) { "/home/cheftestbar" } it "ensures the home directory is set to the desired value" do - expect(pw_entry.home).to eq("/home/bar") + expect(pw_entry.home).to eq("/home/cheftestbar") end context "and manage_home is enabled" do let(:existing_manage_home) { true } let(:manage_home) { true } it "moves the home directory to the new location" do - expect(File).not_to exist("/home/foo") - expect(File).to exist("/home/bar") + expect(File).not_to exist("/home/cheftestfoo") + expect(File).to exist("/home/cheftestbar") end end @@ -409,19 +423,19 @@ describe Chef::Provider::User::Useradd, metadata do # Inconsistent behavior. See: CHEF-2205 it "created the home dir b/c of CHEF-2205 so it still exists" do # This behavior seems contrary to expectation and non-convergent. - expect(File).not_to exist("/home/foo") - expect(File).to exist("/home/bar") + expect(File).not_to exist("/home/cheftestfoo") + expect(File).to exist("/home/cheftestbar") end elsif ohai[:platform] == "aix" it "creates the home dir in the desired location" do - expect(File).not_to exist("/home/foo") - expect(File).to exist("/home/bar") + expect(File).not_to exist("/home/cheftestfoo") + expect(File).to exist("/home/cheftestbar") end else it "does not create the home dir in the desired location (XXX)" do # This behavior seems contrary to expectation and non-convergent. - expect(File).not_to exist("/home/foo") - expect(File).not_to exist("/home/bar") + expect(File).not_to exist("/home/cheftestfoo") + expect(File).not_to exist("/home/cheftestbar") end end end @@ -432,8 +446,8 @@ describe Chef::Provider::User::Useradd, metadata do it "leaves the old home directory around (XXX)" do # Would it be better to remove the old home? - expect(File).to exist("/home/foo") - expect(File).not_to exist("/home/bar") + expect(File).to exist("/home/cheftestfoo") + expect(File).not_to exist("/home/cheftestbar") end end end diff --git a/spec/functional/win32/crypto_spec.rb b/spec/functional/win32/crypto_spec.rb new file mode 100644 index 0000000000..1492995886 --- /dev/null +++ b/spec/functional/win32/crypto_spec.rb @@ -0,0 +1,57 @@ +# +# Author:: Jay Mundrawala(<jdm@chef.io>) +# Copyright:: Copyright (c) 2015 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' +if Chef::Platform.windows? + require 'chef/win32/crypto' +end + +describe 'Chef::ReservedNames::Win32::Crypto', :windows_only do + describe '#encrypt' do + before(:all) do + ohai_reader = Ohai::System.new + ohai_reader.all_plugins("platform") + + new_node = Chef::Node.new + new_node.consume_external_attrs(ohai_reader.data,{}) + + events = Chef::EventDispatch::Dispatcher.new + + @run_context = Chef::RunContext.new(new_node, {}, events) + end + + let (:plaintext) { 'p@assword' } + + it 'can be decrypted by powershell' do + encrypted = Chef::ReservedNames::Win32::Crypto.encrypt(plaintext) + resource = Chef::Resource::WindowsScript::PowershellScript.new("Powershell resource functional test", @run_context) + resource.code <<-EOF +$encrypted = '#{encrypted}' | ConvertTo-SecureString +$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($encrypted) +$plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) +if ($plaintext -ne '#{plaintext}') { + Write-Error 'Got: ' $plaintext + exit 1 +} +exit 0 + EOF + resource.returns(0) + resource.run_action(:run) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8888efc424..fb284c721b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -110,10 +110,13 @@ RSpec.configure do |config| # Tests that randomly fail, but may have value. config.filter_run_excluding :volatile => true config.filter_run_excluding :volatile_on_solaris => true if solaris? + config.filter_run_excluding :volatile_from_verify => false # Add jruby filters here config.filter_run_excluding :windows_only => true unless windows? config.filter_run_excluding :not_supported_on_mac_osx_106 => true if mac_osx_106? + config.filter_run_excluding :not_supported_on_mac_osx=> true if mac_osx? + config.filter_run_excluding :mac_osx_only=> true if !mac_osx? config.filter_run_excluding :not_supported_on_win2k3 => true if windows_win2k3? config.filter_run_excluding :not_supported_on_solaris => true if solaris? config.filter_run_excluding :win2k3_only => true unless windows_win2k3? diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb index a412fe38e1..da0313758b 100644 --- a/spec/support/platform_helpers.rb +++ b/spec/support/platform_helpers.rb @@ -88,6 +88,20 @@ def mac_osx_106? false end +def mac_osx? + if File.exists? "/usr/bin/sw_vers" + result = ShellHelpers.shell_out("/usr/bin/sw_vers") + result.stdout.each_line do |line| + if line =~ /^ProductName:\sMac OS X.*$/ + return true + end + end + end + + false +end + + # detects if the hardware is 64-bit (evaluates to true in "WOW64" mode in a 32-bit app on a 64-bit system) def windows64? windows? && ( ENV['PROCESSOR_ARCHITECTURE'] == 'AMD64' || ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64' ) diff --git a/spec/support/shared/functional/securable_resource_with_reporting.rb b/spec/support/shared/functional/securable_resource_with_reporting.rb index 8a2ceed837..37fc538801 100644 --- a/spec/support/shared/functional/securable_resource_with_reporting.rb +++ b/spec/support/shared/functional/securable_resource_with_reporting.rb @@ -35,7 +35,7 @@ shared_examples_for "a securable resource with reporting" do # Default mode varies based on implementation. Providers that use a tempfile # will default to 0600. Providers that use File.open will default to 0666 - # umask - # let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) } + # let(:default_mode) { (0666 & ~File.umask).to_s(8) } describe "reading file security metadata for reporting on unix", :unix_only => true do # According to POSIX standard created files get either the @@ -185,7 +185,7 @@ shared_examples_for "a securable resource with reporting" do # TODO: most stable way to specify? expect(current_resource.owner).to eq(Etc.getpwuid(Process.uid).name) expect(current_resource.group).to eq(@expected_group_name) - expect(current_resource.mode).to eq("0#{((0100666 - File.umask) & 07777).to_s(8)}") + expect(current_resource.mode).to eq("0#{(0666 & ~File.umask).to_s(8)}") end end @@ -239,8 +239,8 @@ shared_examples_for "a securable resource with reporting" do end context "and mode is specified as a String" do - let(:default_create_mode) { (0100666 - File.umask) } - let(:expected_mode) { "0#{(default_create_mode & 07777).to_s(8)}" } + let(:default_create_mode) { 0666 & ~File.umask } + let(:expected_mode) { "0#{default_create_mode.to_s(8)}" } before do resource.mode(expected_mode) @@ -252,7 +252,7 @@ shared_examples_for "a securable resource with reporting" do end context "and mode is specified as an Integer" do - let(:set_mode) { (0100666 - File.umask) & 07777 } + let(:set_mode) { 0666 & ~File.umask } let(:expected_mode) { "0#{set_mode.to_s(8)}" } before do diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb index ea2ad473e5..501251b7fe 100644 --- a/spec/unit/application/client_spec.rb +++ b/spec/unit/application/client_spec.rb @@ -305,7 +305,7 @@ describe Chef::Application::Client, "run_application", :unix_only do allow(Chef::Daemon).to receive(:daemonize).and_return(true) end - it "should exit hard with exitstatus 3" do + it "should exit hard with exitstatus 3", :volatile do pid = fork do @app.run_application end @@ -320,9 +320,9 @@ describe Chef::Application::Client, "run_application", :unix_only do end expect(@pipe[0].gets).to eq("started\n") Process.kill("TERM", pid) - Process.wait - sleep 1 # Make sure we give the converging child process enough time to finish - expect(IO.select([@pipe[0]], nil, nil, 0)).not_to be_nil + Process.wait(pid) + # The timeout value needs to be large enough for the child process to finish + expect(IO.select([@pipe[0]], nil, nil, 15)).not_to be_nil expect(@pipe[0].gets).to eq("finished\n") end end diff --git a/spec/unit/audit/audit_reporter_spec.rb b/spec/unit/audit/audit_reporter_spec.rb index 84d7ea82f0..4bf889510a 100644 --- a/spec/unit/audit/audit_reporter_spec.rb +++ b/spec/unit/audit/audit_reporter_spec.rb @@ -203,7 +203,7 @@ describe Chef::Audit::AuditReporter do it "doesn't send reports" do expect(reporter).to receive(:auditing_enabled?).and_return(true) expect(reporter).to receive(:run_status).and_return(nil) - expect(Chef::Log).to receive(:debug).with("Run failed before audits were initialized, not sending audit report to server") + expect(Chef::Log).to receive(:debug).with("Run failed before audit mode was initialized, not sending audit report to server") reporter.run_completed(node) end diff --git a/spec/unit/audit/runner_spec.rb b/spec/unit/audit/runner_spec.rb index 801147bdb9..0bd4c18388 100644 --- a/spec/unit/audit/runner_spec.rb +++ b/spec/unit/audit/runner_spec.rb @@ -36,6 +36,15 @@ describe Chef::Audit::Runner do RSpec::Core::Sandbox.sandboxed { ex.run } end + context "when we run in audit mode" do + let(:paths) { [ "/opt/chef/lib/chef/", 'C:\windows/here/lib/chef/' , "/opt/chef/extra/folders/lib/chef/"] } + it "excludes the current path from backtrace" do + paths.each do |path| + expect(runner.exclusion_pattern).to match(path) + end + end + end + describe "#initialize" do it "correctly sets the run_context during initialization" do expect(runner.instance_variable_get(:@run_context)).to eq(run_context) @@ -72,6 +81,7 @@ describe Chef::Audit::Runner do expect(RSpec.configuration.color).to eq(color) expect(RSpec.configuration.expose_dsl_globally?).to eq(false) + expect(RSpec.configuration.backtrace_exclusion_patterns).to include(runner.exclusion_pattern) expect(Specinfra.configuration.backend).to eq(:exec) end diff --git a/spec/unit/config_spec.rb b/spec/unit/config_spec.rb index 06178f7733..6ea67246b5 100644 --- a/spec/unit/config_spec.rb +++ b/spec/unit/config_spec.rb @@ -360,18 +360,12 @@ describe Chef::Config do describe "Chef::Config[:user_home]" do it "should set when HOME is provided" do expected = to_platform("/home/kitten") - allow(Chef::Config).to receive(:env).and_return({ 'HOME' => expected }) - expect(Chef::Config[:user_home]).to eq(expected) - end - - it "should be set when only USERPROFILE is provided" do - expected = to_platform("/users/kitten") - allow(Chef::Config).to receive(:env).and_return({ 'USERPROFILE' => expected }) + allow(Chef::Util::PathHelper).to receive(:home).and_return(expected) expect(Chef::Config[:user_home]).to eq(expected) end it "falls back to the current working directory when HOME and USERPROFILE is not set" do - allow(Chef::Config).to receive(:env).and_return({}) + allow(Chef::Util::PathHelper).to receive(:home).and_return(nil) expect(Chef::Config[:user_home]).to eq(Dir.pwd) end end diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb index 848af11db5..f1ca510ed3 100644 --- a/spec/unit/knife/bootstrap_spec.rb +++ b/spec/unit/knife/bootstrap_spec.rb @@ -115,7 +115,7 @@ describe Chef::Knife::Bootstrap do end def configure_env_home - ENV['HOME'] = "/env/home" + allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_yield(env_home_template_path) end def configure_gem_files @@ -123,15 +123,9 @@ describe Chef::Knife::Bootstrap do end before(:each) do - @original_home = ENV['HOME'] - ENV['HOME'] = nil expect(File).to receive(:exists?).with(bootstrap_template).and_return(false) end - after(:each) do - ENV['HOME'] = @original_home - end - context "when file is available everywhere" do before do configure_chef_config_dir @@ -161,7 +155,7 @@ describe Chef::Knife::Bootstrap do end end - context "when file is available in ENV['HOME']" do + context "when file is available in home directory" do before do configure_chef_config_dir configure_env_home @@ -180,7 +174,25 @@ describe Chef::Knife::Bootstrap do context "when file is available in Gem files" do before do configure_chef_config_dir + configure_env_home + configure_gem_files + + expect(File).to receive(:exists?).with(builtin_template_path).and_return(false) + expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false) + expect(File).to receive(:exists?).with(env_home_template_path).and_return(false) + expect(File).to receive(:exists?).with(gem_files_template_path).and_return(true) + end + + it "should load the template from Gem files" do + expect(knife.find_template).to eq(gem_files_template_path) + end + end + + context "when file is available in Gem files and home dir doesn't exist" do + before do + configure_chef_config_dir configure_gem_files + allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_return(nil) expect(File).to receive(:exists?).with(builtin_template_path).and_return(false) expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false) diff --git a/spec/unit/knife/core/subcommand_loader_spec.rb b/spec/unit/knife/core/subcommand_loader_spec.rb index df42cff2ea..7f9308b28a 100644 --- a/spec/unit/knife/core/subcommand_loader_spec.rb +++ b/spec/unit/knife/core/subcommand_loader_spec.rb @@ -19,23 +19,29 @@ require 'spec_helper' describe Chef::Knife::SubcommandLoader do + let(:loader) { Chef::Knife::SubcommandLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands')) } + let(:home) { File.join(CHEF_SPEC_DATA, 'knife-home') } + let(:plugin_dir) { File.join(home, '.chef', 'plugins', 'knife') } + before do allow(Chef::Platform).to receive(:windows?) { false } - @home = File.join(CHEF_SPEC_DATA, 'knife-home') - @env = {'HOME' => @home} - @loader = Chef::Knife::SubcommandLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands'), @env) + Chef::Util::PathHelper.class_variable_set(:@@home_dir, home) + end + + after do + Chef::Util::PathHelper.class_variable_set(:@@home_dir, nil) end it "builds a list of the core subcommand file require paths" do - expect(@loader.subcommand_files).not_to be_empty - @loader.subcommand_files.each do |require_path| + expect(loader.subcommand_files).not_to be_empty + loader.subcommand_files.each do |require_path| expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/) end end it "finds files installed via rubygems" do - expect(@loader.find_subcommands_via_rubygems).to include('chef/knife/node_create') - @loader.find_subcommands_via_rubygems.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])} + expect(loader.find_subcommands_via_rubygems).to include('chef/knife/node_create') + loader.find_subcommands_via_rubygems.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])} end it "finds files from latest version of installed gems" do @@ -54,23 +60,23 @@ describe Chef::Knife::SubcommandLoader do expect(gems[0]).to receive(:full_gem_path).and_return('/usr/lib/ruby/gems/knife-ec2-0.5.12') expect(Dir).to receive(:[]).with('/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/*.rb').and_return(gem_files) end - expect(@loader).to receive(:find_subcommands_via_dirglob).and_return({}) - expect(@loader.find_subcommands_via_rubygems.values.select { |file| file =~ /knife-ec2/ }.sort).to eq(gem_files) + expect(loader).to receive(:find_subcommands_via_dirglob).and_return({}) + expect(loader.find_subcommands_via_rubygems.values.select { |file| file =~ /knife-ec2/ }.sort).to eq(gem_files) end it "finds files using a dirglob when rubygems is not available" do - expect(@loader.find_subcommands_via_dirglob).to include('chef/knife/node_create') - @loader.find_subcommands_via_dirglob.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])} + expect(loader.find_subcommands_via_dirglob).to include('chef/knife/node_create') + loader.find_subcommands_via_dirglob.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])} end it "finds user-specific subcommands in the user's ~/.chef directory" do - expected_command = File.join(@home, '.chef', 'plugins', 'knife', 'example_home_subcommand.rb') - expect(@loader.site_subcommands).to include(expected_command) + expected_command = File.join(home, '.chef', 'plugins', 'knife', 'example_home_subcommand.rb') + expect(loader.site_subcommands).to include(expected_command) end it "finds repo specific subcommands by searching for a .chef directory" do expected_command = File.join(CHEF_SPEC_DATA, 'knife-site-subcommands', 'plugins', 'knife', 'example_subcommand.rb') - expect(@loader.site_subcommands).to include(expected_command) + expect(loader.site_subcommands).to include(expected_command) end # https://github.com/opscode/chef-dk/issues/227 @@ -137,25 +143,19 @@ describe Chef::Knife::SubcommandLoader do end before do - expect(@loader).to receive(:find_files_latest_gems).with("chef/knife/*.rb").and_return(all_found_commands) - expect(@loader).to receive(:find_subcommands_via_dirglob).and_return({}) + expect(loader).to receive(:find_files_latest_gems).with("chef/knife/*.rb").and_return(all_found_commands) + expect(loader).to receive(:find_subcommands_via_dirglob).and_return({}) end it "ignores commands from the non-matching gem install" do - expect(@loader.find_subcommands_via_rubygems.values).to eq(expected_valid_commands) + expect(loader.find_subcommands_via_rubygems.values).to eq(expected_valid_commands) end end describe "finding 3rd party plugins" do - let(:env_home) { "/home/alice" } - let(:manifest_path) { env_home + "/.chef/plugin_manifest.json" } - - before do - env_dup = ENV.to_hash - allow(ENV).to receive(:[]) { |key| env_dup[key] } - allow(ENV).to receive(:[]).with("HOME").and_return(env_home) - end + let(:home) { "/home/alice" } + let(:manifest_path) { home + "/.chef/plugin_manifest.json" } context "when there is not a ~/.chef/plugin_manifest.json file" do before do @@ -168,14 +168,14 @@ describe Chef::Knife::SubcommandLoader do else expect(Gem.source_index).to receive(:latest_specs).and_call_original end - @loader.subcommand_files.each do |require_path| + loader.subcommand_files.each do |require_path| expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/) end end context "and HOME environment variable is not set" do before do - allow(ENV).to receive(:[]).with("HOME").and_return(nil) + allow(Chef::Util::PathHelper).to receive(:home).and_return(nil) end it "searches rubygems for plugins" do @@ -184,7 +184,7 @@ describe Chef::Knife::SubcommandLoader do else expect(Gem.source_index).to receive(:latest_specs).and_call_original end - @loader.subcommand_files.each do |require_path| + loader.subcommand_files.each do |require_path| expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/) end end @@ -215,7 +215,7 @@ describe Chef::Knife::SubcommandLoader do it "uses paths from the manifest instead of searching gems" do expect(Gem::Specification).not_to receive(:latest_specs).and_call_original - expect(@loader.subcommand_files).to include(ec2_server_create_plugin) + expect(loader.subcommand_files).to include(ec2_server_create_plugin) end end diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb index 85e1c1abab..1b61f9b238 100644 --- a/spec/unit/mixin/params_validate_spec.rb +++ b/spec/unit/mixin/params_validate_spec.rb @@ -339,69 +339,83 @@ describe Chef::Mixin::ParamsValidate do end.to raise_error(Chef::Exceptions::ValidationFailed) end - it "should set and return a value, then return the same value" do - value = "meow" - expect(@vo.set_or_return(:test, value, {}).object_id).to eq(value.object_id) - expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id) + def self.test_set_or_return_method(method) + # caller is responsible for passing in the right arg to get 'return' behavior + return_arg = method == :nillable_set_or_return ? TinyClass::NULL_ARG : nil + + it "#{method} should set and return a value, then return the same value" do + value = "meow" + expect(@vo.send(method,:test, value, {}).object_id).to eq(value.object_id) + expect(@vo.send(method,:test, return_arg, {}).object_id).to eq(value.object_id) + end + + it "#{method} should set and return a default value when the argument is nil, then return the same value" do + value = "meow" + expect(@vo.send(method,:test, return_arg, { :default => value }).object_id).to eq(value.object_id) + expect(@vo.send(method,:test, return_arg, {}).object_id).to eq(value.object_id) + end + + it "#{method} should raise an ArgumentError when argument is nil and required is true" do + expect { + @vo.send(method,:test, return_arg, { :required => true }) + }.to raise_error(ArgumentError) + end + + it "#{method} should not raise an error when argument is nil and required is false" do + expect { + @vo.send(method,:test, return_arg, { :required => false }) + }.not_to raise_error + end + + it "#{method} should set and return @name, then return @name for foo when argument is nil" do + value = "meow" + expect(@vo.send(method,:name, value, { }).object_id).to eq(value.object_id) + expect(@vo.send(method,:foo, return_arg, { :name_attribute => true }).object_id).to eq(value.object_id) + end + + it "#{method} should allow DelayedEvaluator instance to be set for value regardless of restriction" do + value = Chef::DelayedEvaluator.new{ 'test' } + @vo.send(method,:test, value, {:kind_of => Numeric}) + end + + it "#{method} should raise an error when delayed evaluated attribute is not valid" do + value = Chef::DelayedEvaluator.new{ 'test' } + @vo.send(method,:test, value, {:kind_of => Numeric}) + expect do + @vo.send(method,:test, return_arg, {:kind_of => Numeric}) + end.to raise_error(Chef::Exceptions::ValidationFailed) + end + + it "#{method} should create DelayedEvaluator instance when #lazy is used" do + @vo.send(method,:delayed, @vo.lazy{ 'test' }, {}) + expect(@vo.instance_variable_get(:@delayed)).to be_a(Chef::DelayedEvaluator) + end + + it "#{method} should execute block on each call when DelayedEvaluator" do + value = 'fubar' + @vo.send(method,:test, @vo.lazy{ value }, {}) + expect(@vo.send(method,:test, return_arg, {})).to eq('fubar') + value = 'foobar' + expect(@vo.send(method,:test, return_arg, {})).to eq('foobar') + value = 'fauxbar' + expect(@vo.send(method,:test, return_arg, {})).to eq('fauxbar') + end + + it "#{method} should not evaluate non DelayedEvaluator instances" do + value = lambda{ 'test' } + @vo.send(method,:test, value, {}) + expect(@vo.send(method,:test, return_arg, {}).object_id).to eq(value.object_id) + expect(@vo.send(method,:test, return_arg, {})).to be_a(Proc) + end end - it "should set and return a default value when the argument is nil, then return the same value" do - value = "meow" - expect(@vo.set_or_return(:test, nil, { :default => value }).object_id).to eq(value.object_id) - expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id) - end - - it "should raise an ArgumentError when argument is nil and required is true" do - expect { - @vo.set_or_return(:test, nil, { :required => true }) - }.to raise_error(ArgumentError) - end - - it "should not raise an error when argument is nil and required is false" do - expect { - @vo.set_or_return(:test, nil, { :required => false }) - }.not_to raise_error - end - - it "should set and return @name, then return @name for foo when argument is nil" do - value = "meow" - expect(@vo.set_or_return(:name, value, { }).object_id).to eq(value.object_id) - expect(@vo.set_or_return(:foo, nil, { :name_attribute => true }).object_id).to eq(value.object_id) - end + test_set_or_return_method(:set_or_return) + test_set_or_return_method(:nillable_set_or_return) - it "should allow DelayedEvaluator instance to be set for value regardless of restriction" do - value = Chef::DelayedEvaluator.new{ 'test' } - @vo.set_or_return(:test, value, {:kind_of => Numeric}) + it "nillable_set_or_return supports nilling values" do + expect(@vo.nillable_set_or_return(:test, "meow", {})).to eq("meow") + expect(@vo.nillable_set_or_return(:test, TinyClass::NULL_ARG, {})).to eq("meow") + expect(@vo.nillable_set_or_return(:test, nil, {})).to be_nil + expect(@vo.nillable_set_or_return(:test, TinyClass::NULL_ARG, {})).to be_nil end - - it "should raise an error when delayed evaluated attribute is not valid" do - value = Chef::DelayedEvaluator.new{ 'test' } - @vo.set_or_return(:test, value, {:kind_of => Numeric}) - expect do - @vo.set_or_return(:test, nil, {:kind_of => Numeric}) - end.to raise_error(Chef::Exceptions::ValidationFailed) - end - - it "should create DelayedEvaluator instance when #lazy is used" do - @vo.set_or_return(:delayed, @vo.lazy{ 'test' }, {}) - expect(@vo.instance_variable_get(:@delayed)).to be_a(Chef::DelayedEvaluator) - end - - it "should execute block on each call when DelayedEvaluator" do - value = 'fubar' - @vo.set_or_return(:test, @vo.lazy{ value }, {}) - expect(@vo.set_or_return(:test, nil, {})).to eq('fubar') - value = 'foobar' - expect(@vo.set_or_return(:test, nil, {})).to eq('foobar') - value = 'fauxbar' - expect(@vo.set_or_return(:test, nil, {})).to eq('fauxbar') - end - - it "should not evaluate non DelayedEvaluator instances" do - value = lambda{ 'test' } - @vo.set_or_return(:test, value, {}) - expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id) - expect(@vo.set_or_return(:test, nil, {})).to be_a(Proc) - end - end diff --git a/spec/unit/mixin/powershell_type_coercions_spec.rb b/spec/unit/mixin/powershell_type_coercions_spec.rb new file mode 100644 index 0000000000..988c3926c1 --- /dev/null +++ b/spec/unit/mixin/powershell_type_coercions_spec.rb @@ -0,0 +1,72 @@ +# +# Author:: Jay Mundrawala (<jdm@chef.io>) +# Copyright:: Copyright (c) 2015 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 'chef/mixin/powershell_type_coercions' +require 'base64' + +class Chef::PSTypeTester + include Chef::Mixin::PowershellTypeCoercions +end + +describe Chef::Mixin::PowershellTypeCoercions do + let (:test_class) { Chef::PSTypeTester.new } + + describe '#translate_type' do + it 'should single quote a string' do + expect(test_class.translate_type('foo')).to eq("'foo'") + end + + ["'", '"', '#', '`'].each do |c| + it "should base64 encode a string that contains #{c}" do + expect(test_class.translate_type("#{c}")).to match(Base64.strict_encode64(c)) + end + end + + it 'should not quote an integer' do + expect(test_class.translate_type(123)).to eq('123') + end + + it 'should not quote a floating point number' do + expect(test_class.translate_type(123.4)).to eq('123.4') + end + + it 'should return $false when an instance of FalseClass is provided' do + expect(test_class.translate_type(false)).to eq('$false') + end + + it 'should return $true when an instance of TrueClass is provided' do + expect(test_class.translate_type(true)).to eq('$true') + end + + it 'should translate all members of a hash and wrap them in @{} separated by ;' do + expect(test_class.translate_type({"a" => 1, "b" => 1.2, "c" => false, "d" => true + })).to eq("@{a=1;b=1.2;c=$false;d=$true}") + end + + it 'should translat all members of an array and them by a ,' do + expect(test_class.translate_type([true, false])).to eq('@($true,$false)') + end + + it 'should fall back :to_psobject if we have not defined at explicit rule' do + ps_obj = double("PSObject") + expect(ps_obj).to receive(:to_psobject).and_return('$true') + expect(test_class.translate_type(ps_obj)).to eq('($true)') + end + end +end diff --git a/spec/unit/platform/query_helpers_spec.rb b/spec/unit/platform/query_helpers_spec.rb index 7aafc287ea..1dbd07a021 100644 --- a/spec/unit/platform/query_helpers_spec.rb +++ b/spec/unit/platform/query_helpers_spec.rb @@ -53,3 +53,25 @@ describe 'Chef::Platform#supports_dsc?' do end end end + +describe 'Chef::Platform#supports_dsc_invoke_resource?' do + it 'returns false if powershell is not present' do + node = Chef::Node.new + expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey + end + + ['1.0', '2.0', '3.0', '4.0', '5.0.10017.9'].each do |version| + it "returns false for Powershell #{version}" do + node = Chef::Node.new + node.automatic[:languages][:powershell][:version] = version + expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey + end + end + + it "returns true for Powershell 5.0.10018.0" do + node = Chef::Node.new + node.automatic[:languages][:powershell][:version] = "5.0.10018.0" + expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_truthy + end +end + diff --git a/spec/unit/platform_spec.rb b/spec/unit/platform_spec.rb index fb65ef0fea..e0115bc42a 100644 --- a/spec/unit/platform_spec.rb +++ b/spec/unit/platform_spec.rb @@ -20,8 +20,6 @@ require 'spec_helper' describe "Chef::Platform supports" do [ - :mac_os_x, - :mac_os_x_server, :freebsd, :ubuntu, :debian, @@ -34,9 +32,6 @@ describe "Chef::Platform supports" do :gentoo, :arch, :solaris, - :mswin, - :mingw32, - :windows, :gcel, :ibm_powerkvm ].each do |platform| diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb new file mode 100644 index 0000000000..0a6c22bdcf --- /dev/null +++ b/spec/unit/provider/dsc_resource_spec.rb @@ -0,0 +1,84 @@ +# +# Author:: Jay Mundrawala (<jdm@chef.io>) +# +# Copyright:: Copyright (c) 2014 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 'chef' +require 'spec_helper' + +describe Chef::Provider::DscResource do + let (:events) { Chef::EventDispatch::Dispatcher.new } + let (:run_context) { Chef::RunContext.new(node, {}, events) } + let (:resource) { Chef::Resource::DscResource.new("dscresource", run_context) } + let (:provider) do + Chef::Provider::DscResource.new(resource, run_context) + end + + context 'when Powershell does not support Invoke-DscResource' do + let (:node) { + node = Chef::Node.new + node.automatic[:languages][:powershell][:version] = '4.0' + node + } + + it 'raises a NoProviderAvailable exception' do + expect(provider).not_to receive(:meta_configuration) + expect{provider.run_action(:run)}.to raise_error( + Chef::Exceptions::NoProviderAvailable, /5\.0\.10018\.0/) + end + end + + context 'when Powershell supports Invoke-DscResource' do + let (:node) { + node = Chef::Node.new + node.automatic[:languages][:powershell][:version] = '5.0.10018.0' + node + } + + context 'when RefreshMode is not set to Disabled' do + let (:meta_configuration) { {'RefreshMode' => 'AnythingElse'}} + + it 'raises an exception' do + expect(provider).to receive(:meta_configuration).and_return( + meta_configuration) + expect { provider.run_action(:run) }.to raise_error( + Chef::Exceptions::NoProviderAvailable, /Disabled/) + end + end + + context 'when RefreshMode is set to Disabled' do + let (:meta_configuration) { {'RefreshMode' => 'Disabled'}} + + it 'does not update the resource if it is up to date' do + expect(provider).to receive(:meta_configuration).and_return( + meta_configuration) + expect(provider).to receive(:test_resource).and_return(true) + provider.run_action(:run) + expect(resource).not_to be_updated + end + + it 'converges the resource if it is not up to date' do + expect(provider).to receive(:meta_configuration).and_return( + meta_configuration) + expect(provider).to receive(:test_resource).and_return(false) + expect(provider).to receive(:set_resource) + provider.run_action(:run) + expect(resource).to be_updated + end + end + end +end diff --git a/spec/unit/provider/package/openbsd_spec.rb b/spec/unit/provider/package/openbsd_spec.rb index ee9c9e89fb..b0cdb9969d 100644 --- a/spec/unit/provider/package/openbsd_spec.rb +++ b/spec/unit/provider/package/openbsd_spec.rb @@ -21,28 +21,116 @@ require 'ostruct' describe Chef::Provider::Package::Openbsd do + let(:node) do + node = Chef::Node.new + node.default['kernel'] = {'name' => 'OpenBSD', 'release' => '5.5', 'machine' => 'amd64'} + node + end + + let (:provider) do + events = Chef::EventDispatch::Dispatcher.new + run_context = Chef::RunContext.new(node, {}, events) + Chef::Provider::Package::Openbsd.new(new_resource, run_context) + end + + let(:new_resource) { Chef::Resource::Package.new(name)} + before(:each) do - @node = Chef::Node.new - @node.default['kernel'] = {'name' => 'OpenBSD', 'release' => '5.5', 'machine' => 'amd64'} - @events = Chef::EventDispatch::Dispatcher.new - @run_context = Chef::RunContext.new(@node, {}, @events) ENV['PKG_PATH'] = nil end describe "install a package" do - before do - @name = 'ihavetoes' - @new_resource = Chef::Resource::Package.new(@name) - @current_resource = Chef::Resource::Package.new(@name) - @provider = Chef::Provider::Package::Openbsd.new(@new_resource, @run_context) - @provider.current_resource = @current_resource - end - it "should run the installation command" do - expect(@provider).to receive(:shell_out!).with( - "pkg_add -r #{@name}", - {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}} - ) {OpenStruct.new :status => true} - @provider.install_package(@name, nil) + let(:name) { 'ihavetoes' } + let(:version) {'0.0'} + + context 'when not already installed' do + before do + allow(provider).to receive(:shell_out!).with("pkg_info -e \"#{name}->0\"", anything()).and_return(instance_double('shellout', :stdout => '')) + end + + context 'when there is a single candidate' do + + context 'when installing from source' do + it 'should run the installation command' do + pending('Installing from source is not supported yet') + # This is a consequence of load_current_resource being called before define_resource_requirements + # It can be deleted once an implementation is provided + allow(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return( + instance_double('shellout', :stdout => "#{name}-#{version}\n")) + new_resource.source('/some/path/on/disk.tgz') + provider.run_action(:install) + end + end + + context 'when source is not provided' do + it 'should run the installation command' do + expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return( + instance_double('shellout', :stdout => "#{name}-#{version}\n")) + expect(provider).to receive(:shell_out!).with( + "pkg_add -r #{name}-#{version}", + {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}} + ) {OpenStruct.new :status => true} + provider.run_action(:install) + end + end + end + + context 'when there are multiple candidates' do + let(:flavor_a) { 'flavora' } + let(:flavor_b) { 'flavorb' } + + context 'if no version is specified' do + it 'should raise an exception' do + expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return( + instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor_a}\n#{name}-#{version}-#{flavor_b}\n")) + expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /multiple matching candidates/) + end + end + + context 'if a flavor is specified' do + + let(:flavor) { 'flavora' } + let(:package_name) {'ihavetoes' } + let(:name) { "#{package_name}--#{flavor}" } + + context 'if no version is specified' do + it 'should run the installation command' do + expect(provider).to receive(:shell_out!).with("pkg_info -e \"#{package_name}->0\"", anything()).and_return(instance_double('shellout', :stdout => '')) + expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return( + instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor}\n")) + expect(provider).to receive(:shell_out!).with( + "pkg_add -r #{name}-#{version}-#{flavor}", + {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}} + ) {OpenStruct.new :status => true} + provider.run_action(:install) + end + end + + context 'if a version is specified' do + it 'runs the installation command' do + pending('Specifying both a version and flavor is not supported') + new_resource.version(version) + allow(provider).to receive(:shell_out!).with(/pkg_info -e/, anything()).and_return(instance_double('shellout', :stdout => '')) + allow(provider).to receive(:candidate_version).and_return("#{package_name}-#{version}-#{flavor}") + provider.run_action(:install) + end + end + end + + context 'if a version is specified' do + it 'should use the flavor from the version' do + expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}-#{version}-#{flavor_b}\"", anything()).and_return( + instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor_a}\n")) + + new_resource.version("#{version}-#{flavor_b}") + expect(provider).to receive(:shell_out!).with( + "pkg_add -r #{name}-#{version}-#{flavor_b}", + {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}} + ) {OpenStruct.new :status => true} + provider.run_action(:install) + end + end + end end end diff --git a/spec/unit/provider/service/macosx_spec.rb b/spec/unit/provider/service/macosx_spec.rb index fb751592df..9905a6e4ae 100644 --- a/spec/unit/provider/service/macosx_spec.rb +++ b/spec/unit/provider/service/macosx_spec.rb @@ -22,17 +22,17 @@ describe Chef::Provider::Service::Macosx do describe ".gather_plist_dirs" do context "when HOME directory is set" do before do - allow(ENV).to receive(:[]).with('HOME').and_return("/User/someuser") + allow(Chef::Util::PathHelper).to receive(:home).with('Library', 'LaunchAgents').and_yield('/Users/someuser/Library/LaunchAgents') end it "includes users's LaunchAgents folder" do - expect(described_class.gather_plist_dirs).to include("#{ENV['HOME']}/Library/LaunchAgents") + expect(described_class.gather_plist_dirs).to include("/Users/someuser/Library/LaunchAgents") end end context "when HOME directory is not set" do before do - allow(ENV).to receive(:[]).with('HOME').and_return(nil) + allow(Chef::Util::PathHelper).to receive(:home).with('Library', 'LaunchAgents').and_return(nil) end it "doesn't include user's LaunchAgents folder" do diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb index a9fa08ebfd..63c381f08e 100644 --- a/spec/unit/provider_resolver_spec.rb +++ b/spec/unit/provider_resolver_spec.rb @@ -452,6 +452,137 @@ describe Chef::ProviderResolver do end + describe "for the package provider" do + let(:resource_name) { :package } + + before do + expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup) + end + + %w{mac_os_x mac_os_x_server}.each do |platform| + describe "on #{platform}" do + let(:os) { "darwin" } + let(:platform) { platform } + let(:platform_family) { "mac_os_x" } + let(:platform_version) { "10.9.2" } + + + it "returns a Chef::Provider::Package::Homebrew provider" do + expect(resolved_provider).to eql(Chef::Provider::Package::Homebrew) + end + end + end + end + + provider_mapping = { + "mac_os_x" => { + :package => Chef::Provider::Package::Homebrew, + :user => Chef::Provider::User::Dscl, + :group => Chef::Provider::Group::Dscl, + }, + "mac_os_x_server" => { + :package => Chef::Provider::Package::Homebrew, + :user => Chef::Provider::User::Dscl, + :group => Chef::Provider::Group::Dscl, + }, + "mswin" => { + :env => Chef::Provider::Env::Windows, + :user => Chef::Provider::User::Windows, + :group => Chef::Provider::Group::Windows, + :mount => Chef::Provider::Mount::Windows, + :batch => Chef::Provider::Batch, + :powershell_script => Chef::Provider::PowershellScript, + }, + "mingw32" => { + :env => Chef::Provider::Env::Windows, + :user => Chef::Provider::User::Windows, + :group => Chef::Provider::Group::Windows, + :mount => Chef::Provider::Mount::Windows, + :batch => Chef::Provider::Batch, + :powershell_script => Chef::Provider::PowershellScript, + }, + "windows" => { + :env => Chef::Provider::Env::Windows, + :user => Chef::Provider::User::Windows, + :group => Chef::Provider::Group::Windows, + :mount => Chef::Provider::Mount::Windows, + :batch => Chef::Provider::Batch, + :powershell_script => Chef::Provider::PowershellScript, + }, + "aix" => { + :cron => Chef::Provider::Cron::Aix, + }, + "netbsd"=> { + :group => Chef::Provider::Group::Groupmod, + }, + "openbsd" => { + :group => Chef::Provider::Group::Usermod, + :package => Chef::Provider::Package::Openbsd, + }, + } + + def self.do_platform(platform_hash) + platform_hash.each do |resource, provider| + describe "for #{resource}" do + let(:resource_name) { resource } + + it "resolves to a #{provider}" do + expect(resolved_provider).to eql(provider) + end + end + end + end + + describe "individual platform mappings" do + let(:resource_name) { :user } + + before do + expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup) + end + + %w{mac_os_x mac_os_x_server}.each do |platform| + describe "on #{platform}" do + let(:os) { "darwin" } + let(:platform) { platform } + let(:platform_family) { "mac_os_x" } + let(:platform_version) { "10.9.2" } + + do_platform(provider_mapping[platform]) + end + end + + %w{mswin mingw32 windows}.each do |platform| + describe "on #{platform}" do + let(:os) { "windows" } + let(:platform) { platform } + let(:platform_family) { "windows" } + let(:platform_version) { "10.9.2" } + + do_platform(provider_mapping[platform]) + end + end + + describe "on AIX" do + let(:os) { "aix" } + let(:platform) { "aix" } + let(:platform_family) { "aix" } + let(:platform_version) { "6.2" } + + do_platform(provider_mapping['aix']) + end + + %w{netbsd openbsd}.each do |platform| + describe "on #{platform}" do + let(:os) { platform } + let(:platform) { platform } + let(:platform_family) { platform } + let(:platform_version) { "10.0-RELEASE" } + + do_platform(provider_mapping[platform]) + end + end + end + describe "resolving static providers" do def resource_class(resource) Chef::Resource.const_get(convert_to_class_name(resource.to_s)) @@ -481,6 +612,7 @@ describe Chef::ProviderResolver do link: Chef::Provider::Link, log: Chef::Provider::Log::ChefLog, macports_package: Chef::Provider::Package::Macports, + mdadm: Chef::Provider::Mdadm, pacman_package: Chef::Provider::Package::Pacman, paludis_package: Chef::Provider::Package::Paludis, perl: Chef::Provider::Script, diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb index 8d0b1bcfd2..e1604483f3 100644 --- a/spec/unit/recipe_spec.rb +++ b/spec/unit/recipe_spec.rb @@ -593,5 +593,9 @@ describe Chef::Recipe do expect(recipe.singleton_class.included_modules).to include(Chef::DSL::Audit) expect(recipe.respond_to?(:control_group)).to be true end + + it "should respond to :ps_credential from Chef::DSL::Powershell" do + expect(recipe.respond_to?(:ps_credential)).to be true + end end end diff --git a/spec/unit/resource/deploy_spec.rb b/spec/unit/resource/deploy_spec.rb index 0403a7ba6b..07f5f973c0 100644 --- a/spec/unit/resource/deploy_spec.rb +++ b/spec/unit/resource/deploy_spec.rb @@ -30,12 +30,35 @@ describe Chef::Resource::Deploy do class << self + + def resource_has_a_hash_attribute(attr_name) + it "has a Hash attribute for #{attr_name.to_s}" do + @resource.send(attr_name, {foo: "bar"}) + expect(@resource.send(attr_name)).to eql({foo: "bar"}) + expect {@resource.send(attr_name, 8675309)}.to raise_error(ArgumentError) + end + + it "the Hash attribute for #{attr_name.to_s} is nillable" do + @resource.send(attr_name, {foo: "bar"}) + expect(@resource.send(attr_name)).to eql({foo: "bar"}) + @resource.send(attr_name, nil) + expect(@resource.send(attr_name)).to eql(nil) + end + end + def resource_has_a_string_attribute(attr_name) it "has a String attribute for #{attr_name.to_s}" do @resource.send(attr_name, "this is a string") expect(@resource.send(attr_name)).to eql("this is a string") expect {@resource.send(attr_name, 8675309)}.to raise_error(ArgumentError) end + + it "the String attribute for #{attr_name.to_s} is nillable" do + @resource.send(attr_name, "this is a string") + expect(@resource.send(attr_name)).to eql("this is a string") + @resource.send(attr_name, nil) + expect(@resource.send(attr_name)).to eql(nil) + end end def resource_has_a_boolean_attribute(attr_name, opts={:defaults_to=>false}) @@ -189,6 +212,10 @@ describe Chef::Resource::Deploy do expect(@resource.symlink_before_migrate).to eq({"wtf?" => "wtf is going on"}) end + resource_has_a_hash_attribute :symlink_before_migrate + resource_has_a_hash_attribute :symlinks + resource_has_a_hash_attribute :additional_remotes + resource_has_a_callback_attribute :before_migrate resource_has_a_callback_attribute :before_symlink resource_has_a_callback_attribute :before_restart diff --git a/spec/unit/resource/dsc_resource_spec.rb b/spec/unit/resource/dsc_resource_spec.rb new file mode 100644 index 0000000000..ae15f56eaf --- /dev/null +++ b/spec/unit/resource/dsc_resource_spec.rb @@ -0,0 +1,85 @@ +# +# Author:: Adam Edwards (<adamed@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Resource::DscResource do + let(:dsc_test_resource_name) { 'DSCTest' } + let(:dsc_test_property_name) { :DSCTestProperty } + let(:dsc_test_property_value) { 'DSCTestValue' } + + context 'when Powershell supports Dsc' do + let(:dsc_test_run_context) { + node = Chef::Node.new + node.automatic[:languages][:powershell][:version] = '5.0.10018.0' + empty_events = Chef::EventDispatch::Dispatcher.new + Chef::RunContext.new(node, {}, empty_events) + } + let(:dsc_test_resource) { + Chef::Resource::DscResource.new(dsc_test_resource_name, dsc_test_run_context) + } + + it "has a default action of `:run`" do + expect(dsc_test_resource.action).to eq(:run) + end + + it "has an allowed_actions attribute with only the `:run` and `:nothing` attributes" do + expect(dsc_test_resource.allowed_actions.to_set).to eq([:run,:nothing].to_set) + end + + it "allows the resource attribute to be set" do + dsc_test_resource.resource(dsc_test_resource_name) + expect(dsc_test_resource.resource).to eq(dsc_test_resource_name) + end + + it "allows the module_name attribute to be set" do + dsc_test_resource.module_name(dsc_test_resource_name) + expect(dsc_test_resource.module_name).to eq(dsc_test_resource_name) + end + + context "when setting a dsc property" do + it "allows setting a dsc property with a property name of type Symbol" do + dsc_test_resource.property(dsc_test_property_name, dsc_test_property_value) + expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value) + expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value) + end + + it "raises a TypeError if property_name is not a symbol" do + expect{ + dsc_test_resource.property('Foo', dsc_test_property_value) + }.to raise_error(TypeError) + end + + context "when using DelayedEvaluators" do + it "allows setting a dsc property with a property name of type Symbol" do + dsc_test_resource.property(dsc_test_property_name, Chef::DelayedEvaluator.new { + dsc_test_property_value + }) + expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value) + expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value) + end + end + end + + context 'Powershell DSL methods' do + it "responds to :ps_credential" do + expect(dsc_test_resource.respond_to?(:ps_credential)).to be true + end + end + end +end diff --git a/spec/unit/shell_spec.rb b/spec/unit/shell_spec.rb index 0e028f4359..617abcfde2 100644 --- a/spec/unit/shell_spec.rb +++ b/spec/unit/shell_spec.rb @@ -56,7 +56,7 @@ describe Shell do describe "configuring IRB" do it "configures irb history" do Shell.configure_irb - expect(Shell.irb_conf[:HISTORY_FILE]).to eq("~/.chef/chef_shell_history") + expect(Shell.irb_conf[:HISTORY_FILE]).to eq("#{ENV['HOME']}/.chef/chef_shell_history") expect(Shell.irb_conf[:SAVE_HISTORY]).to eq(1000) end diff --git a/spec/unit/util/dsc/resource_store.rb b/spec/unit/util/dsc/resource_store.rb new file mode 100644 index 0000000000..a89e73fcaa --- /dev/null +++ b/spec/unit/util/dsc/resource_store.rb @@ -0,0 +1,76 @@ +# +# Author:: Jay Mundrawala <jdm@chef.io> +# Copyright:: Copyright (c) 2015 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 'chef' +require 'chef/util/dsc/resource_store' + +describe Chef::Util::DSC::ResourceStore do + let(:resource_store) { Chef::Util::DSC::ResourceStore.new } + let(:resource_a) { { + 'ResourceType' => 'AFoo', + 'Name' => 'Foo', + 'Module' => {'Name' => 'ModuleA'} + } + } + + let(:resource_b) { { + 'ResourceType' => 'BFoo', + 'Name' => 'Foo', + 'Module' => {'Name' => 'ModuleB'} + } + } + + context 'when resources are not cached' do + context 'when calling #resources' do + it 'returns an empty array' do + expect(resource_store.resources).to eql([]) + end + end + + context 'when calling #find' do + it 'returns an empty list if it cannot find any matching resources' do + expect(resource_store).to receive(:query_resource).and_return([]) + expect(resource_store.find('foo')).to eql([]) + end + + it 'returns the resource if it is found (comparisons are case insensitive)' do + expect(resource_store).to receive(:query_resource).and_return([resource_a]) + expect(resource_store.find('foo')).to eql([resource_a]) + end + + it 'returns multiple resoures if they are found' do + expect(resource_store).to receive(:query_resource).and_return([resource_a, resource_b]) + expect(resource_store.find('foo')).to include(resource_a, resource_b) + end + + it 'deduplicates resources by ResourceName' do + expect(resource_store).to receive(:query_resource).and_return([resource_a, resource_a]) + resource_store.find('foo') + expect(resource_store.resources).to eq([resource_a]) + end + end + end + + context 'when resources are cached' do + it 'recalls resources from the cache if present' do + expect(resource_store).not_to receive(:query_resource) + expect(resource_store).to receive(:resources).and_return([resource_a]) + resource_store.find('foo') + end + end +end diff --git a/spec/unit/util/powershell/ps_credential_spec.rb b/spec/unit/util/powershell/ps_credential_spec.rb new file mode 100644 index 0000000000..bac58b02e5 --- /dev/null +++ b/spec/unit/util/powershell/ps_credential_spec.rb @@ -0,0 +1,37 @@ +# +# Author:: Jay Mundrawala <jdm@chef.io> +# Copyright:: Copyright (c) 2015 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 'chef' +require 'chef/util/powershell/ps_credential' + +describe Chef::Util::Powershell::PSCredential do + let (:username) { 'foo' } + let (:password) { 'password' } + + context 'when username and password are provided' do + let(:ps_credential) { Chef::Util::Powershell::PSCredential.new(username, password)} + context 'when calling to_psobject' do + it 'should create the script to create a PSCredential when calling' do + allow(ps_credential).to receive(:encrypt).with(password).and_return('encrypted') + expect(ps_credential.to_psobject).to eq( + "New-Object System.Management.Automation.PSCredential("\ + "'#{username}',('encrypted' | ConvertTo-SecureString))") + end + end + end +end diff --git a/spec/unit/workstation_config_loader_spec.rb b/spec/unit/workstation_config_loader_spec.rb index a865103188..72631f3dfa 100644 --- a/spec/unit/workstation_config_loader_spec.rb +++ b/spec/unit/workstation_config_loader_spec.rb @@ -65,7 +65,7 @@ describe Chef::WorkstationConfigLoader do let(:home) { "/Users/example.user" } before do - env["HOME"] = home + allow(Chef::Util::PathHelper).to receive(:home).with('.chef').and_yield(File.join(home, '.chef')) allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/knife.rb").and_return(true) end |