diff options
Diffstat (limited to 'spec/unit')
33 files changed, 1170 insertions, 222 deletions
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb index ea2ad473e5..c753ca0ab8 100644 --- a/spec/unit/application/client_spec.rb +++ b/spec/unit/application/client_spec.rb @@ -131,6 +131,16 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config end + describe "when --no-listen is set" do + + it "configures listen = false" do + app.config[:listen] = false + app.reconfigure + expect(Chef::Config[:listen]).to eq(false) + end + + end + describe "when the json_attribs configuration option is specified" do let(:json_attribs) { {"a" => "b"} } @@ -305,7 +315,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 +330,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/client_spec.rb b/spec/unit/client_spec.rb index 2ec32b32ac..fa8317744c 100644 --- a/spec/unit/client_spec.rb +++ b/spec/unit/client_spec.rb @@ -24,6 +24,9 @@ require 'chef/run_context' require 'chef/rest' require 'rbconfig' +class FooError < RuntimeError +end + describe Chef::Client do let(:hostname) { "hostname" } @@ -75,6 +78,19 @@ describe Chef::Client do allow(Ohai::System).to receive(:new).and_return(ohai_system) end + context "when minimal ohai is configured" do + before do + Chef::Config[:minimal_ohai] = true + end + + it "runs ohai with only the minimum required plugins" do + expected_filter = %w[fqdn machinename hostname platform platform_version os os_version] + expect(ohai_system).to receive(:all_plugins).with(expected_filter) + client.run_ohai + end + + end + describe "authentication protocol selection" do after do Chef::Config[:authentication_protocol_version] = "1.0" @@ -428,34 +444,80 @@ describe Chef::Client do describe "when the audit phase fails" do context "with an exception" do - include_context "a client run" do - let(:e) { Exception.new } - def stub_for_audit - expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner) - expect(audit_runner).to receive(:run).and_raise(e) - expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError) + context "when audit mode is enabled" do + include_context "a client run" do + let(:e) { Exception.new } + def stub_for_audit + expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner) + expect(audit_runner).to receive(:run).and_raise(e) + expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError) + end + + def stub_for_run + expect_any_instance_of(Chef::RunLock).to receive(:acquire) + expect_any_instance_of(Chef::RunLock).to receive(:save_pid) + expect_any_instance_of(Chef::RunLock).to receive(:release) + + # Post conditions: check that node has been filled in correctly + expect(client).to receive(:run_started) + expect(client).to receive(:run_failed) + + expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed) + expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed) + end end - def stub_for_run - expect_any_instance_of(Chef::RunLock).to receive(:acquire) - expect_any_instance_of(Chef::RunLock).to receive(:save_pid) - expect_any_instance_of(Chef::RunLock).to receive(:release) - - # Post conditions: check that node has been filled in correctly - expect(client).to receive(:run_started) - expect(client).to receive(:run_failed) - - expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed) - expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed) + it "should save the node after converge and raise exception" do + expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error| + expect(error.wrapped_errors.size).to eq(1) + expect(error.wrapped_errors[0]).to eq(e) + end end end - it "should save the node after converge and raise exception" do - expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error| - expect(error.wrapped_errors.size).to eq(1) - expect(error.wrapped_errors[0]).to eq(e) + context "when audit mode is disabled" do + include_context "a client run" do + before do + Chef::Config[:audit_mode] = :disabled + end + + let(:e) { FooError.new } + + def stub_for_audit + expect(Chef::Audit::Runner).to_not receive(:new) + end + + def stub_for_converge + expect(Chef::Runner).to receive(:new).and_return(runner) + expect(runner).to receive(:converge).and_raise(e) + expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(FooError) + end + + def stub_for_node_save + expect(client).to_not receive(:save_updated_node) + end + + def stub_for_run + expect_any_instance_of(Chef::RunLock).to receive(:acquire) + expect_any_instance_of(Chef::RunLock).to receive(:save_pid) + expect_any_instance_of(Chef::RunLock).to receive(:release) + + + # Post conditions: check that node has been filled in correctly + expect(client).to receive(:run_started) + expect(client).to receive(:run_failed) + + expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed) + + end + + it "re-raises an unwrapped exception" do + expect { client.run }.to raise_error(FooError) + end end end + + end context "with failed audits" do 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/cookbook/file_vendor_spec.rb b/spec/unit/cookbook/file_vendor_spec.rb index 4fad7d5808..145541a63f 100644 --- a/spec/unit/cookbook/file_vendor_spec.rb +++ b/spec/unit/cookbook/file_vendor_spec.rb @@ -21,9 +21,6 @@ describe Chef::Cookbook::FileVendor do let(:file_vendor_class) { Class.new(described_class) } - # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest - let(:manifest) { {:cookbook_name => "bob"} } - context "when configured to fetch files over http" do let(:http) { double("Chef::REST") } @@ -40,19 +37,42 @@ describe Chef::Cookbook::FileVendor do expect(file_vendor_class.initialization_options).to eq(http) end - it "creates a RemoteFileVendor for a given manifest" do - file_vendor = file_vendor_class.create_from_manifest(manifest) - expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor) - expect(file_vendor.rest).to eq(http) - expect(file_vendor.cookbook_name).to eq("bob") + context "with a manifest from a cookbook version" do + + # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest + let(:manifest) { {:cookbook_name => "bob", :name => "bob-1.2.3"} } + + it "creates a RemoteFileVendor for a given manifest" do + file_vendor = file_vendor_class.create_from_manifest(manifest) + expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor) + expect(file_vendor.rest).to eq(http) + expect(file_vendor.cookbook_name).to eq("bob") + end + end + context "with a manifest from a cookbook artifact" do + + # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest + let(:manifest) { {:name => "bob"} } + + it "creates a RemoteFileVendor for a given manifest" do + file_vendor = file_vendor_class.create_from_manifest(manifest) + expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor) + expect(file_vendor.rest).to eq(http) + expect(file_vendor.cookbook_name).to eq("bob") + end + + end end context "when configured to load files from disk" do let(:cookbook_path) { %w[/var/chef/cookbooks /var/chef/other_cookbooks] } + # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest + let(:manifest) { {:cookbook_name => "bob"} } + before do file_vendor_class.fetch_from_disk(cookbook_path) end diff --git a/spec/unit/cookbook_manifest_spec.rb b/spec/unit/cookbook_manifest_spec.rb index 938f72c743..f985942e09 100644 --- a/spec/unit/cookbook_manifest_spec.rb +++ b/spec/unit/cookbook_manifest_spec.rb @@ -24,6 +24,8 @@ describe Chef::CookbookManifest do let(:version) { "1.2.3" } + let(:identifier) { "9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b" } + let(:metadata) do Chef::Cookbook::Metadata.new.tap do |m| m.version(version) @@ -35,6 +37,7 @@ describe Chef::CookbookManifest do let(:cookbook_version) do Chef::CookbookVersion.new("tatft", cookbook_root).tap do |c| c.metadata = metadata + c.identifier = identifier end end @@ -212,12 +215,26 @@ describe Chef::CookbookManifest do let(:policy_mode) { true } + let(:cookbook_manifest_hash) { cookbook_manifest.to_hash } + + it "sets the identifier in the manifest data" do + expect(cookbook_manifest_hash["identifier"]).to eq("9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b") + end + + it "sets the name to just the name" do + expect(cookbook_manifest_hash["name"]).to eq("tatft") + end + + it "does not set a 'cookbook_name' field" do + expect(cookbook_manifest_hash).to_not have_key("cookbook_name") + end + it "gives the save URL" do - expect(cookbook_manifest.save_url).to eq("cookbook_artifacts/tatft/1.2.3") + expect(cookbook_manifest.save_url).to eq("cookbook_artifacts/tatft/9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b") end it "gives the force save URL" do - expect(cookbook_manifest.force_save_url).to eq("cookbook_artifacts/tatft/1.2.3?force=true") + expect(cookbook_manifest.force_save_url).to eq("cookbook_artifacts/tatft/9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b?force=true") end end diff --git a/spec/unit/cookbook_uploader_spec.rb b/spec/unit/cookbook_uploader_spec.rb index 152e5373f0..76727c18e2 100644 --- a/spec/unit/cookbook_uploader_spec.rb +++ b/spec/unit/cookbook_uploader_spec.rb @@ -25,11 +25,17 @@ describe Chef::CookbookUploader do let(:cookbook_loader) do loader = Chef::CookbookLoader.new(File.join(CHEF_SPEC_DATA, "cookbooks")) loader.load_cookbooks + loader.cookbooks_by_name["apache2"].identifier = apache2_identifier + loader.cookbooks_by_name["java"].identifier = java_identifier loader end + let(:apache2_identifier) { "6644e6cb2ade90b8aff2ebb44728958fbc939ebf" } + let(:apache2_cookbook) { cookbook_loader.cookbooks_by_name["apache2"] } + let(:java_identifier) { "edd40c30c4e0ebb3658abde4620597597d2e9c17" } + let(:java_cookbook) { cookbook_loader.cookbooks_by_name["java"] } let(:cookbooks_to_upload) { [apache2_cookbook, java_cookbook] } @@ -175,7 +181,7 @@ describe Chef::CookbookUploader do let(:policy_mode) { true } def expected_save_url(cookbook) - "cookbook_artifacts/#{cookbook.name}/#{cookbook.version}" + "cookbook_artifacts/#{cookbook.name}/#{cookbook.identifier}" end it "uploads all files in a sandbox transaction, then creates cookbooks on the server using cookbook_artifacts API" do diff --git a/spec/unit/http/socketless_chef_zero_client_spec.rb b/spec/unit/http/socketless_chef_zero_client_spec.rb new file mode 100644 index 0000000000..963cc9e8c4 --- /dev/null +++ b/spec/unit/http/socketless_chef_zero_client_spec.rb @@ -0,0 +1,174 @@ +#-- +# Author:: Daniel DeLeo (<dan@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/http/socketless_chef_zero_client' + +describe Chef::HTTP::SocketlessChefZeroClient do + + let(:relative_url) { "" } + let(:uri_str) { "chefzero://localhost:1/#{relative_url}" } + let(:uri) { URI(uri_str) } + + subject(:zero_client) { Chef::HTTP::SocketlessChefZeroClient.new(uri) } + + it "has a host" do + expect(zero_client.host).to eq("localhost") + end + + it "has a port" do + expect(zero_client.port).to eq(1) + end + + describe "converting requests to rack format" do + + let(:expected_rack_req) do + { + "SCRIPT_NAME" => "", + "SERVER_NAME" => "localhost", + "REQUEST_METHOD" => method.to_s.upcase, + "PATH_INFO" => uri.path, + "QUERY_STRING" => uri.query, + "SERVER_PORT" => uri.port, + "HTTP_HOST" => "localhost:#{uri.port}", + "rack.url_scheme" => "chefzero", + } + end + + context "when the request has no body" do + + let(:method) { :GET } + let(:relative_url) { "clients" } + let(:headers) { { "Accept" => "application/json" } } + let(:body) { false } + let(:expected_body_str) { "" } + + let(:rack_req) { zero_client.req_to_rack(method, uri, body, headers) } + + it "creates a rack request env" do + # StringIO doesn't implement == in a way that we can compare, so we + # check rack.input individually and then iterate over everything else + expect(rack_req["rack.input"].string).to eq(expected_body_str) + expected_rack_req.each do |key, value| + expect(rack_req[key]).to eq(value) + end + end + + end + + context "when the request has a body" do + + let(:method) { :PUT } + let(:relative_url) { "clients/foo" } + let(:headers) { { "Accept" => "application/json" } } + let(:body) { "bunch o' JSON" } + let(:expected_body_str) { "bunch o' JSON" } + + let(:rack_req) { zero_client.req_to_rack(method, uri, body, headers) } + + it "creates a rack request env" do + # StringIO doesn't implement == in a way that we can compare, so we + # check rack.input individually and then iterate over everything else + expect(rack_req["rack.input"].string).to eq(expected_body_str) + expected_rack_req.each do |key, value| + expect(rack_req[key]).to eq(value) + end + end + + end + + end + + describe "converting responses to Net::HTTP objects" do + + let(:net_http_response) { zero_client.to_net_http(code, headers, body) } + + context "when the request was successful (2XX)" do + + let(:code) { 200 } + let(:headers) { { "Content-Type" => "Application/JSON" } } + let(:body) { [ "bunch o' JSON" ] } + + it "creates a Net::HTTP success response object" do + expect(net_http_response).to be_a_kind_of(Net::HTTPOK) + expect(net_http_response.read_body).to eq("bunch o' JSON") + expect(net_http_response["content-type"]).to eq("Application/JSON") + end + + it "does not fail when calling read_body with a block" do + expect(net_http_response.read_body {|chunk| chunk }).to eq("bunch o' JSON") + end + + end + + context "when the requested object doesn't exist (404)" do + + let(:code) { 404 } + let(:headers) { { "Content-Type" => "Application/JSON" } } + let(:body) { [ "nope" ] } + + it "creates a Net::HTTPNotFound response object" do + expect(net_http_response).to be_a_kind_of(Net::HTTPNotFound) + end + end + + end + + describe "request-response round trip" do + + let(:method) { :GET } + let(:relative_url) { "clients" } + let(:headers) { { "Accept" => "application/json" } } + let(:body) { false } + + let(:expected_rack_req) do + { + "SCRIPT_NAME" => "", + "SERVER_NAME" => "localhost", + "REQUEST_METHOD" => method.to_s.upcase, + "PATH_INFO" => uri.path, + "QUERY_STRING" => uri.query, + "SERVER_PORT" => uri.port, + "HTTP_HOST" => "localhost:#{uri.port}", + "rack.url_scheme" => "chefzero", + "rack.input" => an_instance_of(StringIO), + } + end + + + let(:response_code) { 200 } + let(:response_headers) { { "Content-Type" => "Application/JSON" } } + let(:response_body) { [ "bunch o' JSON" ] } + + let(:rack_response) { [ response_code, response_headers, response_body ] } + + let(:response) { zero_client.request(method, uri, body, headers) } + + before do + expect(ChefZero::SocketlessServerMap).to receive(:request).with(1, expected_rack_req).and_return(rack_response) + end + + it "makes a rack request to Chef Zero and returns the response as a Net::HTTP object" do + _client, net_http_response = response + expect(net_http_response).to be_a_kind_of(Net::HTTPOK) + expect(net_http_response.code).to eq("200") + expect(net_http_response.body).to eq("bunch o' JSON") + end + + end + +end diff --git a/spec/unit/http_spec.rb b/spec/unit/http_spec.rb index ddfc56583d..4d851df951 100644 --- a/spec/unit/http_spec.rb +++ b/spec/unit/http_spec.rb @@ -20,6 +20,7 @@ require 'spec_helper' require 'chef/http' require 'chef/http/basic_client' +require 'chef/http/socketless_chef_zero_client' class Chef::HTTP public :create_url @@ -27,6 +28,19 @@ end describe Chef::HTTP do + context "when given a chefzero:// URL" do + + let(:uri) { URI("chefzero://localhost:1") } + + subject(:http) { Chef::HTTP.new(uri) } + + it "uses the SocketlessChefZeroClient to handle requests" do + expect(http.http_client).to be_a_kind_of(Chef::HTTP::SocketlessChefZeroClient) + expect(http.http_client.url).to eq(uri) + end + + end + describe "create_url" do it 'should return a correctly formatted url 1/3 CHEF-5261' do 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/knife/ssh_spec.rb b/spec/unit/knife/ssh_spec.rb index 501b02c933..a838a21edc 100644 --- a/spec/unit/knife/ssh_spec.rb +++ b/spec/unit/knife/ssh_spec.rb @@ -96,6 +96,24 @@ describe Chef::Knife::Ssh do should_return_specified_attributes end + context "when cloud hostnames are available but empty" do + before do + @node_foo.automatic_attrs[:cloud][:public_hostname] = '' + @node_bar.automatic_attrs[:cloud][:public_hostname] = '' + end + + it "returns an array of fqdns" do + configure_query([@node_foo, @node_bar]) + expect(@knife).to receive(:session_from_list).with([ + ['foo.example.org', nil], + ['bar.example.org', nil] + ]) + @knife.configure_session + end + + should_return_specified_attributes + end + it "should raise an error if no host are found" do configure_query([ ]) expect(@knife.ui).to receive(:fatal) diff --git a/spec/unit/knife/status_spec.rb b/spec/unit/knife/status_spec.rb index 2522bc61b1..ee44f3b3fd 100644 --- a/spec/unit/knife/status_spec.rb +++ b/spec/unit/knife/status_spec.rb @@ -24,15 +24,81 @@ describe Chef::Knife::Status do n.automatic_attrs["fqdn"] = "foobar" n.automatic_attrs["ohai_time"] = 1343845969 end - query = double("Chef::Search::Query") - allow(query).to receive(:search).and_yield(node) - allow(Chef::Search::Query).to receive(:new).and_return(query) + allow(Time).to receive(:now).and_return(Time.at(1428573420)) + @query = double("Chef::Search::Query") + allow(@query).to receive(:search).and_yield(node) + allow(Chef::Search::Query).to receive(:new).and_return(@query) @knife = Chef::Knife::Status.new @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe "run" do + let(:opts) {{filter_result: + { name: ["name"], ipaddress: ["ipaddress"], ohai_time: ["ohai_time"], + ec2: ["ec2"], run_list: ["run_list"], platform: ["platform"], + platform_version: ["platform_version"], chef_environment: ["chef_environment"]}}} + + it "should default to searching for everything" do + expect(@query).to receive(:search).with(:node, "*:*", opts) + @knife.run + end + + it "should filter healthy nodes" do + @knife.config[:hide_healthy] = true + expect(@query).to receive(:search).with(:node, "NOT ohai_time:[1428569820 TO 1428573420]", opts) + @knife.run + end + + it "should filter by environment" do + @knife.config[:environment] = "production" + expect(@query).to receive(:search).with(:node, "chef_environment:production", opts) + @knife.run + end + + it "should filter by environment and health" do + @knife.config[:environment] = "production" + @knife.config[:hide_healthy] = true + expect(@query).to receive(:search).with(:node, "chef_environment:production NOT ohai_time:[1428569820 TO 1428573420]", opts) + @knife.run + end + + it "should not use partial search with long output" do + @knife.config[:long_output] = true + expect(@query).to receive(:search).with(:node, "*:*", {}) + @knife.run + end + + context "with a custom query" do + before :each do + @knife.instance_variable_set(:@name_args, ["name:my_custom_name"]) + end + + it "should allow a custom query to be specified" do + expect(@query).to receive(:search).with(:node, "name:my_custom_name", opts) + @knife.run + end + + it "should filter healthy nodes" do + @knife.config[:hide_healthy] = true + expect(@query).to receive(:search).with(:node, "name:my_custom_name NOT ohai_time:[1428569820 TO 1428573420]", opts) + @knife.run + end + + it "should filter by environment" do + @knife.config[:environment] = "production" + expect(@query).to receive(:search).with(:node, "name:my_custom_name AND chef_environment:production", opts) + @knife.run + end + + it "should filter by environment and health" do + @knife.config[:environment] = "production" + @knife.config[:hide_healthy] = true + expect(@query).to receive(:search).with(:node, "name:my_custom_name AND chef_environment:production NOT ohai_time:[1428569820 TO 1428573420]", opts) + @knife.run + end + end + it "should not colorize output unless it's writing to a tty" do @knife.run expect(@stdout.string.match(/foobar/)).not_to be_nil diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb index 2ccf8493ad..b748232081 100644 --- a/spec/unit/knife_spec.rb +++ b/spec/unit/knife_spec.rb @@ -271,6 +271,11 @@ describe Chef::Knife do expect(knife_command.config[:opt_with_default]).to eq("from-cli") end + it "merges `listen` config to Chef::Config" do + Chef::Knife.run(%w[test yourself --no-listen], Chef::Application::Knife.options) + expect(Chef::Config[:listen]).to be(false) + end + context "verbosity is greater than zero" do let(:fake_config) { "/does/not/exist/knife.rb" } diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb index 1b61f9b238..85e1c1abab 100644 --- a/spec/unit/mixin/params_validate_spec.rb +++ b/spec/unit/mixin/params_validate_spec.rb @@ -339,83 +339,69 @@ describe Chef::Mixin::ParamsValidate do end.to raise_error(Chef::Exceptions::ValidationFailed) end - 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 + 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) end - test_set_or_return_method(:set_or_return) - test_set_or_return_method(:nillable_set_or_return) + 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 - 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 + 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}) 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/policy_builder/policyfile_spec.rb b/spec/unit/policy_builder/policyfile_spec.rb index 8b6e928a46..e4f7388a1c 100644 --- a/spec/unit/policy_builder/policyfile_spec.rb +++ b/spec/unit/policy_builder/policyfile_spec.rb @@ -256,7 +256,7 @@ describe Chef::PolicyBuilder::Policyfile do context "and policy_name and policy_group are configured" do - let(:policy_relative_url) { "policies/policy-stage/example" } + let(:policy_relative_url) { "policy_groups/policy-stage/policies/example" } before do expect(http_api).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json) @@ -386,6 +386,9 @@ describe Chef::PolicyBuilder::Policyfile do describe "fetching the desired cookbook set" do + let(:example1_cookbook_data) { double("CookbookVersion Hash for example1 cookbook") } + let(:example2_cookbook_data) { double("CookbookVersion Hash for example2 cookbook") } + let(:example1_cookbook_object) { double("Chef::CookbookVersion for example1 cookbook") } let(:example2_cookbook_object) { double("Chef::CookbookVersion for example2 cookbook") } @@ -396,9 +399,12 @@ describe Chef::PolicyBuilder::Policyfile do let(:example1_xyz_version) { example1_lock_data["dotted_decimal_identifier"] } let(:example2_xyz_version) { example2_lock_data["dotted_decimal_identifier"] } + let(:example1_identifier) { example1_lock_data["identifier"] } + let(:example2_identifier) { example2_lock_data["identifier"] } + let(:cookbook_synchronizer) { double("Chef::CookbookSynchronizer") } - shared_examples_for "fetching cookbooks" do + shared_examples "fetching cookbooks when they don't exist" do context "and a cookbook is missing" do let(:error404) { Net::HTTPServerException.new("404 message", :body) } @@ -418,7 +424,9 @@ describe Chef::PolicyBuilder::Policyfile do end end + end + shared_examples_for "fetching cookbooks when they exist" do context "and the cookbooks can be fetched" do before do expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) @@ -426,11 +434,6 @@ describe Chef::PolicyBuilder::Policyfile do policy_builder.load_node policy_builder.build_node - expect(http_api).to receive(:get).with(cookbook1_url). - and_return(example1_cookbook_object) - expect(http_api).to receive(:get).with(cookbook2_url). - and_return(example2_cookbook_object) - allow(Chef::CookbookSynchronizer).to receive(:new). with(expected_cookbook_hash, events). and_return(cookbook_synchronizer) @@ -457,11 +460,23 @@ describe Chef::PolicyBuilder::Policyfile do end # shared_examples_for "fetching cookbooks" context "when using compatibility mode (policy_document_native_api == false)" do - include_examples "fetching cookbooks" do + let(:cookbook1_url) { "cookbooks/example1/#{example1_xyz_version}" } + let(:cookbook2_url) { "cookbooks/example2/#{example2_xyz_version}" } - let(:cookbook1_url) { "cookbooks/example1/#{example1_xyz_version}" } - let(:cookbook2_url) { "cookbooks/example2/#{example2_xyz_version}" } + context "when the cookbooks don't exist on the server" do + include_examples "fetching cookbooks when they don't exist" + end + + context "when the cookbooks exist on the server" do + + before do + expect(http_api).to receive(:get).with(cookbook1_url). + and_return(example1_cookbook_object) + expect(http_api).to receive(:get).with(cookbook2_url). + and_return(example2_cookbook_object) + end + include_examples "fetching cookbooks when they exist" end end @@ -474,13 +489,33 @@ describe Chef::PolicyBuilder::Policyfile do Chef::Config[:policy_name] = "example" end - include_examples "fetching cookbooks" do + let(:cookbook1_url) { "cookbook_artifacts/example1/#{example1_identifier}" } + let(:cookbook2_url) { "cookbook_artifacts/example2/#{example2_identifier}" } + + context "when the cookbooks don't exist on the server" do + include_examples "fetching cookbooks when they don't exist" + end + - let(:cookbook1_url) { "cookbook_artifacts/example1/#{example1_xyz_version}" } - let(:cookbook2_url) { "cookbook_artifacts/example2/#{example2_xyz_version}" } + context "when the cookbooks exist on the server" do + + before do + expect(http_api).to receive(:get).with(cookbook1_url). + and_return(example1_cookbook_data) + expect(http_api).to receive(:get).with(cookbook2_url). + and_return(example2_cookbook_data) + + expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example1_cookbook_data). + and_return(example1_cookbook_object) + expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example2_cookbook_data). + and_return(example2_cookbook_object) + end + + include_examples "fetching cookbooks when they exist" end + end end 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/package/rubygems_spec.rb b/spec/unit/provider/package/rubygems_spec.rb index b17c216ddd..380572499c 100644 --- a/spec/unit/provider/package/rubygems_spec.rb +++ b/spec/unit/provider/package/rubygems_spec.rb @@ -547,6 +547,25 @@ describe Chef::Provider::Package::Rubygems do expect(@new_resource).to be_updated_by_last_action end + it "installs the gem with rubygems.org as an added source" do + @new_resource.gem_binary('/foo/bar') + @new_resource.source('http://mirror.ops.rhcloud.com/mirror/ruby') + expected ="/foo/bar install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\" --source=#{@new_resource.source} --source=https://rubygems.org" + expect(@provider).to receive(:shell_out!).with(expected, :env => nil) + @provider.run_action(:install) + expect(@new_resource).to be_updated_by_last_action + end + + it "installs the gem with cleared sources and explict source when specified" do + @new_resource.gem_binary('/foo/bar') + @new_resource.source('http://mirror.ops.rhcloud.com/mirror/ruby') + @new_resource.clear_sources(true) + expected ="/foo/bar install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\" --clear-sources --source=#{@new_resource.source}" + expect(@provider).to receive(:shell_out!).with(expected, :env => nil) + @provider.run_action(:install) + expect(@new_resource).to be_updated_by_last_action + end + context "when no version is given" do let(:target_version) { nil } diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb index cd2b3decf4..865dce23fa 100644 --- a/spec/unit/provider/package/yum_spec.rb +++ b/spec/unit/provider/package/yum_spec.rb @@ -73,6 +73,20 @@ describe Chef::Provider::Package::Yum do expect(@provider.load_current_resource).to eql(@provider.current_resource) end + describe "when source is provided" do + it "should set the candidate version" do + @new_resource = Chef::Resource::YumPackage.new('testing.source') + @new_resource.source "chef-server-core-12.0.5-1.rpm" + @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) + allow(File).to receive(:exists?).with(@new_resource.source).and_return(true) + allow(@yum_cache).to receive(:installed_version).and_return(nil) + shellout_double = double(:stdout => 'chef-server-core 12.0.5-1') + allow(@provider).to receive(:shell_out!).and_return(shellout_double) + @provider.load_current_resource + expect(@provider.candidate_version).to eql('12.0.5-1') + end + end + describe "when arch in package_name" do it "should set the arch if no existing package_name is found and new_package_name+new_arch is available" do @new_resource = Chef::Resource::YumPackage.new('testing.noarch') diff --git a/spec/unit/provider/service/macosx_spec.rb b/spec/unit/provider/service/macosx_spec.rb index 42d45f09e0..597845a558 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/recipe_spec.rb b/spec/unit/recipe_spec.rb index 8d0b1bcfd2..b370e12732 100644 --- a/spec/unit/recipe_spec.rb +++ b/spec/unit/recipe_spec.rb @@ -237,8 +237,17 @@ describe Chef::Recipe do action :nothing end end + + it "validating resources via build_resource" do + expect {recipe.build_resource(:remote_file, "klopp") do + source Chef::DelayedEvaluator.new {"http://chef.io"} + end}.to_not raise_error + end + end + + describe "creating resources via declare_resource" do let(:zm_resource) do recipe.declare_resource(:zen_master, "klopp") do @@ -593,5 +602,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 07f5f973c0..0403a7ba6b 100644 --- a/spec/unit/resource/deploy_spec.rb +++ b/spec/unit/resource/deploy_spec.rb @@ -30,35 +30,12 @@ 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}) @@ -212,10 +189,6 @@ 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/resource/link_spec.rb b/spec/unit/resource/link_spec.rb index 3573a15f31..51221e0472 100644 --- a/spec/unit/resource/link_spec.rb +++ b/spec/unit/resource/link_spec.rb @@ -53,6 +53,21 @@ describe Chef::Resource::Link do expect(@resource.target_file).to eql("fakey_fakerton") end + it "should accept a delayed evaluator as the target path" do + @resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" } + expect(@resource.target_file).to eql("my_lazy_name") + end + + it "should accept a delayed evaluator when accessing via 'path'" do + @resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" } + expect(@resource.path).to eql("my_lazy_name") + end + + it "should accept a delayed evaluator via 'to'" do + @resource.to Chef::DelayedEvaluator.new { "my_lazy_name" } + expect(@resource.to).to eql("my_lazy_name") + end + it "should accept a string as the link source via 'to'" do expect { @resource.to "/tmp" }.not_to raise_error end diff --git a/spec/unit/rest_spec.rb b/spec/unit/rest_spec.rb index 1aa7ac84ee..85c9e3df8f 100644 --- a/spec/unit/rest_spec.rb +++ b/spec/unit/rest_spec.rb @@ -92,6 +92,15 @@ describe Chef::REST do Chef::REST.new(base_url, nil, nil, options) end + context 'when created with a chef zero URL' do + + let(:url) { "chefzero://localhost:1" } + + it "does not load the signing key" do + expect { Chef::REST.new(url) }.to_not raise_error + end + end + describe "calling an HTTP verb on a path or absolute URL" do it "adds a relative URL to the base url it was initialized with" do expect(rest.create_url("foo/bar/baz")).to eq(URI.parse(base_url + "/foo/bar/baz")) 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/path_helper_spec.rb b/spec/unit/util/path_helper_spec.rb index 5756c29b90..23db9587a6 100644 --- a/spec/unit/util/path_helper_spec.rb +++ b/spec/unit/util/path_helper_spec.rb @@ -230,4 +230,26 @@ describe Chef::Util::PathHelper do end end end + + describe "all_homes" do + before do + stub_const('ENV', env) + allow(Chef::Platform).to receive(:windows?).and_return(is_windows) + end + + context "on windows" do + let (:is_windows) { true } + end + + context "on unix" do + let (:is_windows) { false } + + context "when HOME is not set" do + let (:env) { {} } + it "returns an empty array" do + expect(PathHelper.all_homes).to eq([]) + end + end + 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 |