summaryrefslogtreecommitdiff
path: root/chef-config/spec
diff options
context:
space:
mode:
authordanielsdeleo <dan@getchef.com>2015-04-23 11:44:24 -0700
committerdanielsdeleo <dan@getchef.com>2015-05-20 15:13:56 -0700
commite6062caaefe82ff351690af15e882f02efc0f91b (patch)
tree850f5ed39e9bd7e33303bf907c4ea7389a6a33d7 /chef-config/spec
parentb6f9e5feff3b97576534d70dc2873c4fd62d28e4 (diff)
downloadchef-e6062caaefe82ff351690af15e882f02efc0f91b.tar.gz
Move Chef::Config into a subproject
Diffstat (limited to 'chef-config/spec')
-rw-r--r--chef-config/spec/spec_helper.rb75
-rw-r--r--chef-config/spec/unit/config_spec.rb581
-rw-r--r--chef-config/spec/unit/path_helper_spec.rb291
3 files changed, 947 insertions, 0 deletions
diff --git a/chef-config/spec/spec_helper.rb b/chef-config/spec/spec_helper.rb
new file mode 100644
index 0000000000..df9461cde9
--- /dev/null
+++ b/chef-config/spec/spec_helper.rb
@@ -0,0 +1,75 @@
+require 'chef-config/windows'
+
+# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+RSpec.configure do |config|
+ # rspec-expectations config goes here. You can use an alternate
+ # assertion/expectation library such as wrong or the stdlib/minitest
+ # assertions if you prefer.
+ config.expect_with :rspec do |expectations|
+ # This option will default to `true` in RSpec 4. It makes the `description`
+ # and `failure_message` of custom matchers include text for helper methods
+ # defined using `chain`, e.g.:
+ # be_bigger_than(2).and_smaller_than(4).description
+ # # => "be bigger than 2 and smaller than 4"
+ # ...rather than:
+ # # => "be bigger than 2"
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ # rspec-mocks config goes here. You can use an alternate test double
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
+ config.mock_with :rspec do |mocks|
+ # Prevents you from mocking or stubbing a method that does not exist on
+ # a real object. This is generally recommended, and will default to
+ # `true` in RSpec 4.
+ mocks.verify_partial_doubles = true
+ end
+
+ # These two settings work together to allow you to limit a spec run
+ # to individual examples or groups you care about by tagging them with
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
+ # get run.
+ config.filter_run :focus
+ config.run_all_when_everything_filtered = true
+
+ config.filter_run_excluding :windows_only => true unless ChefConfig.windows?
+ config.filter_run_excluding :unix_only => true if ChefConfig.windows?
+
+ # Limits the available syntax to the non-monkey patched syntax that is
+ # recommended. For more details, see:
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
+ config.disable_monkey_patching!
+
+ # This setting enables warnings. It's recommended, but in some cases may
+ # be too noisy due to issues in dependencies.
+ config.warnings = true
+
+ # Many RSpec users commonly either run the entire suite or an individual
+ # file, and it's useful to allow more verbose output when running an
+ # individual spec file.
+ if config.files_to_run.one?
+ # Use the documentation formatter for detailed output,
+ # unless a formatter has already been configured
+ # (e.g. via a command-line flag).
+ config.default_formatter = 'doc'
+ end
+
+ # Print the 10 slowest examples and example groups at the
+ # end of the spec run, to help surface which specs are running
+ # particularly slow.
+ # config.profile_examples = 10
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ config.order = :random
+
+ # Seed global randomization in this process using the `--seed` CLI option.
+ # Setting this allows you to use `--seed` to deterministically reproduce
+ # test failures related to randomization by passing the same `--seed` value
+ # as the one that triggered the failure.
+ Kernel.srand config.seed
+end
diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb
new file mode 100644
index 0000000000..395fa2618e
--- /dev/null
+++ b/chef-config/spec/unit/config_spec.rb
@@ -0,0 +1,581 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
+# Copyright:: Copyright (c) 2008 Opscode, 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-config/config'
+
+RSpec.describe ChefConfig::Config do
+ before(:each) do
+ ChefConfig::Config.reset
+
+ # By default, treat deprecation warnings as errors in tests.
+ ChefConfig::Config.treat_deprecation_warnings_as_errors(true)
+
+ # Set environment variable so the setting persists in child processes
+ ENV['CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS'] = "1"
+ end
+
+ describe "config attribute writer: chef_server_url" do
+ before do
+ ChefConfig::Config.chef_server_url = "https://junglist.gen.nz"
+ end
+
+ it "sets the server url" do
+ expect(ChefConfig::Config.chef_server_url).to eq("https://junglist.gen.nz")
+ end
+
+ context "when the url has a leading space" do
+ before do
+ ChefConfig::Config.chef_server_url = " https://junglist.gen.nz"
+ end
+
+ it "strips the space from the url when setting" do
+ expect(ChefConfig::Config.chef_server_url).to eq("https://junglist.gen.nz")
+ end
+
+ end
+
+ context "when the url is a frozen string" do
+ before do
+ ChefConfig::Config.chef_server_url = " https://junglist.gen.nz".freeze
+ end
+
+ it "strips the space from the url when setting without raising an error" do
+ expect(ChefConfig::Config.chef_server_url).to eq("https://junglist.gen.nz")
+ end
+ end
+ end
+
+ describe "when configuring formatters" do
+ # if TTY and not(force-logger)
+ # formatter = configured formatter or default formatter
+ # formatter goes to STDOUT/ERR
+ # if log file is writeable
+ # log level is configured level or info
+ # log location is file
+ # else
+ # log level is warn
+ # log location is STDERR
+ # end
+ # elsif not(TTY) and force formatter
+ # formatter = configured formatter or default formatter
+ # if log_location specified
+ # formatter goes to log_location
+ # else
+ # formatter goes to STDOUT/ERR
+ # end
+ # else
+ # formatter = "null"
+ # log_location = configured-value or defualt
+ # log_level = info or defualt
+ # end
+ #
+ it "has an empty list of formatters by default" do
+ expect(ChefConfig::Config.formatters).to eq([])
+ end
+
+ it "configures a formatter with a short name" do
+ ChefConfig::Config.add_formatter(:doc)
+ expect(ChefConfig::Config.formatters).to eq([[:doc, nil]])
+ end
+
+ it "configures a formatter with a file output" do
+ ChefConfig::Config.add_formatter(:doc, "/var/log/formatter.log")
+ expect(ChefConfig::Config.formatters).to eq([[:doc, "/var/log/formatter.log"]])
+ end
+
+ end
+
+ [ false, true ].each do |is_windows|
+
+ context "On #{is_windows ? 'Windows' : 'Unix'}" do
+ def to_platform(*args)
+ ChefConfig::Config.platform_specific_path(*args)
+ end
+
+ before :each do
+ allow(ChefConfig).to receive(:windows?).and_return(is_windows)
+ end
+
+ describe "class method: platform_specific_path" do
+ if is_windows
+ it "should return a windows path on windows systems" do
+ path = "/etc/chef/cookbooks"
+ allow(ChefConfig::Config).to receive(:env).and_return({ 'SYSTEMDRIVE' => 'C:' })
+ # match on a regex that looks for the base path with an optional
+ # system drive at the beginning (c:)
+ # system drive is not hardcoded b/c it can change and b/c it is not present on linux systems
+ expect(ChefConfig::Config.platform_specific_path(path)).to eq("C:\\chef\\cookbooks")
+ end
+ else
+ it "should return given path on non-windows systems" do
+ path = "/etc/chef/cookbooks"
+ expect(ChefConfig::Config.platform_specific_path(path)).to eq("/etc/chef/cookbooks")
+ end
+ end
+ end
+
+ describe "default values" do
+ let :primary_cache_path do
+ if is_windows
+ "#{ChefConfig::Config.env['SYSTEMDRIVE']}\\chef"
+ else
+ "/var/chef"
+ end
+ end
+
+ let :secondary_cache_path do
+ if is_windows
+ "#{ChefConfig::Config[:user_home]}\\.chef"
+ else
+ "#{ChefConfig::Config[:user_home]}/.chef"
+ end
+ end
+
+ before do
+ if is_windows
+ allow(ChefConfig::Config).to receive(:env).and_return({ 'SYSTEMDRIVE' => 'C:' })
+ ChefConfig::Config[:user_home] = 'C:\Users\charlie'
+ else
+ ChefConfig::Config[:user_home] = '/Users/charlie'
+ end
+
+ allow(ChefConfig::Config).to receive(:path_accessible?).and_return(false)
+ end
+
+ describe "ChefConfig::Config[:chef_server_root]" do
+ context "when chef_server_url isn't set manually" do
+ it "returns the default of 'https://localhost:443'" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq("https://localhost:443")
+ end
+ end
+
+ context "when chef_server_url matches '../organizations/*' without a trailing slash" do
+ before do
+ ChefConfig::Config[:chef_server_url] = "https://example.com/organizations/myorg"
+ end
+ it "returns the full URL without /organizations/*" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq("https://example.com")
+ end
+ end
+
+ context "when chef_server_url matches '../organizations/*' with a trailing slash" do
+ before do
+ ChefConfig::Config[:chef_server_url] = "https://example.com/organizations/myorg/"
+ end
+ it "returns the full URL without /organizations/*" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq("https://example.com")
+ end
+ end
+
+ context "when chef_server_url matches '..organizations..' but not '../organizations/*'" do
+ before do
+ ChefConfig::Config[:chef_server_url] = "https://organizations.com/organizations"
+ end
+ it "returns the full URL without any modifications" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq(ChefConfig::Config[:chef_server_url])
+ end
+ end
+
+ context "when chef_server_url is a standard URL without the string organization(s)" do
+ before do
+ ChefConfig::Config[:chef_server_url] = "https://example.com/some_other_string"
+ end
+ it "returns the full URL without any modifications" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq(ChefConfig::Config[:chef_server_url])
+ end
+ end
+ end
+
+ describe "ChefConfig::Config[:cache_path]" do
+ context "when /var/chef exists and is accessible" do
+ it "defaults to /var/chef" do
+ allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var/chef")).and_return(true)
+ expect(ChefConfig::Config[:cache_path]).to eq(primary_cache_path)
+ end
+ end
+
+ context "when /var/chef does not exist and /var is accessible" do
+ it "defaults to /var/chef" do
+ allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false)
+ allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(true)
+ expect(ChefConfig::Config[:cache_path]).to eq(primary_cache_path)
+ end
+ end
+
+ context "when /var/chef does not exist and /var is not accessible" do
+ it "defaults to $HOME/.chef" do
+ allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false)
+ allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(false)
+ expect(ChefConfig::Config[:cache_path]).to eq(secondary_cache_path)
+ end
+ end
+
+ context "when /var/chef exists and is not accessible" do
+ it "defaults to $HOME/.chef" do
+ allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(true)
+ allow(File).to receive(:readable?).with(to_platform("/var/chef")).and_return(true)
+ allow(File).to receive(:writable?).with(to_platform("/var/chef")).and_return(false)
+
+ expect(ChefConfig::Config[:cache_path]).to eq(secondary_cache_path)
+ end
+ end
+
+ context "when chef is running in local mode" do
+ before do
+ ChefConfig::Config.local_mode = true
+ end
+
+ context "and config_dir is /a/b/c" do
+ before do
+ ChefConfig::Config.config_dir to_platform('/a/b/c')
+ end
+
+ it "cache_path is /a/b/c/local-mode-cache" do
+ expect(ChefConfig::Config.cache_path).to eq(to_platform('/a/b/c/local-mode-cache'))
+ end
+ end
+
+ context "and config_dir is /a/b/c/" do
+ before do
+ ChefConfig::Config.config_dir to_platform('/a/b/c/')
+ end
+
+ it "cache_path is /a/b/c/local-mode-cache" do
+ expect(ChefConfig::Config.cache_path).to eq(to_platform('/a/b/c/local-mode-cache'))
+ end
+ end
+ end
+ end
+
+ it "ChefConfig::Config[:file_backup_path] defaults to /var/chef/backup" do
+ allow(ChefConfig::Config).to receive(:cache_path).and_return(primary_cache_path)
+ backup_path = is_windows ? "#{primary_cache_path}\\backup" : "#{primary_cache_path}/backup"
+ expect(ChefConfig::Config[:file_backup_path]).to eq(backup_path)
+ end
+
+ it "ChefConfig::Config[:ssl_verify_mode] defaults to :verify_peer" do
+ expect(ChefConfig::Config[:ssl_verify_mode]).to eq(:verify_peer)
+ end
+
+ it "ChefConfig::Config[:ssl_ca_path] defaults to nil" do
+ expect(ChefConfig::Config[:ssl_ca_path]).to be_nil
+ end
+
+ # On Windows, we'll detect an omnibus build and set this to the
+ # cacert.pem included in the package, but it's nil if you're on Windows
+ # w/o omnibus (e.g., doing development on Windows, custom build, etc.)
+ if !is_windows
+ it "ChefConfig::Config[:ssl_ca_file] defaults to nil" do
+ expect(ChefConfig::Config[:ssl_ca_file]).to be_nil
+ end
+ end
+
+ it "ChefConfig::Config[:data_bag_path] defaults to /var/chef/data_bags" do
+ allow(ChefConfig::Config).to receive(:cache_path).and_return(primary_cache_path)
+ data_bag_path = is_windows ? "#{primary_cache_path}\\data_bags" : "#{primary_cache_path}/data_bags"
+ expect(ChefConfig::Config[:data_bag_path]).to eq(data_bag_path)
+ end
+
+ it "ChefConfig::Config[:environment_path] defaults to /var/chef/environments" do
+ allow(ChefConfig::Config).to receive(:cache_path).and_return(primary_cache_path)
+ environment_path = is_windows ? "#{primary_cache_path}\\environments" : "#{primary_cache_path}/environments"
+ expect(ChefConfig::Config[:environment_path]).to eq(environment_path)
+ end
+
+ describe "setting the config dir" do
+
+ context "when the config file is /etc/chef/client.rb" do
+
+ before do
+ ChefConfig::Config.config_file = to_platform("/etc/chef/client.rb")
+ end
+
+ it "config_dir is /etc/chef" do
+ expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef"))
+ end
+
+ context "and chef is running in local mode" do
+ before do
+ ChefConfig::Config.local_mode = true
+ end
+
+ it "config_dir is /etc/chef" do
+ expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef"))
+ end
+ end
+
+ context "when config_dir is set to /other/config/dir/" do
+ before do
+ ChefConfig::Config.config_dir = to_platform("/other/config/dir/")
+ end
+
+ it "yields the explicit value" do
+ expect(ChefConfig::Config.config_dir).to eq(to_platform("/other/config/dir/"))
+ end
+ end
+
+ end
+
+ context "when the user's home dir is /home/charlie/" do
+ before do
+ ChefConfig::Config.user_home = to_platform("/home/charlie")
+ end
+
+ it "config_dir is /home/charlie/.chef/" do
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(to_platform("/home/charlie/.chef"), ''))
+ end
+
+ context "and chef is running in local mode" do
+ before do
+ ChefConfig::Config.local_mode = true
+ end
+
+ it "config_dir is /home/charlie/.chef/" do
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(to_platform("/home/charlie/.chef"), ''))
+ end
+ end
+ end
+
+ end
+
+ if is_windows
+ describe "finding the windows embedded dir" do
+ let(:default_config_location) { "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" }
+ let(:alternate_install_location) { "c:/my/alternate/install/place/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" }
+ let(:non_omnibus_location) { "c:/my/dev/stuff/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" }
+
+ let(:default_ca_file) { "c:/opscode/chef/embedded/ssl/certs/cacert.pem" }
+
+ it "finds the embedded dir in the default location" do
+ allow(ChefConfig::Config).to receive(:_this_file).and_return(default_config_location)
+ expect(ChefConfig::Config.embedded_dir).to eq("c:/opscode/chef/embedded")
+ end
+
+ it "finds the embedded dir in a custom install location" do
+ allow(ChefConfig::Config).to receive(:_this_file).and_return(alternate_install_location)
+ expect(ChefConfig::Config.embedded_dir).to eq("c:/my/alternate/install/place/chef/embedded")
+ end
+
+ it "doesn't error when not in an omnibus install" do
+ allow(ChefConfig::Config).to receive(:_this_file).and_return(non_omnibus_location)
+ expect(ChefConfig::Config.embedded_dir).to be_nil
+ end
+
+ it "sets the ssl_ca_cert path if the cert file is available" do
+ allow(ChefConfig::Config).to receive(:_this_file).and_return(default_config_location)
+ allow(File).to receive(:exist?).with(default_ca_file).and_return(true)
+ expect(ChefConfig::Config.ssl_ca_file).to eq(default_ca_file)
+ end
+ end
+ end
+ end
+
+ describe "ChefConfig::Config[:user_home]" do
+ it "should set when HOME is provided" do
+ expected = to_platform("/home/kitten")
+ allow(ChefConfig::PathHelper).to receive(:home).and_return(expected)
+ expect(ChefConfig::Config[:user_home]).to eq(expected)
+ end
+
+ it "falls back to the current working directory when HOME and USERPROFILE is not set" do
+ allow(ChefConfig::PathHelper).to receive(:home).and_return(nil)
+ expect(ChefConfig::Config[:user_home]).to eq(Dir.pwd)
+ end
+ end
+
+ describe "ChefConfig::Config[:encrypted_data_bag_secret]" do
+ let(:db_secret_default_path){ to_platform("/etc/chef/encrypted_data_bag_secret") }
+
+ before do
+ allow(File).to receive(:exist?).with(db_secret_default_path).and_return(secret_exists)
+ end
+
+ context "/etc/chef/encrypted_data_bag_secret exists" do
+ let(:secret_exists) { true }
+ it "sets the value to /etc/chef/encrypted_data_bag_secret" do
+ expect(ChefConfig::Config[:encrypted_data_bag_secret]).to eq db_secret_default_path
+ end
+ end
+
+ context "/etc/chef/encrypted_data_bag_secret does not exist" do
+ let(:secret_exists) { false }
+ it "sets the value to nil" do
+ expect(ChefConfig::Config[:encrypted_data_bag_secret]).to be_nil
+ end
+ end
+ end
+
+ describe "ChefConfig::Config[:event_handlers]" do
+ it "sets a event_handlers to an empty array by default" do
+ expect(ChefConfig::Config[:event_handlers]).to eq([])
+ end
+ it "should be able to add custom handlers" do
+ o = Object.new
+ ChefConfig::Config[:event_handlers] << o
+ expect(ChefConfig::Config[:event_handlers]).to be_include(o)
+ end
+ end
+
+ describe "ChefConfig::Config[:user_valid_regex]" do
+ context "on a platform that is not Windows" do
+ it "allows one letter usernames" do
+ any_match = ChefConfig::Config[:user_valid_regex].any? { |regex| regex.match('a') }
+ expect(any_match).to be_truthy
+ end
+ end
+ end
+
+ describe "ChefConfig::Config[:internal_locale]" do
+ let(:shell_out) do
+ cmd = instance_double("Mixlib::ShellOut", exitstatus: 0, stdout: locales, error!: nil)
+ allow(cmd).to receive(:run_command).and_return(cmd)
+ cmd
+ end
+
+ let(:locales) { locale_array.join("\n") }
+
+ before do
+ allow(Mixlib::ShellOut).to receive(:new).with("locale -a").and_return(shell_out)
+ end
+
+ shared_examples_for "a suitable locale" do
+ it "returns an English UTF-8 locale" do
+ expect(ChefConfig.logger).to_not receive(:warn).with(/Please install an English UTF-8 locale for Chef to use/)
+ expect(ChefConfig.logger).to_not receive(:debug).with(/Defaulting to locale en_US.UTF-8 on Windows/)
+ expect(ChefConfig.logger).to_not receive(:debug).with(/No usable locale -a command found/)
+ expect(ChefConfig::Config.guess_internal_locale).to eq expected_locale
+ end
+ end
+
+ context "when the result includes 'C.UTF-8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { [expected_locale, "en_US.UTF-8"] }
+ let(:expected_locale) { "C.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en_US.UTF-8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { ["en_CA.UTF-8", expected_locale, "en_NZ.UTF-8"] }
+ let(:expected_locale) { "en_US.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en_US.utf8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { ["en_CA.utf8", "en_US.utf8", "en_NZ.utf8"] }
+ let(:expected_locale) { "en_US.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en.UTF-8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { ["en.ISO8859-1", expected_locale] }
+ let(:expected_locale) { "en.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en_*.UTF-8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { [expected_locale, "en_CA.UTF-8", "en_GB.UTF-8"] }
+ let(:expected_locale) { "en_AU.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en_*.utf8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { ["en_AU.utf8", "en_CA.utf8", "en_GB.utf8"] }
+ let(:expected_locale) { "en_AU.UTF-8" }
+ end
+ end
+
+ context "when the result does not include 'en_*.UTF-8'" do
+ let(:locale_array) { ["af_ZA", "af_ZA.ISO8859-1", "af_ZA.ISO8859-15", "af_ZA.UTF-8"] }
+
+ it "should fall back to C locale" do
+ expect(ChefConfig.logger).to receive(:warn).with("Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.")
+ expect(ChefConfig::Config.guess_internal_locale).to eq 'C'
+ end
+ end
+
+ context "on error" do
+ let(:locale_array) { [] }
+
+ let(:shell_out_cmd) { instance_double("Mixlib::ShellOut") }
+
+ before do
+ allow(Mixlib::ShellOut).to receive(:new).and_return(shell_out_cmd)
+ allow(shell_out_cmd).to receive(:run_command)
+ allow(shell_out_cmd).to receive(:error!).and_raise(Mixlib::ShellOut::ShellCommandFailed, "this is an error")
+ end
+
+ it "should default to 'en_US.UTF-8'" do
+ if is_windows
+ expect(ChefConfig.logger).to receive(:debug).with("Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else.")
+ else
+ expect(ChefConfig.logger).to receive(:debug).with("No usable locale -a command found, assuming you have en_US.UTF-8 installed.")
+ end
+ expect(ChefConfig::Config.guess_internal_locale).to eq "en_US.UTF-8"
+ end
+ end
+ end
+ end
+ end
+
+ describe "Treating deprecation warnings as errors" do
+
+ context "when using our default RSpec configuration" do
+
+ it "defaults to treating deprecation warnings as errors" do
+ expect(ChefConfig::Config[:treat_deprecation_warnings_as_errors]).to be(true)
+ end
+
+ it "sets CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS environment variable" do
+ expect(ENV['CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS']).to eq("1")
+ end
+
+ it "treats deprecation warnings as errors in child processes when testing" do
+ # Doing a full integration test where we launch a child process is slow
+ # and liable to break for weird reasons (bundler env stuff, etc.), so
+ # we're just checking that the presence of the environment variable
+ # causes treat_deprecation_warnings_as_errors to be set to true after a
+ # config reset.
+ ChefConfig::Config.reset
+ expect(ChefConfig::Config[:treat_deprecation_warnings_as_errors]).to be(true)
+ end
+
+ end
+
+ context "outside of our test environment" do
+
+ before do
+ ENV.delete('CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS')
+ ChefConfig::Config.reset
+ end
+
+ it "defaults to NOT treating deprecation warnings as errors" do
+ expect(ChefConfig::Config[:treat_deprecation_warnings_as_errors]).to be(false)
+ end
+ end
+
+
+ end
+
+end
diff --git a/chef-config/spec/unit/path_helper_spec.rb b/chef-config/spec/unit/path_helper_spec.rb
new file mode 100644
index 0000000000..3e6213597a
--- /dev/null
+++ b/chef-config/spec/unit/path_helper_spec.rb
@@ -0,0 +1,291 @@
+#
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# 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-config/path_helper'
+require 'spec_helper'
+
+RSpec.describe ChefConfig::PathHelper do
+
+ let(:path_helper) { described_class }
+
+ shared_examples_for "common_functionality" do
+ describe "join" do
+
+ it "joins starting with '' resolve to absolute paths" do
+ expect(path_helper.join('', 'a', 'b')).to eq("#{path_helper.path_separator}a#{path_helper.path_separator}b")
+ end
+
+ it "joins ending with '' add a / to the end" do
+ expect(path_helper.join('a', 'b', '')).to eq("a#{path_helper.path_separator}b#{path_helper.path_separator}")
+ end
+
+ end
+
+ describe "dirname" do
+ it "dirname('abc') is '.'" do
+ expect(path_helper.dirname('abc')).to eq('.')
+ end
+ it "dirname('/') is '/'" do
+ expect(path_helper.dirname(path_helper.path_separator)).to eq(path_helper.path_separator)
+ end
+ it "dirname('a/b/c') is 'a/b'" do
+ expect(path_helper.dirname(path_helper.join('a', 'b', 'c'))).to eq(path_helper.join('a', 'b'))
+ end
+ it "dirname('a/b/c/') is 'a/b'" do
+ expect(path_helper.dirname(path_helper.join('a', 'b', 'c', ''))).to eq(path_helper.join('a', 'b'))
+ end
+ it "dirname('/a/b/c') is '/a/b'" do
+ expect(path_helper.dirname(path_helper.join('', 'a', 'b', 'c'))).to eq(path_helper.join('', 'a', 'b'))
+ end
+ end
+ end
+
+ context "on windows" do
+
+ before(:each) do
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ end
+
+ include_examples("common_functionality")
+
+ it "path_separator is \\" do
+ expect(path_helper.path_separator).to eq('\\')
+ end
+
+ describe "platform-specific #join behavior" do
+
+ it "joins components on Windows when some end with unix separators" do
+ expect(path_helper.join('C:\\foo/', "bar", "baz")).to eq('C:\\foo\\bar\\baz')
+ end
+
+ it "joins components when some end with separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expected = "C:#{expected}"
+ expect(path_helper.join('C:\\foo\\', "bar", "baz")).to eq(expected)
+ end
+
+ it "joins components when some end and start with separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expected = "C:#{expected}"
+ expect(path_helper.join('C:\\foo\\', "bar/", "/baz")).to eq(expected)
+ end
+
+ it "joins components that don't end in separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expected = "C:#{expected}"
+ expect(path_helper.join('C:\\foo', "bar", "baz")).to eq(expected)
+ end
+
+ end
+
+
+ it "cleanpath changes slashes into backslashes and leaves backslashes alone" do
+ expect(path_helper.cleanpath('/a/b\\c/d/')).to eq('\\a\\b\\c\\d')
+ end
+
+ it "cleanpath does not remove leading double backslash" do
+ expect(path_helper.cleanpath('\\\\a/b\\c/d/')).to eq('\\\\a\\b\\c\\d')
+ end
+
+ end
+
+ context "on unix" do
+
+ before(:each) do
+ allow(ChefConfig).to receive(:windows?).and_return(false)
+ end
+
+ include_examples("common_functionality")
+
+ it "path_separator is /" do
+ expect(path_helper.path_separator).to eq('/')
+ end
+
+ it "cleanpath removes extra slashes alone" do
+ expect(path_helper.cleanpath('/a///b/c/d/')).to eq('/a/b/c/d')
+ end
+
+ describe "platform-specific #join behavior" do
+
+ it "joins components when some end with separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expect(path_helper.join("/foo/", "bar", "baz")).to eq(expected)
+ end
+
+ it "joins components when some end and start with separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expect(path_helper.join("/foo/", "bar/", "/baz")).to eq(expected)
+ end
+
+ it "joins components that don't end in separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expect(path_helper.join("/foo", "bar", "baz")).to eq(expected)
+ end
+
+ end
+
+ end
+
+ describe "validate_path" do
+ context "on windows" do
+ before(:each) do
+ # pass by default
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(path_helper).to receive(:printable?).and_return(true)
+ allow(path_helper).to receive(:windows_max_length_exceeded?).and_return(false)
+ end
+
+ it "returns the path if the path passes the tests" do
+ expect(path_helper.validate_path("C:\\ThisIsRigged")).to eql("C:\\ThisIsRigged")
+ end
+
+ it "does not raise an error if everything looks great" do
+ expect { path_helper.validate_path("C:\\cool path\\dude.exe") }.not_to raise_error
+ end
+
+ it "raises an error if the path has invalid characters" do
+ allow(path_helper).to receive(:printable?).and_return(false)
+ expect { path_helper.validate_path("Newline!\n") }.to raise_error(ChefConfig::InvalidPath)
+ end
+
+ it "Adds the \\\\?\\ prefix if the path exceeds MAX_LENGTH and does not have it" do
+ long_path = "C:\\" + "a" * 250 + "\\" + "b" * 250
+ prefixed_long_path = "\\\\?\\" + long_path
+ allow(path_helper).to receive(:windows_max_length_exceeded?).and_return(true)
+ expect(path_helper.validate_path(long_path)).to eql(prefixed_long_path)
+ end
+ end
+ end
+
+ describe "windows_max_length_exceeded?" do
+ it "returns true if the path is too long (259 + NUL) for the API" do
+ expect(path_helper.windows_max_length_exceeded?("C:\\" + "a" * 250 + "\\" + "b" * 6)).to be_truthy
+ end
+
+ it "returns false if the path is not too long (259 + NUL) for the standard API" do
+ expect(path_helper.windows_max_length_exceeded?("C:\\" + "a" * 250 + "\\" + "b" * 5)).to be_falsey
+ end
+
+ it "returns false if the path is over 259 characters but uses the \\\\?\\ prefix" do
+ expect(path_helper.windows_max_length_exceeded?("\\\\?\\C:\\" + "a" * 250 + "\\" + "b" * 250)).to be_falsey
+ end
+ end
+
+ describe "printable?" do
+ it "returns true if the string contains no non-printable characters" do
+ expect(path_helper.printable?("C:\\Program Files (x86)\\Microsoft Office\\Files.lst")).to be_truthy
+ end
+
+ it "returns true when given 'abc' in unicode" do
+ expect(path_helper.printable?("\u0061\u0062\u0063")).to be_truthy
+ end
+
+ it "returns true when given japanese unicode" do
+ expect(path_helper.printable?("\uff86\uff87\uff88")).to be_truthy
+ end
+
+ it "returns false if the string contains a non-printable character" do
+ expect(path_helper.printable?("\my files\work\notes.txt")).to be_falsey
+ end
+
+ # This isn't necessarily a requirement, but here to be explicit about functionality.
+ it "returns false if the string contains a newline or tab" do
+ expect(path_helper.printable?("\tThere's no way,\n\t *no* way,\n\t that you came from my loins.\n")).to be_falsey
+ end
+ end
+
+ describe "canonical_path" do
+ context "on windows", :windows_only do
+ it "returns an absolute path with backslashes instead of slashes" do
+ expect(path_helper.canonical_path("\\\\?\\C:/windows/win.ini")).to eq("\\\\?\\c:\\windows\\win.ini")
+ end
+
+ it "adds the \\\\?\\ prefix if it is missing" do
+ expect(path_helper.canonical_path("C:/windows/win.ini")).to eq("\\\\?\\c:\\windows\\win.ini")
+ end
+
+ it "returns a lowercase path" do
+ expect(path_helper.canonical_path("\\\\?\\C:\\CASE\\INSENSITIVE")).to eq("\\\\?\\c:\\case\\insensitive")
+ end
+ end
+
+ context "not on windows", :unix_only do
+ it "returns a canonical path" do
+ expect(path_helper.canonical_path("/etc//apache.d/sites-enabled/../sites-available/default")).to eq("/etc/apache.d/sites-available/default")
+ end
+ end
+ end
+
+ describe "paths_eql?" do
+ it "returns true if the paths are the same" do
+ allow(path_helper).to receive(:canonical_path).with("bandit").and_return("c:/bandit/bandit")
+ allow(path_helper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit")
+ expect(path_helper.paths_eql?("bandit", "../bandit/bandit")).to be_truthy
+ end
+
+ it "returns false if the paths are different" do
+ allow(path_helper).to receive(:canonical_path).with("bandit").and_return("c:/Bo/Bandit")
+ allow(path_helper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit")
+ expect(path_helper.paths_eql?("bandit", "../bandit/bandit")).to be_falsey
+ end
+ end
+
+ describe "escape_glob" do
+ it "escapes characters reserved by glob" do
+ path = "C:\\this\\*path\\[needs]\\escaping?"
+ escaped_path = "C:\\\\this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?"
+ expect(path_helper.escape_glob(path)).to eq(escaped_path)
+ end
+
+ context "when given more than one argument" do
+ it "joins, cleanpaths, and escapes characters reserved by glob" do
+ args = ["this/*path", "[needs]", "escaping?"]
+ escaped_path = if ChefConfig.windows?
+ "this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?"
+ else
+ "this/\\*path/\\[needs\\]/escaping\\?"
+ end
+ expect(path_helper).to receive(:join).with(*args).and_call_original
+ expect(path_helper).to receive(:cleanpath).and_call_original
+ expect(path_helper.escape_glob(*args)).to eq(escaped_path)
+ end
+ end
+ end
+
+ describe "all_homes" do
+ before do
+ stub_const('ENV', env)
+ allow(ChefConfig).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(path_helper.all_homes).to eq([])
+ end
+ end
+ end
+ end
+end