summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc A. Paradise <marc.paradise@gmail.com>2021-03-23 15:05:34 -0400
committerMarc A. Paradise <marc.paradise@gmail.com>2021-03-23 15:14:10 -0400
commitb87b6ee1d991fb71e167b8c75183257002622a82 (patch)
treebdb1f493e6c31873163cd922d5dc92bdb8d1188c
parentcff97d80bee0d1269eed28f4c128f56e69da4aad (diff)
downloadchef-mp/broken-gems.tar.gz
This is a collection of hacked-up and pared-down testsmp/broken-gems
and debug logging that reproduces a corrupt state in the Gem class. See comment in spec/spec_helper.rb labeled 'Hack warning' for all the gory details. In master, this is temporarily worked around by enabling the Gem.clear_paths in config.before(:each); that is commented out in this branch so that it can be repro'd immediately. Signed-off-by: Marc A. Paradise <marc.paradise@gmail.com>
-rw-r--r--chef-utils/lib/chef-utils/dsl/default_paths.rb8
-rw-r--r--lib/chef/knife/supermarket_share.rb55
-rw-r--r--lib/chef/provider/package/rubygems.rb16
-rw-r--r--spec/spec_helper.rb28
-rw-r--r--spec/unit/cookbook/gem_installer_spec.rb53
-rw-r--r--spec/unit/knife/supermarket_share_spec.rb207
-rw-r--r--spec/unit/mixin/shell_out_spec.rb277
-rw-r--r--spec/unit/provider/package/rubygems_spec.rb1156
-rw-r--r--tasks/rspec.rb14
9 files changed, 133 insertions, 1681 deletions
diff --git a/chef-utils/lib/chef-utils/dsl/default_paths.rb b/chef-utils/lib/chef-utils/dsl/default_paths.rb
index b2eb359273..2f25a6f099 100644
--- a/chef-utils/lib/chef-utils/dsl/default_paths.rb
+++ b/chef-utils/lib/chef-utils/dsl/default_paths.rb
@@ -32,8 +32,8 @@ module ChefUtils
# ensure the Ruby and Gem bindirs are included for omnibus chef installs
new_paths = env_path.split(path_separator)
[ __ruby_bindir, __gem_bindir ].compact.each do |path|
- new_paths = [ path ] + new_paths unless new_paths.include?(path)
- end
+ new_paths = [ path ] + new_paths unless new_paths.include?(path)
+ end
__default_paths.each do |path|
new_paths << path unless new_paths.include?(path)
end
@@ -51,7 +51,9 @@ module ChefUtils
end
def __gem_bindir
- Gem.bindir
+ dir = Gem.bindir
+ puts "__gem_bindir = #{dir} "
+ dir
end
extend self
diff --git a/lib/chef/knife/supermarket_share.rb b/lib/chef/knife/supermarket_share.rb
index 49b3474566..56b9f8ff69 100644
--- a/lib/chef/knife/supermarket_share.rb
+++ b/lib/chef/knife/supermarket_share.rb
@@ -54,58 +54,7 @@ class Chef
default: "https://supermarket.chef.io"
def run
- config[:cookbook_path] ||= Chef::Config[:cookbook_path]
-
- if @name_args.length < 1
- show_usage
- ui.fatal("You must specify the cookbook name.")
- exit(1)
- elsif @name_args.length < 2
- cookbook_name = @name_args[0]
- category = get_category(cookbook_name)
- else
- cookbook_name = @name_args[0]
- category = @name_args[1]
- end
-
- cl = Chef::CookbookLoader.new(config[:cookbook_path])
- if cl.cookbook_exists?(cookbook_name)
- cookbook = cl[cookbook_name]
- Chef::CookbookUploader.new(cookbook).validate_cookbooks
- tmp_cookbook_dir = Chef::CookbookSiteStreamingUploader.create_build_dir(cookbook)
- begin
- Chef::Log.trace("Temp cookbook directory is #{tmp_cookbook_dir.inspect}")
- ui.info("Making tarball #{cookbook_name}.tgz")
- shell_out!("#{tar_cmd} -czf #{cookbook_name}.tgz #{cookbook_name}", cwd: tmp_cookbook_dir)
- rescue => e
- ui.error("Error making tarball #{cookbook_name}.tgz: #{e.message}. Increase log verbosity (-VV) for more information.")
- Chef::Log.trace("\n#{e.backtrace.join("\n")}")
- exit(1)
- end
-
- if config[:dry_run]
- ui.info("Not uploading #{cookbook_name}.tgz due to --dry-run flag.")
- result = shell_out!("#{tar_cmd} -tzf #{cookbook_name}.tgz", cwd: tmp_cookbook_dir)
- ui.info(result.stdout)
- FileUtils.rm_rf tmp_cookbook_dir
- return
- end
-
- begin
- do_upload("#{tmp_cookbook_dir}/#{cookbook_name}.tgz", category, Chef::Config[:node_name], Chef::Config[:client_key])
- ui.info("Upload complete")
- Chef::Log.trace("Removing local staging directory at #{tmp_cookbook_dir}")
- FileUtils.rm_rf tmp_cookbook_dir
- rescue => e
- ui.error("Error uploading cookbook #{cookbook_name} to Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
- Chef::Log.trace("\n#{e.backtrace.join("\n")}")
- exit(1)
- end
-
- else
- ui.error("Could not find cookbook #{cookbook_name} in your cookbook path.")
- exit(1)
- end
+ # shell_out("ls")
end
def get_category(cookbook_name)
@@ -156,7 +105,9 @@ class Chef
if shell_out("which gnutar").exitstatus.equal?(0)
@tar_cmd = "gnutar"
end
+ puts "TAR_CMD: #{@tar_cmd}"
rescue Errno::ENOENT
+ puts "OOOPS - ENOENT!"
end
end
@tar_cmd
diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb
index e427cc0d24..4a6ae53fee 100644
--- a/lib/chef/provider/package/rubygems.rb
+++ b/lib/chef/provider/package/rubygems.rb
@@ -98,6 +98,20 @@ class Chef
# installed versions, you get the one from Chef's Ruby's default
# gems. This workaround ignores default gems entirely so we see
# only the installed gems.
+ puts "IN EXECUTION"
+ # OKAY IN HERE, @paths is already defined in Gem!
+ puts "Gem.bindir #{Gem.bindir}"
+ puts "gem_specification.dirs: #{gem_specification.dirs}"
+ #require 'pry'; binding.pry
+ puts "Gem.path: #{Gem.path}"
+ puts "PathSupportGem.home: #{Gem::PathSupport.new(ENV).home}"
+ # puts "ENV['GEM_PATH'] which seems returned from gem_specification.dirs in PathSupport: #{ENV['GEM_PATH']}"
+ # puts "Gem::VERSION: #{Gem::VERSION}"
+ # ::File.open("/home/marc/projects/chef/env.out", "w") do |f|
+ # ENV.each do |k,v|
+ # f.write("#{k}=#{v}\n")
+ # end
+ # end
stubs = gem_specification.send(:installed_stubs, gem_specification.dirs, "#{gem_dep.name}-*.gemspec")
# Filter down to only to only stubs we actually want. The name
# filter is needed in case of things like `foo-*.gemspec` also
@@ -306,6 +320,7 @@ class Chef
end
def gem_paths
+ puts "OHHHH NOOOO!"
if self.class.gempath_cache.key?(@gem_binary_location)
self.class.gempath_cache[@gem_binary_location]
else
@@ -348,6 +363,7 @@ class Chef
if self.class.platform_cache.key?(@gem_binary_location)
self.class.platform_cache[@gem_binary_location]
else
+ puts "OOPS I SHLLED OUT!"
gem_environment = shell_out!("#{@gem_binary_location} env").stdout
self.class.platform_cache[@gem_binary_location] = if jruby = gem_environment[JRUBY_PLATFORM]
["ruby", Gem::Platform.new(jruby)]
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 83cec749a7..64d9cdde39 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -232,6 +232,34 @@ RSpec.configure do |config|
Chef.reset!
+ # Hack warning:
+ #
+ # Something across gem_installer_spec and mixlib_cli specs are polluting gem state
+ # so that the 'unmockening' test in rubygems_spec fails. Calling Gem.clear_paths this before each test
+ # (or anything that causes `Gem.paths` to initialize or reinitialize) prevents
+ # the problem; this is a hacky workaround. The better answer would be to understand
+ # what is happening in those first two specs that are causing gem state to get broken,
+ # and the nature of the breakage. Time doesn't allow for that yet, so I leave this note instead.
+ #
+ # This was discovered in the process of removing `knife` specs from `chef`; a fourth
+ # spec (supermarket_share_spec) was hiding the problem, because in one of its test
+ # it made a call Gem.binpath (indirectly) - which caused it to initialize sooner, and prevents the
+ # failure.
+ #
+ # Of note, if we put this Gem.clear_paths IN the failing test, it does fix it. But it
+ # would leave the trap set for when another gemspec-searching test is added in the wrong place.
+ # Interestingly, using other methods like Gem.paths to fix it won't work inside a test, because
+ # Gem paths are alrady initalized and the damage is done.
+ # entirely outside of the spec file.
+
+ #
+ # The WIP to track this down (with a ton of puts debugs and the test cases pared down to nearly a minimum needed for a repro)
+ # is in mp/broken-gems if anyone wants to take that further. For now, calling Gem.clear_paths forces Gem to return
+ # to a sane internal state and avoids the issue. In that branch `rake gemfail` will run the specific tests in the order needed for
+
+ # Uncomment this to 'fix' the error you get when running `rake mini`.
+ #Gem.clear_paths
+
Chef::ChefFS::FileSystemCache.instance.reset!
Chef::Config.reset
diff --git a/spec/unit/cookbook/gem_installer_spec.rb b/spec/unit/cookbook/gem_installer_spec.rb
index 58843ac826..d31cc05b66 100644
--- a/spec/unit/cookbook/gem_installer_spec.rb
+++ b/spec/unit/cookbook/gem_installer_spec.rb
@@ -52,46 +52,15 @@ describe Chef::Cookbook::GemInstaller do
b.instance_eval(gemfile.string)
b
end
-
before(:each) do
- # Prepare mocks: using a StringIO instead of a File
- expect(Dir).to receive(:mktmpdir).and_yield("")
- expect(File).to receive(:open).and_yield(gemfile)
- expect(gemfile).to receive(:path).and_return("")
- expect(IO).to receive(:read).and_return("")
-
- end
-
- it "generates a valid Gemfile" do
- expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
- expect { gem_installer.install }.to_not raise_error
-
- expect { bundler_dsl }.to_not raise_error
- end
-
- it "generate a Gemfile with all constraints" do
- expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
- expect { gem_installer.install }.to_not raise_error
-
- expect(bundler_dsl.dependencies.find { |d| d.name == "httpclient" }.requirements_list.length).to eql(2)
- end
+ # # Prepare mocks: using a StringIO instead of a File
+ # expect(Dir).to receive(:mktmpdir).and_yield("")
+ # expect(File).to receive(:open).and_yield(gemfile)
+ # expect(gemfile).to receive(:path).and_return("")
+ # expect(IO).to receive(:read).and_return("")
- it "generates a valid Gemfile when Chef::Config[:rubygems_url] is set to a String" do
- expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
- Chef::Config[:rubygems_url] = "https://rubygems.org"
- expect { gem_installer.install }.to_not raise_error
-
- expect(bundler_dsl.dependencies.find { |d| d.name == "httpclient" }.requirements_list.length).to eql(2)
end
- it "generates a valid Gemfile when Chef::Config[:rubygems_url] is set to an Array" do
- expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
- Chef::Config[:rubygems_url] = [ "https://rubygems.org" ]
-
- expect { gem_installer.install }.to_not raise_error
-
- expect(bundler_dsl.dependencies.find { |d| d.name == "httpclient" }.requirements_list.length).to eql(2)
- end
it "skip metadata installation when Chef::Config[:skip_gem_metadata_installation] is set to true" do
Chef::Config[:skip_gem_metadata_installation] = true
@@ -99,16 +68,4 @@ describe Chef::Cookbook::GemInstaller do
expect(gem_installer.install).to be_nil
end
- it "install metadata when Chef::Config[:skip_gem_metadata_installation] is not true" do
- expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
- expect(Chef::Log).to receive(:info).and_return("")
- expect(gem_installer.install).to be_nil
- end
-
- it "install from local cache when Chef::Config[:gem_installer_bundler_options] is set to local" do
- Chef::Config[:gem_installer_bundler_options] = "--local"
- expect(gem_installer).to receive(:shell_out!).with(["bundle", "install", "--local"], any_args).and_return(shell_out)
- expect(Chef::Log).to receive(:info).and_return("")
- expect(gem_installer.install).to be_nil
- end
end
diff --git a/spec/unit/knife/supermarket_share_spec.rb b/spec/unit/knife/supermarket_share_spec.rb
index 088cef9dfd..0c6d56180a 100644
--- a/spec/unit/knife/supermarket_share_spec.rb
+++ b/spec/unit/knife/supermarket_share_spec.rb
@@ -1,20 +1,3 @@
-#
-# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright (c) 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/knife/supermarket_share"
@@ -23,186 +6,22 @@ require "chef/cookbook_site_streaming_uploader"
describe Chef::Knife::SupermarketShare do
- before(:each) do
- @knife = Chef::Knife::SupermarketShare.new
- # Merge default settings in.
- @knife.merge_configs
- @knife.name_args = %w{cookbook_name AwesomeSausage}
+ it "should pass now" do
+ # ShellOut was a red herring. Internally it eventually calls Gem.binpath,
+ # which is what was hiding the failure in the unmockening, below.
+ # class SOT
+ # include Chef::Mixin::ShellOut
+ # end
- @cookbook = Chef::CookbookVersion.new("cookbook_name")
+ # class SOT
+ # include Chef::Mixin::ShellOut
+ # end
+ # SOT.new.shell_out("ls")
- @cookbook_loader = double("Chef::CookbookLoader")
- allow(@cookbook_loader).to receive(:cookbook_exists?).and_return(true)
- allow(@cookbook_loader).to receive(:[]).and_return(@cookbook)
- allow(Chef::CookbookLoader).to receive(:new).and_return(@cookbook_loader)
- @noauth_rest = double(Chef::ServerAPI)
- allow(@knife).to receive(:noauth_rest).and_return(@noauth_rest)
- @cookbook_uploader = Chef::CookbookUploader.new("herpderp", rest: "norest")
- allow(Chef::CookbookUploader).to receive(:new).and_return(@cookbook_uploader)
- allow(@cookbook_uploader).to receive(:validate_cookbooks).and_return(true)
- allow(Chef::CookbookSiteStreamingUploader).to receive(:create_build_dir).and_return(Dir.mktmpdir)
-
- allow(@knife).to receive(:shell_out!).and_return(true)
- @stdout = StringIO.new
- @stderr = StringIO.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
+ # This was coming through knife.run, I've moved it here because it's the minimum
+ # change to suppress the error. Uncomment it to see it in action.
+ # Gem.dir
end
-
- describe "run" do
-
- before(:each) do
- allow(@knife).to receive(:do_upload).and_return(true)
- @category_response = {
- "name" => "cookbook_name",
- "category" => "Testing Category",
- }
- @bad_category_response = {
- "error_code" => "NOT_FOUND",
- "error_messages" => [
- "Resource does not exist.",
- ],
- }
- end
-
- it "should set true to config[:dry_run] as default" do
- expect(@knife.config[:dry_run]).to be_falsey
- end
-
- it "should should print usage and exit when given no arguments" do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should not fail when given only 1 argument and can determine category" do
- @knife.name_args = ["cookbook_name"]
- expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name").and_return(@category_response)
- expect(@knife).to receive(:do_upload)
- @knife.run
- end
-
- it "should use a default category when given only 1 argument and cannot determine category" do
- @knife.name_args = ["cookbook_name"]
- expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name") { raise Net::HTTPClientException.new("404 Not Found", OpenStruct.new(code: "404")) }
- expect(@knife).to receive(:do_upload)
- expect { @knife.run }.to_not raise_error
- end
-
- it "should print error and exit when given only 1 argument and Chef::ServerAPI throws an exception" do
- @knife.name_args = ["cookbook_name"]
- expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name") { raise Errno::ECONNREFUSED, "Connection refused" }
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should check if the cookbook exists" do
- expect(@cookbook_loader).to receive(:cookbook_exists?)
- @knife.run
- end
-
- it "should exit and log to error if the cookbook doesn't exist" do
- allow(@cookbook_loader).to receive(:cookbook_exists?).and_return(false)
- expect(@knife.ui).to receive(:error)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- if File.exist?("/usr/bin/gnutar") || File.exist?("/bin/gnutar")
- it "should use gnutar to make a tarball of the cookbook" do
- expect(@knife).to receive(:shell_out!) do |args|
- expect(args.to_s).to match(/gnutar -czf/)
- end
- @knife.run
- end
- else
- it "should make a tarball of the cookbook" do
- expect(@knife).to receive(:shell_out!) do |args|
- expect(args.to_s).to match(/tar -czf/)
- end
- @knife.run
- end
- end
-
- it "should exit and log to error when the tarball creation fails" do
- allow(@knife).to receive(:shell_out!).and_raise(Chef::Exceptions::Exec)
- expect(@knife.ui).to receive(:error)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should upload the cookbook and clean up the tarball" do
- expect(@knife).to receive(:do_upload)
- expect(FileUtils).to receive(:rm_rf)
- @knife.run
- end
-
- context "when the --dry-run flag is specified" do
- before do
- allow(Chef::CookbookSiteStreamingUploader).to receive(:create_build_dir).and_return("/var/tmp/dummy")
- @knife.config = { dry_run: true }
- @so = instance_double("Mixlib::ShellOut")
- allow(@knife).to receive(:shell_out!).and_return(@so)
- allow(@so).to receive(:stdout).and_return("file")
- end
-
- it "should list files in the tarball" do
- allow(@knife).to receive(:tar_cmd).and_return("footar")
- expect(@knife).to receive(:shell_out!).with("footar -czf #{@cookbook.name}.tgz #{@cookbook.name}", { cwd: "/var/tmp/dummy" })
- expect(@knife).to receive(:shell_out!).with("footar -tzf #{@cookbook.name}.tgz", { cwd: "/var/tmp/dummy" })
- @knife.run
- end
-
- it "does not upload the cookbook" do
- expect(@knife).not_to receive(:do_upload)
- @knife.run
- end
- end
- end
-
- describe "do_upload" do
-
- before(:each) do
- @upload_response = double("Net::HTTPResponse")
- allow(Chef::CookbookSiteStreamingUploader).to receive(:post).and_return(@upload_response)
-
- allow(File).to receive(:open).and_return(true)
- end
-
- it 'should post the cookbook to "https://supermarket.chef.io"' do
- response_text = Chef::JSONCompat.to_json({ uri: "https://supermarket.chef.io/cookbooks/cookbook_name" })
- allow(@upload_response).to receive(:body).and_return(response_text)
- allow(@upload_response).to receive(:code).and_return(201)
- expect(Chef::CookbookSiteStreamingUploader).to receive(:post).with(/supermarket\.chef\.io/, anything, anything, anything)
- @knife.run
- end
-
- it "should alert the user when a version already exists" do
- response_text = Chef::JSONCompat.to_json({ error_messages: ["Version already exists"] })
- allow(@upload_response).to receive(:body).and_return(response_text)
- allow(@upload_response).to receive(:code).and_return(409)
- expect { @knife.run }.to raise_error(SystemExit)
- expect(@stderr.string).to match(/ERROR(.+)cookbook already exists/)
- end
-
- it "should pass any errors on to the user" do
- response_text = Chef::JSONCompat.to_json({ error_messages: ["You're holding it wrong"] })
- allow(@upload_response).to receive(:body).and_return(response_text)
- allow(@upload_response).to receive(:code).and_return(403)
- expect { @knife.run }.to raise_error(SystemExit)
- expect(@stderr.string).to match("ERROR(.*)You're holding it wrong")
- end
-
- it "should print the body if no errors are exposed on failure" do
- response_text = Chef::JSONCompat.to_json({ system_error: "Your call was dropped", reason: "There's a map for that" })
- allow(@upload_response).to receive(:body).and_return(response_text)
- allow(@upload_response).to receive(:code).and_return(500)
- expect(@knife.ui).to receive(:error).with(/#{Regexp.escape(response_text)}/) # .ordered
- expect(@knife.ui).to receive(:error).with(/Unknown error/) # .ordered
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- end
-
end
diff --git a/spec/unit/mixin/shell_out_spec.rb b/spec/unit/mixin/shell_out_spec.rb
index 2b76a10e6c..d4b052a595 100644
--- a/spec/unit/mixin/shell_out_spec.rb
+++ b/spec/unit/mixin/shell_out_spec.rb
@@ -36,272 +36,47 @@ describe Chef::Mixin::ShellOut do
end
context "when testing individual methods" do
- before(:each) do
- @original_env = ENV.to_hash
- ENV.clear
- end
-
- after(:each) do
- ENV.clear
- ENV.update(@original_env)
- end
+ before(:each) do
+ @original_env = ENV.to_hash
+ ENV.clear
+ end
+ #
+ after(:each) do
+ ENV.clear
+ ENV.update(@original_env)
+ end
let(:retobj) { instance_double(Mixlib::ShellOut, "error!" => false) }
let(:cmd) { "echo '#{rand(1000)}'" }
- %i{shell_out shell_out!}.each do |method|
+ %i{shell_out}.each do |method|
describe "##{method}" do
describe "when the last argument is a Hash" do
+ # Fails for either one of these - ANYTHING that invokes?
describe "and environment is an option" do
it "should not change environment language settings when they are set to nil" do
- options = { environment: { "LC_ALL" => nil, "LANGUAGE" => nil, "LANG" => nil, env_path => nil } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, **options).and_return(retobj)
- shell_out_obj.send(method, cmd, **options)
- end
-
- it "should not change environment language settings when they are set to non-nil" do
- options = { environment: { "LC_ALL" => "en_US.UTF-8", "LANGUAGE" => "en_US.UTF-8", "LANG" => "en_US.UTF-8", env_path => "foo:bar:baz" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, **options).and_return(retobj)
- shell_out_obj.send(method, cmd, **options)
- end
-
- it "should set environment language settings to the configured internal locale when they are not present" do
- options = { environment: { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
- environment: {
- "HOME" => "/Users/morty",
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- env_path => shell_out_obj.default_paths,
- }).and_return(retobj)
- shell_out_obj.send(method, cmd, **options)
- end
-
- it "should not mutate the options hash when it adds language settings" do
- options = { environment: { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
- environment: {
- "HOME" => "/Users/morty",
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- env_path => shell_out_obj.default_paths,
- }).and_return(retobj)
- shell_out_obj.send(method, cmd, **options)
- expect(options[:environment].key?("LC_ALL")).to be false
- end
- end
+ shell_out_obj.send(method, cmd )
- describe "and env is an option" do
- it "should not change env when langauge options are set to nil" do
- options = { env: { "LC_ALL" => nil, "LANG" => nil, "LANGUAGE" => nil, env_path => nil } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, **options).and_return(retobj)
- shell_out_obj.send(method, cmd, **options)
- end
-
- it "should not change env when language options are set to non-nil" do
- options = { env: { "LC_ALL" => "de_DE.UTF-8", "LANG" => "de_DE.UTF-8", "LANGUAGE" => "de_DE.UTF-8", env_path => "foo:bar:baz" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, **options).and_return(retobj)
- shell_out_obj.send(method, cmd, **options)
- end
-
- it "should set environment language settings to the configured internal locale when they are not present" do
- options = { env: { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
- env: {
- "HOME" => "/Users/morty",
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- env_path => shell_out_obj.default_paths,
- }).and_return(retobj)
- shell_out_obj.send(method, cmd, **options)
- end
-
- it "should not mutate the options hash when it adds language settings" do
- options = { env: { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
- env: {
- "HOME" => "/Users/morty",
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- env_path => shell_out_obj.default_paths,
- }).and_return(retobj)
- shell_out_obj.send(method, cmd, **options)
- expect(options[:env].key?("LC_ALL")).to be false
end
end
- describe "and no env/environment option is present" do
- it "should set environment language settings to the configured internal locale" do
- options = { user: "morty" }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
- user: "morty",
- environment: {
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- env_path => shell_out_obj.default_paths,
- }).and_return(retobj)
- shell_out_obj.send(method, cmd, **options)
- end
- end
+ # describe "and no env/environment option is present" do
+ # it "should set environment language settings to the configured internal locale" do
+ # options = { user: "morty" }
+ # expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
+ # user: "morty",
+ # environment: {
+ # "LC_ALL" => Chef::Config[:internal_locale],
+ # "LANG" => Chef::Config[:internal_locale],
+ # "LANGUAGE" => Chef::Config[:internal_locale],
+ # env_path => shell_out_obj.default_paths,
+ # }).and_return(retobj)
+ # shell_out_obj.send(method, cmd, **options)
+ # end
+ # end
end
- describe "when the last argument is not a Hash" do
- it "should set environment language settings to the configured internal locale" do
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
- environment: {
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- env_path => shell_out_obj.default_paths,
- }).and_return(retobj)
- shell_out_obj.send(method, cmd)
- end
- end
- end
- end
-
- describe "#shell_out default_env: false" do
-
- describe "when the last argument is a Hash" do
- describe "and environment is an option" do
- it "should not change environment['LC_ALL'] when set to nil" do
- options = { environment: { "LC_ALL" => nil } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out(cmd, **options, default_env: false)
- end
-
- it "should not change environment['LC_ALL'] when set to non-nil" do
- options = { environment: { "LC_ALL" => "en_US.UTF-8" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out(cmd, **options, default_env: false)
- end
-
- it "should no longer set environment['LC_ALL'] to nil when 'LC_ALL' not present" do
- options = { environment: { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out(cmd, **options, default_env: false)
- end
- end
-
- describe "and env is an option" do
- it "should not change env when set to nil" do
- options = { env: { "LC_ALL" => nil } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out(cmd, **options, default_env: false)
- end
-
- it "should not change env when set to non-nil" do
- options = { env: { "LC_ALL" => "en_US.UTF-8" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out(cmd, **options, default_env: false)
- end
-
- it "should no longer set env['LC_ALL'] to nil when 'LC_ALL' not present" do
- options = { env: { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out(cmd, **options, default_env: false)
- end
- end
-
- describe "and no env/environment option is present" do
- it "should no longer add environment option and set environment['LC_ALL'] to nil" do
- options = { user: "morty" }
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out(cmd, **options, default_env: false)
- end
- end
- end
-
- describe "when the last argument is not a Hash" do
- it "should no longer add environment options and set environment['LC_ALL'] to nil" do
- expect(shell_out_obj).to receive(:__shell_out_command).with(cmd).and_return(true)
- shell_out_obj.shell_out(cmd, default_env: false)
- end
- end
- end
-
- describe "Custom Resource timeouts" do
- class CustomResource < Chef::Resource
- provides :whatever
-
- property :timeout, Numeric
-
- action :install do
- end
- end
-
- let(:new_resource) { CustomResource.new("foo") }
- let(:provider) { new_resource.provider_for_action(:install) }
-
- %i{shell_out shell_out!}.each do |method|
- stubbed_method = (method == :shell_out) ? :shell_out_compacted : :shell_out_compacted!
- it "#{method} defaults to 900 seconds" do
- expect(provider).to receive(stubbed_method).with("foo", timeout: 900)
- provider.send(method, "foo")
- end
- it "#{method} overrides the default timeout with its options" do
- expect(provider).to receive(stubbed_method).with("foo", timeout: 1)
- provider.send(method, "foo", timeout: 1)
- end
- it "#{method} overrides the new_resource.timeout with the timeout option" do
- new_resource.timeout(99)
- expect(provider).to receive(stubbed_method).with("foo", timeout: 1)
- provider.send(method, "foo", timeout: 1)
- end
- it "#{method} defaults to 900 seconds and preserves options" do
- expect(provider).to receive(stubbed_method).with("foo", env: nil, timeout: 900)
- provider.send(method, "foo", env: nil)
- end
- it "#{method} overrides the default timeout with its options and preserves options" do
- expect(provider).to receive(stubbed_method).with("foo", timeout: 1, env: nil)
- provider.send(method, "foo", timeout: 1, env: nil)
- end
- it "#{method} overrides the new_resource.timeout with the timeout option and preseves options" do
- new_resource.timeout(99)
- expect(provider).to receive(stubbed_method).with("foo", timeout: 1, env: nil)
- provider.send(method, "foo", timeout: 1, env: nil)
- end
- end
- end
-
- describe "timeouts" do
- let(:new_resource) { Chef::Resource::Package.new("foo") }
- let(:provider) { new_resource.provider_for_action(:install) }
-
- %i{shell_out shell_out!}.each do |method|
- stubbed_method = (method == :shell_out) ? :shell_out_compacted : :shell_out_compacted!
- it "#{method} defaults to 900 seconds" do
- expect(provider).to receive(stubbed_method).with("foo", timeout: 900)
- provider.send(method, "foo")
- end
- it "#{method} overrides the default timeout with its options" do
- expect(provider).to receive(stubbed_method).with("foo", timeout: 1)
- provider.send(method, "foo", timeout: 1)
- end
- it "#{method} overrides the new_resource.timeout with the timeout option" do
- new_resource.timeout(99)
- expect(provider).to receive(stubbed_method).with("foo", timeout: 1)
- provider.send(method, "foo", timeout: 1)
- end
- it "#{method} defaults to 900 seconds and preserves options" do
- expect(provider).to receive(stubbed_method).with("foo", env: nil, timeout: 900)
- provider.send(method, "foo", env: nil)
- end
- it "#{method} overrides the default timeout with its options and preserves options" do
- expect(provider).to receive(stubbed_method).with("foo", timeout: 1, env: nil)
- provider.send(method, "foo", timeout: 1, env: nil)
- end
- it "#{method} overrides the new_resource.timeout with the timeout option and preseves options" do
- new_resource.timeout(99)
- expect(provider).to receive(stubbed_method).with("foo", timeout: 1, env: nil)
- provider.send(method, "foo", timeout: 1, env: nil)
- end
end
end
end
diff --git a/spec/unit/provider/package/rubygems_spec.rb b/spec/unit/provider/package/rubygems_spec.rb
index 7153d6be3e..13a1338251 100644
--- a/spec/unit/provider/package/rubygems_spec.rb
+++ b/spec/unit/provider/package/rubygems_spec.rb
@@ -17,15 +17,6 @@
# limitations under the License.
#
-module GemspecBackcompatCreator
- def gemspec(name, version)
- if Gem::Specification.new.method(:initialize).arity == 0
- Gem::Specification.new { |s| s.name = name; s.version = version }
- else
- Gem::Specification.new(name, version)
- end
- end
-end
# this is a global variable we construct of the highest rspec-core version which is installed, using APIs which
# will break out of the bundle -- and done this way so that we can mock all these internal Gem APIs later...
@@ -38,13 +29,29 @@ class RspecVersionString
end
end
end
+# This forces us to load the expected version string before any of our tests (in the entire suite)
+# execute, ensuring that it's a 'clean' result for the environment.
RspecVersionString.rspec_version_string
+
require "spec_helper"
require "ostruct"
+
+describe "here we go" do
+ it "should pass now" do
+ # Uncommenting Gem.dir below does not let it pass, interestingly - though the same
+ # action in a different example, located in a different file, will 'Fix' it.
+ # # In other cases (like the same thing done in the hacked supermarket_share_spec
+ # #) Referring to Gem.paths does fix it. AT time of invocation, Gem's @paths is not
+ # initialized in those cases - but it is already initialized when we run this example,
+ # so the corruption has already occurred by the time we're here.
+ #Gem.dir
+ end
+end
+# end
+
describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
- include GemspecBackcompatCreator
let(:logger) { double("Mixlib::Log::Child").as_null_object }
before do
@@ -53,1132 +60,15 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
WebMock.disable_net_connect!
end
-
- it "determines the gem paths from the in memory rubygems" do
- expect(@gem_env.gem_paths).to eq(Gem.path)
- end
-
- it "determines the installed versions of gems from Gem.source_index" do
- gems = [gemspec("rspec-core", Gem::Version.new("1.2.9")), gemspec("rspec-core", Gem::Version.new("1.3.0"))]
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7")
- expect(Gem::Specification).to receive(:dirs).and_return(["/path/to/gems/specifications", "/another/path/to/gems/specifications"])
- expect(Gem::Specification).to receive(:installed_stubs).with(["/path/to/gems/specifications", "/another/path/to/gems/specifications"], "rspec-core-*.gemspec").and_return(gems)
- else # >= Rubygems 1.8 behavior
- expect(Gem::Specification).to receive(:find_all_by_name).with("rspec-core", Gem::Dependency.new("rspec-core").requirement).and_return(gems)
- end
- expect(@gem_env.installed_versions(Gem::Dependency.new("rspec-core", nil))).to eq(gems)
- end
-
it "determines the installed versions of gems from the source index (part2: the unmockening)" do
- expected = ["rspec-core", Gem::Version.new( RspecVersionString.rspec_version_string )]
- actual = @gem_env.installed_versions(Gem::Dependency.new("rspec-core", nil)).map { |spec| [spec.name, spec.version] }
- expect(actual).to include(expected)
- end
-
- it "yields to a block with an alternate source list set" do
- sources_in_block = nil
- normal_sources = Gem.sources
- begin
- @gem_env.with_gem_sources("http://gems.example.org") do
- sources_in_block = Gem.sources
- raise "sources should be reset even in case of an error"
- end
- rescue RuntimeError
- end
- expect(sources_in_block).to eq(%w{http://gems.example.org})
- expect(Gem.sources).to eq(normal_sources)
- end
-
- it "it doesnt alter the gem sources if none are set" do
- sources_in_block = nil
- normal_sources = Gem.sources
- begin
- @gem_env.with_gem_sources(nil) do
- sources_in_block = Gem.sources
- raise "sources should be reset even in case of an error"
- end
- rescue RuntimeError
- end
- expect(sources_in_block).to eq(normal_sources)
- expect(Gem.sources).to eq(normal_sources)
- end
-
- context "new default rubygems behavior" do
- before do
- Chef::Config[:rubygems_cache_enabled] = false
-
- dep_installer = Gem::DependencyInstaller.new
- expect(dep_installer).not_to receive(:find_gems_with_sources)
- allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
- end
-
- it "finds a matching gem candidate version on rubygems 2.0.0+" do
- stub_request(:head, "https://index.rubygems.org/")
- .to_return(status: 200, body: "", headers: {})
- stub_request(:get, "https://index.rubygems.org/info/sexp_processor")
- .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-info")))
- stub_request(:get, "https://index.rubygems.org/quick/Marshal.4.8/sexp_processor-4.15.1.gemspec.rz")
- .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-4.15.1.gemspec.rz")))
-
- dep = Gem::Dependency.new("sexp_processor", ">= 0")
- expect(@gem_env.candidate_version_from_remote(dep)).to be_kind_of(Gem::Version)
- end
-
- it "gives the candidate version as nil if none is found" do
- stub_request(:head, "https://index.rubygems.org/")
- .to_return(status: 200, body: "", headers: {})
- stub_request(:get, "https://index.rubygems.org/info/nonexistent_gem")
- .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "nonexistent_gem-info")))
-
- dep = Gem::Dependency.new("nonexistent_gem", ">= 0")
- expect(@gem_env.candidate_version_from_remote(dep)).to be_nil
- end
-
- it "finds a matching gem from a specific gemserver when explicit sources are given (to a server that doesn't respond to api requests)" do
- stub_request(:head, "https://rubygems2.org/")
- .to_return(status: 200, body: "", headers: {})
- stub_request(:get, "https://rubygems2.org/info/sexp_processor")
- .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-info")))
- stub_request(:get, "https://rubygems2.org/quick/Marshal.4.8/sexp_processor-4.15.1.gemspec.rz")
- .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-4.15.1.gemspec.rz")))
-
- dep = Gem::Dependency.new("sexp_processor", ">= 0")
- expect(@gem_env.candidate_version_from_remote(dep, "https://rubygems2.org")).to be_kind_of(Gem::Version)
- end
- end
-
- context "old rubygems caching behavior" do
- before do
- Chef::Config[:rubygems_cache_enabled] = true
-
- stub_request(:get, "https://rubygems.org/latest_specs.4.8.gz")
- .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "latest_specs.4.8.gz")))
- end
-
- it "finds a matching gem candidate version on rubygems 2.0.0+" do
- stub_request(:get, "https://rubygems.org/quick/Marshal.4.8/sexp_processor-4.15.1.gemspec.rz")
- .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-4.15.1.gemspec.rz")))
-
- dep = Gem::Dependency.new("sexp_processor", ">= 0")
- expect(@gem_env.candidate_version_from_remote(dep)).to be_kind_of(Gem::Version)
- end
-
- it "gives the candidate version as nil if none is found" do
- dep = Gem::Dependency.new("lksdjflksdjflsdkfj", ">= 0")
- expect(@gem_env.candidate_version_from_remote(dep)).to be_nil
- end
-
- it "finds a matching gem from a specific gemserver when explicit sources are given" do
- stub_request(:get, "https://rubygems.org/quick/Marshal.4.8/sexp_processor-4.15.1.gemspec.rz")
- .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-4.15.1.gemspec.rz")))
-
- dep = Gem::Dependency.new("sexp_processor", ">= 0")
- expect(@gem_env.candidate_version_from_remote(dep, "http://rubygems2.org")).to be_kind_of(Gem::Version)
- end
- end
-
- it "finds a matching candidate version from a .gem file when the path to the gem is supplied" do
- location = CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem"
- expect(@gem_env.candidate_version_from_file(Gem::Dependency.new("chef-integration-test", ">= 0"), location)).to eq(Gem::Version.new("0.1.0"))
- expect(@gem_env.candidate_version_from_file(Gem::Dependency.new("chef-integration-test", ">= 0.2.0"), location)).to be_nil
- end
-
- it "installs a gem with a hash of options for the dependency installer" do
- dep_installer = Gem::DependencyInstaller.new
- expect(@gem_env).to receive(:dependency_installer).with(install_dir: "/foo/bar").and_return(dep_installer)
- expect(@gem_env).to receive(:with_gem_sources).with("http://gems.example.com").and_yield
- expect(dep_installer).to receive(:install).with(Gem::Dependency.new("rspec", ">= 0"))
- @gem_env.install(Gem::Dependency.new("rspec", ">= 0"), install_dir: "/foo/bar", sources: ["http://gems.example.com"])
- end
-
- it "builds an uninstaller for a gem with options set to avoid requiring user input" do
- # default options for uninstaller should be:
- # :ignore => true, :executables => true
- expect(Gem::Uninstaller).to receive(:new).with("rspec", ignore: true, executables: true)
- @gem_env.uninstaller("rspec")
- end
-
- it "uninstalls all versions of a gem" do
- uninstaller = double("gem uninstaller")
- expect(uninstaller).to receive(:uninstall)
- expect(@gem_env).to receive(:uninstaller).with("rspec", all: true).and_return(uninstaller)
- @gem_env.uninstall("rspec")
- end
-
- it "uninstalls a specific version of a gem" do
- uninstaller = double("gem uninstaller")
- expect(uninstaller).to receive(:uninstall)
- expect(@gem_env).to receive(:uninstaller).with("rspec", version: "1.2.3").and_return(uninstaller)
- @gem_env.uninstall("rspec", "1.2.3")
- end
-
-end
-
-describe Chef::Provider::Package::Rubygems::AlternateGemEnvironment do
- include GemspecBackcompatCreator
-
- before do
- Chef::Provider::Package::Rubygems::AlternateGemEnvironment.gempath_cache.clear
- Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache.clear
- @gem_env = Chef::Provider::Package::Rubygems::AlternateGemEnvironment.new("/usr/weird/bin/gem")
- end
-
- it "determines the gem paths from shelling out to gem env" do
- gem_env_output = ["/path/to/gems", "/another/path/to/gems"].join(File::PATH_SEPARATOR)
- shell_out_result = OpenStruct.new(stdout: gem_env_output)
- expect(@gem_env).to receive(:shell_out_compacted!).with("/usr/weird/bin/gem env gempath").and_return(shell_out_result)
- expect(@gem_env.gem_paths).to eq(["/path/to/gems", "/another/path/to/gems"])
- end
-
- it "caches the gempaths by gem_binary" do
- gem_env_output = ["/path/to/gems", "/another/path/to/gems"].join(File::PATH_SEPARATOR)
- shell_out_result = OpenStruct.new(stdout: gem_env_output)
- expect(@gem_env).to receive(:shell_out_compacted!).with("/usr/weird/bin/gem env gempath").and_return(shell_out_result)
- expected = ["/path/to/gems", "/another/path/to/gems"]
- expect(@gem_env.gem_paths).to eq(["/path/to/gems", "/another/path/to/gems"])
- expect(Chef::Provider::Package::Rubygems::AlternateGemEnvironment.gempath_cache["/usr/weird/bin/gem"]).to eq(expected)
- end
-
- it "uses the cached result for gem paths when available" do
- expect(@gem_env).not_to receive(:shell_out_compacted!)
- expected = ["/path/to/gems", "/another/path/to/gems"]
- Chef::Provider::Package::Rubygems::AlternateGemEnvironment.gempath_cache["/usr/weird/bin/gem"] = expected
- expect(@gem_env.gem_paths).to eq(["/path/to/gems", "/another/path/to/gems"])
- end
-
- it "builds the gems source index from the gem paths" do
- allow(@gem_env).to receive(:gem_paths).and_return(["/path/to/gems", "/another/path/to/gems"])
- @gem_env.gem_specification
- expect(Gem::Specification.dirs).to eq([ "/path/to/gems/specifications", "/another/path/to/gems/specifications" ])
- end
- it "determines the installed versions of gems from the source index" do
- gems = [gemspec("rspec", Gem::Version.new("1.2.9")), gemspec("rspec", Gem::Version.new("1.3.0"))]
- rspec_dep = Gem::Dependency.new("rspec", nil)
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7")
- allow(@gem_env).to receive(:gem_specification).and_return(Gem::Specification)
- expect(Gem::Specification).to receive(:dirs).and_return(["/path/to/gems/specifications", "/another/path/to/gems/specifications"])
- expect(Gem::Specification).to receive(:installed_stubs).with(["/path/to/gems/specifications", "/another/path/to/gems/specifications"], "rspec-*.gemspec").and_return(gems)
- else # >= rubygems 1.8 behavior
- allow(@gem_env).to receive(:gem_specification).and_return(Gem::Specification)
- expect(@gem_env.gem_specification).to receive(:find_all_by_name).with(rspec_dep.name, rspec_dep.requirement).and_return(gems)
- end
- expect(@gem_env.installed_versions(Gem::Dependency.new("rspec", nil))).to eq(gems)
- end
-
- it "determines the installed versions of gems from the source index (part2: the unmockening)" do
- allow($stdout).to receive(:write)
- path_to_gem = if windows?
- `where gem`.split[1]
- else
- `which gem`.strip
- end
- skip("cant find your gem executable") if path_to_gem.empty?
- gem_env = Chef::Provider::Package::Rubygems::AlternateGemEnvironment.new(path_to_gem)
+ puts "BEFORE WE TEST"
+ puts "Gem.path: #{Gem.path}"
+ puts "Gem::Specification.dirs: #{Gem::Specification.dirs}"
+ puts "PathSupportGem.home: #{Gem::PathSupport.new(ENV).home}"
expected = ["rspec-core", Gem::Version.new( RspecVersionString.rspec_version_string )]
- actual = gem_env.installed_versions(Gem::Dependency.new("rspec-core", nil)).map { |s| [s.name, s.version] }
+ #require 'pry'; binding.pry
+ actual = @gem_env.installed_versions(Gem::Dependency.new("rspec-core", nil)).map { |spec| [spec.name, spec.version] }
expect(actual).to include(expected)
end
-
- it "detects when the target gem environment is the jruby platform" do
- gem_env_out = <<~JRUBY_GEM_ENV
- RubyGems Environment:
- - RUBYGEMS VERSION: 1.3.6
- - RUBY VERSION: 1.8.7 (2010-05-12 patchlevel 249) [java]
- - INSTALLATION DIRECTORY: /Users/you/.rvm/gems/jruby-1.5.0
- - RUBY EXECUTABLE: /Users/you/.rvm/rubies/jruby-1.5.0/bin/jruby
- - EXECUTABLE DIRECTORY: /Users/you/.rvm/gems/jruby-1.5.0/bin
- - RUBYGEMS PLATFORMS:
- - ruby
- - universal-java-1.6
- - GEM PATHS:
- - /Users/you/.rvm/gems/jruby-1.5.0
- - /Users/you/.rvm/gems/jruby-1.5.0@global
- - GEM CONFIGURATION:
- - :update_sources => true
- - :verbose => true
- - :benchmark => false
- - :backtrace => false
- - :bulk_threshold => 1000
- - "install" => "--env-shebang"
- - "update" => "--env-shebang"
- - "gem" => "--no-rdoc --no-ri"
- - :sources => ["https://rubygems.org/", "http://gems.github.com/"]
- - REMOTE SOURCES:
- - https://rubygems.org/
- - http://gems.github.com/
- JRUBY_GEM_ENV
- expect(@gem_env).to receive(:shell_out_compacted!).with("/usr/weird/bin/gem env").and_return(double("jruby_gem_env", stdout: gem_env_out))
- expected = ["ruby", Gem::Platform.new("universal-java-1.6")]
- expect(@gem_env.gem_platforms).to eq(expected)
- # it should also cache the result
- expect(Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache["/usr/weird/bin/gem"]).to eq(expected)
- end
-
- it "uses the cached result for gem platforms if available" do
- expect(@gem_env).not_to receive(:shell_out_compacted!)
- expected = ["ruby", Gem::Platform.new("universal-java-1.6")]
- Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache["/usr/weird/bin/gem"] = expected
- expect(@gem_env.gem_platforms).to eq(expected)
- end
-
- it "uses the current gem platforms when the target env is not jruby" do
- gem_env_out = <<~RBX_GEM_ENV
- RubyGems Environment:
- - RUBYGEMS VERSION: 1.3.6
- - RUBY VERSION: 1.8.7 (2010-05-14 patchlevel 174) [x86_64-apple-darwin10.3.0]
- - INSTALLATION DIRECTORY: /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514
- - RUBYGEMS PREFIX: /Users/ddeleo/.rvm/rubies/rbx-1.0.0-20100514
- - RUBY EXECUTABLE: /Users/ddeleo/.rvm/rubies/rbx-1.0.0-20100514/bin/rbx
- - EXECUTABLE DIRECTORY: /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514/bin
- - RUBYGEMS PLATFORMS:
- - ruby
- - x86_64-darwin-10
- - x86_64-rubinius-1.0
- - GEM PATHS:
- - /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514
- - /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514@global
- - GEM CONFIGURATION:
- - :update_sources => true
- - :verbose => true
- - :benchmark => false
- - :backtrace => false
- - :bulk_threshold => 1000
- - :sources => ["https://rubygems.org/", "http://gems.github.com/"]
- - "gem" => "--no-rdoc --no-ri"
- - REMOTE SOURCES:
- - https://rubygems.org/
- - http://gems.github.com/
- RBX_GEM_ENV
- expect(@gem_env).to receive(:shell_out_compacted!).with("/usr/weird/bin/gem env").and_return(double("rbx_gem_env", stdout: gem_env_out))
- expect(@gem_env.gem_platforms).to eq(Gem.platforms)
- expect(Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache["/usr/weird/bin/gem"]).to eq(Gem.platforms)
- end
-
- it "yields to a block while masquerading as a different gems platform" do
- original_platforms = Gem.platforms
- platforms_in_block = nil
- begin
- @gem_env.with_gem_platforms(["ruby", Gem::Platform.new("sparc64-java-1.7")]) do
- platforms_in_block = Gem.platforms
- raise "gem platforms should get set to the correct value even when an error occurs"
- end
- rescue RuntimeError
- end
- expect(platforms_in_block).to eq(["ruby", Gem::Platform.new("sparc64-java-1.7")])
- expect(Gem.platforms).to eq(original_platforms)
- end
-
-end
-
-describe Chef::Provider::Package::Rubygems do
- let(:target_version) { nil }
- let(:gem_name) { "rspec-core" }
- let(:gem_binary) { nil }
- let(:bindir) { "/usr/bin" }
- let(:options) { nil }
- let(:source) { nil }
- let(:include_default_source) { nil }
-
- let(:new_resource) do
- new_resource = Chef::Resource::GemPackage.new(gem_name)
- new_resource.version(target_version)
- new_resource.gem_binary(gem_binary) if gem_binary
- new_resource.options(options) if options
- new_resource.source(source) if source
- new_resource.include_default_source(include_default_source)
- new_resource
- end
-
- let(:current_resource) { nil }
-
- let(:provider) do
- run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
- provider = Chef::Provider::Package::Rubygems.new(new_resource, run_context)
- if current_resource
- allow(provider).to receive(:load_current_resource)
- provider.current_resource = current_resource
- end
- provider
- end
-
- let(:gem_dep) { Gem::Dependency.new(gem_name, target_version) }
-
- before(:each) do
- # We choose detect omnibus via RbConfig::CONFIG['bindir'] in Chef::Provider::Package::Rubygems.new
- allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(bindir)
- # Rubygems uses these two interally
- allow(RbConfig::CONFIG).to receive(:[]).with("arch").and_call_original
- allow(RbConfig::CONFIG).to receive(:[]).with("ruby_install_name").and_call_original
- allow(File).to receive(:executable?).and_return false
- allow(File).to receive(:executable?).with("#{bindir}/gem").and_return true
- # XXX: we can't stub the provider object directly here because referencing it will create it and that
- # will break later tests that want to test the initialize method, so we stub any instance
- # (yet more evidence that initialize methods should be thin and do very little work)
- allow_any_instance_of(Chef::Provider::Package::Rubygems).to receive(:needs_nodocument?).and_return true
- end
-
- describe "when new_resource version is nil" do
- let(:target_version) { nil }
-
- it "target_version_already_installed? should return false so that we can search for candidates" do
- provider.load_current_resource
- expect(provider.target_version_already_installed?(provider.current_resource.version, new_resource.version)).to be_falsey
- end
-
- it "version_equals? should return false so that we can search for candidates" do
- provider.load_current_resource
- expect(provider.version_equals?(provider.current_resource.version, new_resource.version)).to be_falsey
- end
- end
-
- describe "when new_resource version is an rspec version" do
- let(:current_version) { RspecVersionString.rspec_version_string }
- let(:target_version) { current_version }
-
- it "triggers a gem configuration load so a later one will not stomp its config values" do
- _ = provider
- # ugly, is there a better way?
- expect(Gem.instance_variable_get(:@configuration)).not_to be_nil
- end
-
- it "uses the CurrentGemEnvironment implementation when no gem_binary_path is provided" do
- expect(provider.gem_env).to be_a_kind_of(Chef::Provider::Package::Rubygems::CurrentGemEnvironment)
- end
-
- context "when a gem_binary_path is provided" do
- let(:gem_binary) { "/usr/weird/bin/gem" }
-
- it "uses the AlternateGemEnvironment implementation when a gem_binary_path is provided" do
- expect(provider.gem_env.gem_binary_location).to eq(gem_binary)
- end
-
- context "when you try to use a hash of install options" do
- let(:options) { { fail: :burger } }
-
- it "smites you" do
- expect { provider }.to raise_error(ArgumentError)
- end
- end
- end
-
- context "when in omnibus opscode" do
- let(:bindir) { "/opt/opscode/embedded/bin" }
-
- it "recognizes opscode as omnibus" do
- expect(provider.is_omnibus?).to be true
- end
- end
-
- context "when in omnibus chefdk" do
- let(:bindir) { "/opt/chefdk/embedded/bin" }
-
- it "recognizes chefdk as omnibus" do
- expect(provider.is_omnibus?).to be true
- end
- end
-
- context "when in omnibus chef" do
- let(:bindir) { "/opt/chef/embedded/bin" }
-
- it "recognizes chef as omnibus" do
- expect(provider.is_omnibus?).to be true
- end
-
- it "searches for a gem binary when running on Omnibus on Unix" do
- platform_mock :unix do
- allow(ENV).to receive(:[]).with("PATH").and_return("/usr/bin:/usr/sbin:/opt/chef/embedded/bin")
- allow(ENV).to receive(:[]).with("PATHEXT").and_return(nil)
- allow(File).to receive(:executable?).with("/usr/bin/gem").and_return(false)
- allow(File).to receive(:executable?).with("/usr/sbin/gem").and_return(true)
- allow(File).to receive(:executable?).with("/opt/chef/embedded/bin/gem").and_return(true) # should not get here
- expect(provider.gem_env.gem_binary_location).to eq("/usr/sbin/gem")
- end
- end
-
- context "when on Windows" do
- let(:bindir) { "d:/opscode/chef/embedded/bin" }
-
- it "searches for a gem binary when running on Omnibus on Windows" do
- platform_mock :windows do
- allow(ENV).to receive(:[]).with("PATH").and_return('C:\windows\system32;C:\windows;C:\Ruby186\bin')
- allow(ENV).to receive(:[]).with("PATHEXT").and_return(nil)
- allow(File).to receive(:executable?).with('C:\\windows\\system32/gem').and_return(false)
- allow(File).to receive(:executable?).with('C:\\windows/gem').and_return(false)
- allow(File).to receive(:executable?).with('C:\\Ruby186\\bin/gem').and_return(true)
- allow(File).to receive(:executable?).with('d:\\opscode\\chef\\bin/gem').and_return(false) # should not get here
- allow(File).to receive(:executable?).with('d:\\opscode\\chef\\bin/gem').and_return(false) # should not get here
- allow(File).to receive(:executable?).with("d:/opscode/chef/embedded/bin/gem").and_return(false) # should not get here
- expect(provider.gem_env.gem_binary_location).to eq('C:\Ruby186\bin/gem')
- end
- end
- end
- end
-
- it "converts the new resource into a gem dependency" do
- expect(provider.gem_dependency).to eq(gem_dep)
- end
-
- context "when the new resource is not the current version" do
- let(:target_version) { "~> 9000.0.2" }
-
- it "converts the new resource into a gem dependency" do
- expect(provider.gem_dependency).to eq(gem_dep)
- end
- end
-
- describe "when determining the currently installed version" do
- before do
- provider.load_current_resource
- end
-
- it "sets the current version to the version specified by the new resource if that version is installed" do
- expect(provider.current_resource.version).to eq(current_version)
- end
-
- context "if the requested version is not installed" do
- let(:target_version) { "9000.0.2" }
-
- it "sets the current version to the highest installed version if the requested version is not installed" do
- expect(provider.current_resource.version).to eq(current_version)
- end
- end
-
- context "if the package is not currently installed" do
- let(:gem_name) { "no-such-gem-should-exist-with-this-name" }
-
- it "leaves the current version at nil" do
- expect(provider.current_resource.version).to be_nil
- end
- end
-
- end
-
- describe "when determining the candidate version to install" do
- before do
- provider.load_current_resource
- end
-
- context "when the current version is the target version" do
- it "does not query for available versions" do
- # NOTE: odd use case -- we've equality pinned a version, but are calling :upgrade
- expect(provider.gem_env).not_to receive(:candidate_version_from_remote)
- expect(provider.gem_env).not_to receive(:install)
- provider.run_action(:upgrade)
- expect(new_resource).not_to be_updated_by_last_action
- end
- end
-
- context "when the current version satisfies the target version requirement" do
- let(:target_version) { ">= 0" }
-
- it "does not query for available versions on install" do
- expect(provider.gem_env).not_to receive(:candidate_version_from_remote)
- expect(provider.gem_env).not_to receive(:install)
- provider.run_action(:install)
- expect(new_resource).not_to be_updated_by_last_action
- end
-
- it "queries for available versions on upgrade" do
- expect(provider.gem_env).to receive(:candidate_version_from_remote)
- .and_return(Gem::Version.new("9000.0.2"))
- expect(provider.gem_env).to receive(:install)
- provider.run_action(:upgrade)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "when the source is from the rubygems_url" do
- it "determines the candidate version by querying the remote gem servers" do
- Chef::Config[:rubygems_url] = "https://mirror1/"
- expect(provider.gem_env).to receive(:candidate_version_from_remote)
- .with(gem_dep, "https://mirror1/")
- .and_return(Gem::Version.new(target_version))
- expect(provider.candidate_version).to eq(target_version)
- end
- end
-
- context "when the requested source is a remote server" do
- let(:source) { "http://mygems.example.com" }
-
- it "determines the candidate version by querying the remote gem servers" do
- expect(provider.gem_env).to receive(:candidate_version_from_remote)
- .with(gem_dep, source)
- .and_return(Gem::Version.new(target_version))
- expect(provider.candidate_version).to eq(target_version)
- end
-
- it "overwrites the config variable" do
- new_resource.include_default_source false
- Chef::Config[:rubygems_url] = "https://overridden"
- expect(provider.gem_env).to receive(:candidate_version_from_remote)
- .with(gem_dep, source)
- .and_return(Gem::Version.new(target_version))
- expect(provider.candidate_version).to eq(target_version)
- end
- end
-
- context "when the requested source is an array" do
- let(:source) { [ "https://mirror1", "https://mirror2" ] }
-
- it "determines the candidate version by querying the remote gem servers" do
- expect(provider.gem_env).to receive(:candidate_version_from_remote)
- .with(gem_dep, *source)
- .and_return(Gem::Version.new(target_version))
- expect(provider.candidate_version).to eq(target_version)
- end
-
- it "overwrites the config variable" do
- new_resource.include_default_source false
- Chef::Config[:rubygems_url] = "https://overridden"
- expect(provider.gem_env).to receive(:candidate_version_from_remote)
- .with(gem_dep, *source)
- .and_return(Gem::Version.new(target_version))
- expect(provider.candidate_version).to eq(target_version)
- end
- end
-
- context "when the requested source is a file" do
- let(:gem_name) { "chef-integration-test" }
- let(:source) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
- let(:target_version) { ">= 0" }
-
- it "parses the gem's specification" do
- expect(provider.candidate_version).to eq("0.1.0")
- end
- end
- end
-
- describe "when installing a gem" do
- let(:target_version) { "9000.0.2" }
- let(:current_version) { nil }
- let(:candidate_version) { "9000.0.2" }
- let(:current_resource) do
- current_resource = Chef::Resource::GemPackage.new(gem_name)
- current_resource.version(current_version)
- current_resource
- end
-
- let(:version) { Gem::Version.new(candidate_version) }
-
- before do
- expected_source = [ source ]
- expected_source << "https://rubygems.org" if provider.include_default_source?
- allow(provider.gem_env).to receive(:candidate_version_from_remote).with(gem_dep, *expected_source.flatten.compact).and_return(version)
- end
-
- describe "in the current gem environment" do
- it "installs the gem via the gems api when no explicit options are used" do
- expect(provider.gem_env).to receive(:install).with(gem_dep, sources: [ "https://rubygems.org" ])
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
-
- context "when a remote source is provided" do
- let(:source) { "http://gems.example.org" }
-
- it "installs the gem via the gems api" do
- expect(provider.gem_env).to receive(:install).with(gem_dep, sources: [source])
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "when source is a path" do
- let(:source) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
-
- it "installs the gem from file via the gems api" do
- expect(provider.gem_env).to receive(:install).with(source)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "when the gem name is a file path and source is nil" do
- let(:gem_name) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
-
- it "installs the gem from file via the gems api" do
- expect(new_resource.source).to eq(gem_name)
- expect(provider.gem_env).to receive(:install).with(gem_name)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- # this catches 'gem_package "foo"' when "./foo" is a file in the cwd, and instead of installing './foo' it fetches the remote gem
- it "installs the gem via the gems api, when the package has no file separator characters in it, but a matching file exists in cwd" do
- allow(::File).to receive(:exist?).and_return(true)
- new_resource.package_name("rspec-core")
- expect(provider.gem_env).to receive(:install).with(gem_dep, sources: [ "https://rubygems.org" ])
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
-
- context "when options are provided as a String" do
- let(:options) { "-i /alt/install/location" }
-
- it "installs the gem by shelling out when options are provided as a String" do
- expected = "gem install rspec-core -q --no-document -v \"#{target_version}\" --source=https://rubygems.org #{options}"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
-
- it "unmockening needs_nodocument?" do
- expected = "gem install rspec-core -q --no-document -v \"#{target_version}\" --source=https://rubygems.org #{options}"
- expect(provider).to receive(:needs_nodocument?).and_call_original
- stub_const("Gem::VERSION", "3.0.0")
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
-
- it "when the rubygems_version is old it uses the old flags" do
- expected = "gem install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://rubygems.org #{options}"
- expect(provider).to receive(:needs_nodocument?).and_call_original
- stub_const("Gem::VERSION", "2.8.0")
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "when the Chef::Config[:rubygems_url] option is provided" do
- let(:gem_binary) { "/foo/bar" }
-
- it "installs the gem" do
- Chef::Config[:rubygems_url] = "https://mirror1"
- expect(provider.gem_env).to receive(:candidate_version_from_remote).with(gem_dep, "https://mirror1").and_return(version)
- expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=https://mirror1"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "when another source and binary are provided" do
- let(:source) { "http://mirror.ops.rhcloud.com/mirror/ruby" }
- let(:gem_binary) { "/foo/bar" }
-
- it "installs the gem" do
- expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=#{source}"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
-
- context "with include_default_source true" do
- let(:include_default_source) { true }
-
- it "ignores the Chef::Config setting" do
- expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=#{source} --source=https://rubygems.org"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "with include_default_source false" do
- let(:include_default_source) { false }
-
- it "ignores the Chef::Config setting" do
- Chef::Config[:rubygems_url] = "https://ignored"
- expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=#{source}"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
- end
-
- context "when the source is an array" do
- let(:source) { [ "https://mirror1" , "https://mirror2" ] }
- let(:gem_binary) { "/foo/bar" }
-
- it "installs the gem with an array as an added source" do
- expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=https://mirror1 --source=https://mirror2"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
-
- context "with include_default_source true" do
- let(:include_default_source) { true }
-
- it "installs the gem with rubygems as a source" do
- expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=https://mirror1 --source=https://mirror2 --source=https://rubygems.org"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "with include_default_source false" do
- let(:include_default_source) { false }
-
- it "ignores the Chef::Config setting" do
- Chef::Config[:rubygems_url] = "https://ignored"
- expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=https://mirror1 --source=https://mirror2"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
- end
-
- context "when clear_sources is set true and an explicit source is specified" do
- let(:gem_binary) { "/foo/bar" }
- let(:source) { "http://mirror.ops.rhcloud.com/mirror/ruby" }
-
- it "installs the gem" do
- new_resource.clear_sources(true)
- expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=#{source}"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "when clear_sources is set false and an explicit source is specified" do
- let(:gem_binary) { "/foo/bar" }
- let(:source) { "http://mirror.ops.rhcloud.com/mirror/ruby" }
-
- it "installs the gem" do
- new_resource.clear_sources(false)
- expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=#{source}"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "when no version is given" do
- let(:target_version) { nil }
- let(:options) { "-i /alt/install/location" }
-
- it "installs the gem by shelling out when options are provided but no version is given" do
- expected = "gem install rspec-core -q --no-document -v \"#{candidate_version}\" --source=https://rubygems.org #{options}"
- expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "when options are given as a Hash" do
- let(:options) { { install_dir: "/alt/install/location" } }
-
- it "installs the gem via the gems api when options are given as a Hash" do
- expect(provider.gem_env).to receive(:install).with(gem_dep, { sources: [ "https://rubygems.org" ] }.merge(options))
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- describe "at a specific version" do
- let(:target_version) { "9000.0.2" }
-
- it "installs the gem via the gems api" do
- expect(provider.gem_env).to receive(:install).with(gem_dep, sources: [ "https://rubygems.org" ] )
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- describe "at version specified with comparison operator" do
- context "if current version satisfies requested version" do
- let(:target_version) { ">=2.3.0" }
- let(:current_version) { "2.3.3" }
-
- it "skips the install" do
- expect(provider.gem_env).not_to receive(:install)
- provider.run_action(:install)
- end
-
- it "performs the upgrade" do
- expect(provider.gem_env).to receive(:install)
- provider.run_action(:upgrade)
- end
- end
-
- context "if the fuzzy operator is used" do
- let(:target_version) { "~>2.3.0" }
- let(:current_version) { "2.3.3" }
-
- it "it matches an existing gem" do
- expect(provider.gem_env).not_to receive(:install)
- provider.run_action(:install)
- end
-
- it "it upgrades an existing gem" do
- expect(provider.gem_env).to receive(:install)
- provider.run_action(:upgrade)
- end
- end
- end
- end
-
- describe "in an alternate gem environment" do
- let(:gem_binary) { "/usr/weird/bin/gem" }
-
- it "installs the gem by shelling out to gem install" do
- expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=https://rubygems.org", env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
-
- it "unmockening needs_nodocument?" do
- expect(provider).to receive(:needs_nodocument?).and_call_original
- expect(provider.gem_env).to receive(:shell_out!).with("#{gem_binary} --version").and_return(instance_double(Mixlib::ShellOut, stdout: "3.0.0\n"))
- expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=https://rubygems.org", env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
-
- it "when the rubygems_version is old it uses the old flags" do
- expect(provider).to receive(:needs_nodocument?).and_call_original
- expect(provider.gem_env).to receive(:shell_out!).with("#{gem_binary} --version").and_return(instance_double(Mixlib::ShellOut, stdout: "2.8.0\n"))
- expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://rubygems.org", env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
-
- context "when source is a path" do
- let(:source) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
- let(:target_version) { ">= 0" }
-
- it "installs the gem by shelling out to gem install" do
- expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install #{source} -q --no-document -v \"#{target_version}\"", env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
-
- context "when the package is a path and source is nil" do
- let(:gem_name) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
- let(:target_version) { ">= 0" }
-
- it "installs the gem from file by shelling out to gem install when the package is a path and the source is nil" do
- expect(new_resource.source).to eq(gem_name)
- expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install #{gem_name} -q --no-document -v \"#{target_version}\"", env: nil, timeout: 900)
- provider.run_action(:install)
- expect(new_resource).to be_updated_by_last_action
- end
- end
- end
-
- end
-
- describe "when uninstalling a gem" do
- let(:gem_name) { "rspec" }
- let(:current_version) { "1.2.3" }
- let(:target_version) { nil }
-
- let(:current_resource) do
- current_resource = Chef::Resource::GemPackage.new(gem_name)
- current_resource.version(current_version)
- current_resource
- end
-
- describe "in the current gem environment" do
- it "uninstalls via the api when no explicit options are used" do
- # pre-reqs for action_remove to actually remove the package:
- expect(provider.new_resource.version).to be_nil
- expect(provider.current_resource.version).not_to be_nil
- # the behavior we're testing:
- expect(provider.gem_env).to receive(:uninstall).with("rspec", nil)
- provider.action_remove
- end
-
- context "when options are given as a Hash" do
- let(:options) { { install_dir: "/alt/install/location" } }
-
- it "uninstalls via the api" do
- # pre-reqs for action_remove to actually remove the package:
- expect(provider.new_resource.version).to be_nil
- expect(provider.current_resource.version).not_to be_nil
- # the behavior we're testing:
- expect(provider.gem_env).to receive(:uninstall).with("rspec", nil, options)
- provider.action_remove
- end
- end
-
- context "when options are given as a String" do
- let(:options) { "-i /alt/install/location" }
-
- it "uninstalls via the gem command" do
- expect(provider).to receive(:shell_out_compacted!).with("gem uninstall rspec -q -x -I -a #{options}", env: nil, timeout: 900)
- provider.action_remove
- end
- end
-
- context "when a version is provided" do
- let(:target_version) { "1.2.3" }
-
- it "uninstalls a specific version of a gem" do
- expect(provider.gem_env).to receive(:uninstall).with("rspec", "1.2.3")
- provider.action_remove
- end
- end
- end
-
- describe "in an alternate gem environment" do
- let(:gem_binary) { "/usr/weird/bin/gem" }
-
- it "uninstalls via the gem command" do
- expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} uninstall rspec -q -x -I -a", env: nil, timeout: 900)
- provider.action_remove
- end
- end
- end
- end
-end
-
-describe Chef::Provider::Package::Rubygems, "clear_sources?" do
- let(:new_resource) do
- Chef::Resource::GemPackage.new("foo")
- end
-
- let(:provider) do
- run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
- Chef::Provider::Package::Rubygems.new(new_resource, run_context)
- end
-
- it "is false when clear_sources is unset" do
- expect(provider.clear_sources?).to be false
- end
-
- it "is false when clear_sources is set false" do
- new_resource.clear_sources(false)
- expect(provider.clear_sources?).to be false
- end
-
- it "is true when clear_sources is set true" do
- new_resource.clear_sources(true)
- expect(provider.clear_sources?).to be true
- end
-
- context "when a source is set" do
- before do
- new_resource.source("http://mirror.ops.rhcloud.com/mirror/ruby")
- end
-
- it "is true when clear_sources is unset" do
- expect(provider.clear_sources?).to be true
- end
-
- it "is false when clear_sources is set false" do
- new_resource.clear_sources(false)
- expect(provider.clear_sources?).to be false
- end
-
- it "is true when clear_sources is set true" do
- new_resource.clear_sources(true)
- expect(provider.clear_sources?).to be true
- end
- end
-
- context "when Chef::Config[:rubygems_url] is set" do
- before do
- Chef::Config.rubygems_url = "https://example.com/"
- end
-
- it "is true when clear_sources is unset" do
- expect(provider.clear_sources?).to be true
- end
-
- it "is false when clear_sources is set false" do
- new_resource.clear_sources(false)
- expect(provider.clear_sources?).to be false
- end
-
- it "is true when clear_sources is set true" do
- new_resource.clear_sources(true)
- expect(provider.clear_sources?).to be true
- end
- end
-end
-
-describe Chef::Provider::Package::Rubygems, "include_default_source?" do
- let(:new_resource) do
- Chef::Resource::GemPackage.new("foo")
- end
-
- let(:provider) do
- run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
- Chef::Provider::Package::Rubygems.new(new_resource, run_context)
- end
-
- it "is true when include_default_source is unset" do
- expect(provider.include_default_source?).to be true
- end
-
- it "is false when include_default_source is set false" do
- new_resource.include_default_source(false)
- expect(provider.include_default_source?).to be false
- end
-
- it "is true when include_default_source is set true" do
- new_resource.include_default_source(true)
- expect(provider.include_default_source?).to be true
- end
-
- context "when a source is set" do
- before do
- new_resource.source("http://mirror.ops.rhcloud.com/mirror/ruby")
- end
-
- it "is false when include_default_source is unset" do
- expect(provider.include_default_source?).to be false
- end
-
- it "is false when include_default_source is set false" do
- new_resource.include_default_source(false)
- expect(provider.include_default_source?).to be false
- end
-
- it "is true when include_default_source is set true" do
- new_resource.include_default_source(true)
- expect(provider.include_default_source?).to be true
- end
- end
-
- context "when Chef::Config[:rubygems_url] is set" do
- before do
- Chef::Config.rubygems_url = "https://example.com/"
- end
-
- it "is true when include_default_source is unset" do
- expect(provider.include_default_source?).to be true
- end
-
- it "is false when include_default_source is set false" do
- new_resource.include_default_source(false)
- expect(provider.include_default_source?).to be false
- end
-
- it "is true when include_default_source is set true" do
- new_resource.include_default_source(true)
- expect(provider.include_default_source?).to be true
- end
- end
-
- context "when clear_sources is set" do
- before do
- new_resource.clear_sources(true)
- end
-
- it "is false when include_default_source is unset" do
- expect(provider.include_default_source?).to be false
- end
-
- it "is false when include_default_source is set false" do
- new_resource.include_default_source(false)
- expect(provider.include_default_source?).to be false
- end
-
- it "is true when include_default_source is set true" do
- new_resource.include_default_source(true)
- expect(provider.include_default_source?).to be true
- end
- end
end
diff --git a/tasks/rspec.rb b/tasks/rspec.rb
index 929e0f91b0..1d41914158 100644
--- a/tasks/rspec.rb
+++ b/tasks/rspec.rb
@@ -37,6 +37,20 @@ begin
task default: :spec
+
+ desc "Mini"
+ RSpec::Core::RakeTask.new(:mini) do |t|
+ t.verbose = true
+ t.rspec_opts= "--format doc"
+ t.pattern =
+ FileList["spec/unit/cookbook/gem_installer_spec.rb"] +
+ FileList["spec/unit/mixin/shell_out_spec.rb"] +
+ FileList["spec/unit/provider/package/rubygems_spec.rb"] +
+ # Does not HAVE To be the file, but I've already pared it down
+ # and commented some test variations that surface/hide the failure
+ FileList["spec/unit/knife/supermarket_share_spec.rb"]
+ end
+
task spec: :component_specs
desc "Run all specs in spec directory"