summaryrefslogtreecommitdiff
path: root/spec/unit
diff options
context:
space:
mode:
Diffstat (limited to 'spec/unit')
-rw-r--r--spec/unit/api_client_spec.rb46
-rw-r--r--spec/unit/application/client_spec.rb49
-rw-r--r--spec/unit/client_spec.rb575
-rw-r--r--spec/unit/cookbook/metadata_spec.rb46
-rw-r--r--spec/unit/cookbook_spec.rb10
-rw-r--r--spec/unit/dsl/reboot_pending_spec.rb100
-rw-r--r--spec/unit/knife/client_bulk_delete_spec.rb159
-rw-r--r--spec/unit/knife/client_create_spec.rb30
-rw-r--r--spec/unit/knife/client_delete_spec.rb45
-rw-r--r--spec/unit/knife/core/bootstrap_context_spec.rb5
-rw-r--r--spec/unit/knife/core/ui_spec.rb9
-rw-r--r--spec/unit/knife/ssh_spec.rb23
-rw-r--r--spec/unit/node/attribute_spec.rb7
-rw-r--r--spec/unit/node/immutable_collections_spec.rb6
-rw-r--r--spec/unit/platform_spec.rb3
-rw-r--r--spec/unit/provider/group_spec.rb5
-rw-r--r--spec/unit/provider/ifconfig/debian_spec.rb275
-rw-r--r--spec/unit/provider/ohai_spec.rb5
-rw-r--r--spec/unit/provider/service/macosx_spec.rb40
-rw-r--r--spec/unit/recipe_spec.rb4
-rw-r--r--spec/unit/run_context/cookbook_compiler_spec.rb12
-rw-r--r--spec/unit/run_context_spec.rb7
22 files changed, 1132 insertions, 329 deletions
diff --git a/spec/unit/api_client_spec.rb b/spec/unit/api_client_spec.rb
index 4ccd64bafe..8657fa59a8 100644
--- a/spec/unit/api_client_spec.rb
+++ b/spec/unit/api_client_spec.rb
@@ -164,6 +164,52 @@ describe Chef::ApiClient do
end
+ describe "when loading from JSON" do
+ before do
+ end
+
+ before(:each) do
+ client = {
+ "name" => "black",
+ "clientname" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "validator" => true,
+ "json_class" => "Chef::ApiClient"
+ }
+ @http_client = double("Chef::REST mock")
+ Chef::REST.stub(:new).and_return(@http_client)
+ @http_client.should_receive(:get).with("clients/black").and_return(client)
+ @client = Chef::ApiClient.load(client['name'])
+ end
+
+ it "should deserialize to a Chef::ApiClient object" do
+ @client.should be_a_kind_of(Chef::ApiClient)
+ end
+
+ it "preserves the name" do
+ @client.name.should == "black"
+ end
+
+ it "preserves the public key" do
+ @client.public_key.should == "crowes"
+ end
+
+ it "preserves the admin status" do
+ @client.admin.should be_a_kind_of(Chef::TrueClass)
+ end
+
+ it "preserves the 'validator' status" do
+ @client.validator.should be_a_kind_of(Chef::TrueClass)
+ end
+
+ it "includes the private key if present" do
+ @client.private_key.should == "monkeypants"
+ end
+
+ end
+
describe "with correctly configured API credentials" do
before do
Chef::Config[:node_name] = "silent-bob"
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index f84932073f..8b4ea6a077 100644
--- a/spec/unit/application/client_spec.rb
+++ b/spec/unit/application/client_spec.rb
@@ -127,7 +127,7 @@ describe Chef::Application::Client, "configure_chef" do
end
describe Chef::Application::Client, "run_application", :unix_only do
- before do
+ before(:each) do
@pipe = IO.pipe
@app = Chef::Application::Client.new
@app.stub(:run_chef_client) do
@@ -147,4 +147,51 @@ describe Chef::Application::Client, "run_application", :unix_only do
IO.select([@pipe[0]], nil, nil, 0).should_not be_nil
@pipe[0].gets.should == "finished\n"
end
+
+ describe "when splay is set" do
+ before do
+ Chef::Config[:splay] = 10
+ Chef::Config[:interval] = 10
+
+ run_count = 0
+
+ # uncomment to debug failures...
+ # Chef::Log.init($stderr)
+ # Chef::Log.level = :debug
+
+ @app.stub(:run_chef_client) do
+
+ run_count += 1
+ if run_count > 3
+ exit 0
+ end
+
+ # If everything is fine, sending USR1 to self should prevent
+ # app to go into splay sleep forever.
+ Process.kill("USR1", Process.pid)
+ end
+
+ number_of_sleep_calls = 0
+
+ # This is a very complicated way of writing
+ # @app.should_receive(:sleep).once.
+ # We have to do it this way because the main loop of
+ # Chef::Application::Client swallows most exceptions, and we need to be
+ # able to expose our expectation failures to the parent process in the test.
+ @app.stub(:sleep) do |arg|
+ number_of_sleep_calls += 1
+ if number_of_sleep_calls > 1
+ exit 127
+ end
+ end
+ end
+
+ it "shouldn't sleep when sent USR1" do
+ pid = fork do
+ @app.run_application
+ end
+ _pid, result = Process.waitpid2(pid)
+ result.exitstatus.should == 0
+ end
+ end
end
diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb
index bd80d39237..9688cce2f4 100644
--- a/spec/unit/client_spec.rb
+++ b/spec/unit/client_spec.rb
@@ -24,29 +24,54 @@ require 'chef/run_context'
require 'chef/rest'
require 'rbconfig'
-shared_examples_for Chef::Client do
+describe Chef::Client do
+
+ let(:hostname) { "hostname" }
+ let(:machinename) { "machinename.example.org" }
+ let(:fqdn) { "hostname.example.org" }
+
+ let(:ohai_data) do
+ { :fqdn => fqdn,
+ :hostname => hostname,
+ :machinename => machinename,
+ :platform => 'example-platform',
+ :platform_version => 'example-platform-1.0',
+ :data => {}
+ }
+ end
+
+ let(:ohai_system) do
+ ohai_system = double( "Ohai::System",
+ :all_plugins => true,
+ :data => ohai_data)
+ ohai_system.stub(:[]) do |key|
+ ohai_data[key]
+ end
+ ohai_system
+ end
+
+ let(:node) do
+ Chef::Node.new.tap do |n|
+ n.name(fqdn)
+ n.chef_environment("_default")
+ end
+ end
+
+ let(:json_attribs) { nil }
+ let(:client_opts) { {} }
+
+ let(:client) do
+ Chef::Client.new(json_attribs, client_opts).tap do |c|
+ c.node = node
+ end
+ end
+
before do
Chef::Log.logger = Logger.new(StringIO.new)
# Node/Ohai data
- @hostname = "hostname"
- @fqdn = "hostname.example.org"
- Chef::Config[:node_name] = @fqdn
- ohai_data = { :fqdn => @fqdn,
- :hostname => @hostname,
- :platform => 'example-platform',
- :platform_version => 'example-platform-1.0',
- :data => {} }
- ohai_data.stub(:all_plugins).and_return(true)
- ohai_data.stub(:data).and_return(ohai_data)
- Ohai::System.stub(:new).and_return(ohai_data)
-
- @node = Chef::Node.new
- @node.name(@fqdn)
- @node.chef_environment("_default")
-
- @client = Chef::Client.new
- @client.node = @node
+ #Chef::Config[:node_name] = fqdn
+ Ohai::System.stub(:new).and_return(ohai_system)
end
describe "authentication protocol selection" do
@@ -58,7 +83,7 @@ shared_examples_for Chef::Client do
it "does not force the authentication protocol to 1.1" do
Chef::Config[:node_name] = ("f" * 90)
# ugly that this happens as a side effect of a getter :(
- @client.node_name
+ client.node_name
Chef::Config[:authentication_protocol_version].should == "1.0"
end
end
@@ -67,7 +92,7 @@ shared_examples_for Chef::Client do
it "sets the authentication protocol to version 1.1" do
Chef::Config[:node_name] = ("f" * 91)
# ugly that this happens as a side effect of a getter :(
- @client.node_name
+ client.node_name
Chef::Config[:authentication_protocol_version].should == "1.1"
end
end
@@ -75,9 +100,6 @@ shared_examples_for Chef::Client do
describe "configuring output formatters" do
context "when no formatter has been configured" do
- before do
- @client = Chef::Client.new
- end
context "and STDOUT is a TTY" do
before do
@@ -85,7 +107,7 @@ shared_examples_for Chef::Client do
end
it "configures the :doc formatter" do
- @client.formatters_for_run.should == [[:doc]]
+ client.formatters_for_run.should == [[:doc]]
end
context "and force_logger is set" do
@@ -95,7 +117,7 @@ shared_examples_for Chef::Client do
it "configures the :null formatter" do
Chef::Config[:force_logger].should be_true
- @client.formatters_for_run.should == [[:null]]
+ client.formatters_for_run.should == [[:null]]
end
end
@@ -108,7 +130,7 @@ shared_examples_for Chef::Client do
end
it "configures the :null formatter" do
- @client.formatters_for_run.should == [[:null]]
+ client.formatters_for_run.should == [[:null]]
end
context "and force_formatter is set" do
@@ -116,7 +138,7 @@ shared_examples_for Chef::Client do
Chef::Config[:force_formatter] = true
end
it "it configures the :doc formatter" do
- @client.formatters_for_run.should == [[:doc]]
+ client.formatters_for_run.should == [[:doc]]
end
end
end
@@ -126,16 +148,15 @@ shared_examples_for Chef::Client do
context "when a formatter is configured" do
context "with no output path" do
before do
- @client = Chef::Client.new
Chef::Config.add_formatter(:min)
end
it "does not configure a default formatter" do
- @client.formatters_for_run.should == [[:min, nil]]
+ client.formatters_for_run.should == [[:min, nil]]
end
it "configures the formatter for STDOUT/STDERR" do
- configured_formatters = @client.configure_formatters
+ configured_formatters = client.configure_formatters
min_formatter = configured_formatters[0]
min_formatter.output.out.should == STDOUT
min_formatter.output.err.should == STDERR
@@ -144,7 +165,6 @@ shared_examples_for Chef::Client do
context "with an output path" do
before do
- @client = Chef::Client.new
@tmpout = Tempfile.open("rspec-for-client-formatter-selection-#{Process.pid}")
Chef::Config.add_formatter(:min, @tmpout.path)
end
@@ -155,7 +175,7 @@ shared_examples_for Chef::Client do
end
it "configures the formatter for the file path" do
- configured_formatters = @client.configure_formatters
+ configured_formatters = client.configure_formatters
min_formatter = configured_formatters[0]
min_formatter.output.out.path.should == @tmpout.path
min_formatter.output.err.path.should == @tmpout.path
@@ -165,94 +185,216 @@ shared_examples_for Chef::Client do
end
end
- describe "run" do
-
- it "should identify the node and run ohai, then register the client" do
- mock_chef_rest_for_node = double("Chef::REST (node)")
- mock_chef_rest_for_cookbook_sync = double("Chef::REST (cookbook sync)")
- mock_chef_rest_for_node_save = double("Chef::REST (node save)")
- mock_chef_runner = double("Chef::Runner")
-
- # --Client.register
- # Make sure Client#register thinks the client key doesn't
- # exist, so it tries to register and create one.
- File.should_receive(:exists?).with(Chef::Config[:client_key]).exactly(1).times.and_return(false)
-
- # Client.register will register with the validation client name.
- Chef::ApiClient::Registration.any_instance.should_receive(:run)
- # Client.register will then turn around create another
- # Chef::REST object, this time with the client key it got from the
- # previous step.
- Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url], @fqdn, Chef::Config[:client_key]
- ).exactly(1).and_return(mock_chef_rest_for_node)
-
- # --Client#build_node
- # looks up the node, which we will return, then later saves it.
- Chef::Node.should_receive(:find_or_create).with(@fqdn).and_return(@node)
-
- # --ResourceReporter#node_load_completed
- # gets a run id from the server for storing resource history
- # (has its own tests, so stubbing it here.)
- Chef::ResourceReporter.any_instance.should_receive(:node_load_completed)
-
- # --ResourceReporter#run_completed
- # updates the server with the resource history
- # (has its own tests, so stubbing it here.)
- Chef::ResourceReporter.any_instance.should_receive(:run_completed)
- # --Client#setup_run_context
- # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
- #
- Chef::CookbookSynchronizer.any_instance.should_receive(:sync_cookbooks)
- Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(mock_chef_rest_for_cookbook_sync)
- mock_chef_rest_for_cookbook_sync.should_receive(:post).with("environments/_default/cookbook_versions", {:run_list => []}).and_return({})
-
- # --Client#converge
- Chef::Runner.should_receive(:new).and_return(mock_chef_runner)
- mock_chef_runner.should_receive(:converge).and_return(true)
-
- # --Client#save_updated_node
- Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(mock_chef_rest_for_node_save)
- mock_chef_rest_for_node_save.should_receive(:put_rest).with("nodes/#{@fqdn}", @node).and_return(true)
-
- Chef::RunLock.any_instance.should_receive(:acquire)
- Chef::RunLock.any_instance.should_receive(:save_pid)
- Chef::RunLock.any_instance.should_receive(:release)
-
- # Post conditions: check that node has been filled in correctly
- @client.should_receive(:run_started)
- @client.should_receive(:run_completed_successfully)
+ describe "a full client run" do
+ shared_examples_for "a successful client run" do
+ let(:http_node_load) { double("Chef::REST (node)") }
+ let(:http_cookbook_sync) { double("Chef::REST (cookbook sync)") }
+ let(:http_node_save) { double("Chef::REST (node save)") }
+ let(:runner) { double("Chef::Runner") }
- if(Chef::Config[:client_fork] && !windows?)
- require 'stringio'
- if(Chef::Config[:pipe_node])
- pipe_sim = StringIO.new
- pipe_sim.should_receive(:close).exactly(4).and_return(nil)
- res = ''
- pipe_sim.should_receive(:puts) do |string|
- res.replace(string)
- end
- pipe_sim.should_receive(:gets).and_return(res)
- IO.should_receive(:pipe).and_return([pipe_sim, pipe_sim])
- IO.should_receive(:select).and_return(true)
+ let(:api_client_exists?) { false }
+
+ let(:stdout) { StringIO.new }
+ let(:stderr) { StringIO.new }
+
+ let(:enable_fork) { false }
+
+ def stub_for_register
+ # --Client.register
+ # Make sure Client#register thinks the client key doesn't
+ # exist, so it tries to register and create one.
+ File.should_receive(:exists?).with(Chef::Config[:client_key]).exactly(1).times.and_return(api_client_exists?)
+
+ unless api_client_exists?
+ # Client.register will register with the validation client name.
+ Chef::ApiClient::Registration.any_instance.should_receive(:run)
end
- proc_ret = Class.new.new
- proc_ret.should_receive(:success?).and_return(true)
- Process.should_receive(:waitpid2).and_return([1, proc_ret])
- @client.should_receive(:exit).and_return(nil)
- @client.should_receive(:fork) do |&block|
- block.call
+ end
+
+ def stub_for_node_load
+ # Client.register will then turn around create another
+ # Chef::REST object, this time with the client key it got from the
+ # previous step.
+ Chef::REST.should_receive(:new).
+ with(Chef::Config[:chef_server_url], fqdn, Chef::Config[:client_key]).
+ exactly(1).
+ and_return(http_node_load)
+
+ # --Client#build_node
+ # looks up the node, which we will return, then later saves it.
+ Chef::Node.should_receive(:find_or_create).with(fqdn).and_return(node)
+
+ # --ResourceReporter#node_load_completed
+ # gets a run id from the server for storing resource history
+ # (has its own tests, so stubbing it here.)
+ Chef::ResourceReporter.any_instance.should_receive(:node_load_completed)
+ end
+
+ def stub_for_sync_cookbooks
+ # --Client#setup_run_context
+ # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
+ #
+ Chef::CookbookSynchronizer.any_instance.should_receive(:sync_cookbooks)
+ Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
+ http_cookbook_sync.should_receive(:post).
+ with("environments/_default/cookbook_versions", {:run_list => []}).
+ and_return({})
+ end
+
+ def stub_for_converge
+ # --Client#converge
+ Chef::Runner.should_receive(:new).and_return(runner)
+ runner.should_receive(:converge).and_return(true)
+
+ # --ResourceReporter#run_completed
+ # updates the server with the resource history
+ # (has its own tests, so stubbing it here.)
+ Chef::ResourceReporter.any_instance.should_receive(:run_completed)
+ end
+
+ def stub_for_node_save
+ # --Client#save_updated_node
+ Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_node_save)
+ http_node_save.should_receive(:put_rest).with("nodes/#{fqdn}", node).and_return(true)
+ end
+
+ def stub_for_run
+ Chef::RunLock.any_instance.should_receive(:acquire)
+ Chef::RunLock.any_instance.should_receive(:save_pid)
+ Chef::RunLock.any_instance.should_receive(:release)
+
+ # Post conditions: check that node has been filled in correctly
+ client.should_receive(:run_started)
+ client.should_receive(:run_completed_successfully)
+ end
+
+ before do
+ Chef::Config[:client_fork] = enable_fork
+
+ stub_const("Chef::Client::STDOUT_FD", stdout)
+ stub_const("Chef::Client::STDERR_FD", stderr)
+
+ stub_for_register
+ stub_for_node_load
+ stub_for_sync_cookbooks
+ stub_for_converge
+ stub_for_node_save
+ stub_for_run
+ end
+
+ it "runs ohai, sets up authentication, loads node state, synchronizes policy, and converges" do
+ # This is what we're testing.
+ client.run
+
+ # fork is stubbed, so we can see the outcome of the run
+ node.automatic_attrs[:platform].should == "example-platform"
+ node.automatic_attrs[:platform_version].should == "example-platform-1.0"
+ end
+ end
+
+
+ describe "when running chef-client without fork" do
+
+ include_examples "a successful client run"
+ end
+
+ describe "when running chef-client with forking enabled", :unix_only do
+ include_examples "a successful client run" do
+ let(:process_status) do
+ double("Process::Status")
+ end
+
+ let(:enable_fork) { true }
+
+ before do
+ Process.should_receive(:waitpid2).and_return([1, process_status])
+
+ process_status.should_receive(:success?).and_return(true)
+ client.should_receive(:exit).and_return(nil)
+ client.should_receive(:fork).and_yield
end
end
- # This is what we're testing.
- @client.run
+ end
+
+ describe "when the client key already exists" do
+
+ let(:api_client_exists?) { true }
+
+ include_examples "a successful client run"
+ end
+
+ describe "when an override run list is given" do
+ let(:client_opts) { {:override_runlist => "recipe[override_recipe]"} }
- if(!Chef::Config[:client_fork] || Chef::Config[:pipe_node])
- @node.automatic_attrs[:platform].should == "example-platform"
- @node.automatic_attrs[:platform_version].should == "example-platform-1.0"
+ it "should permit spaces in overriding run list" do
+ Chef::Client.new(nil, :override_runlist => 'role[a], role[b]')
+ end
+
+ describe "when running the client" do
+ include_examples "a successful client run" do
+
+ before do
+ # Client will try to compile and run override_recipe
+ Chef::RunContext::CookbookCompiler.any_instance.should_receive(:compile)
+ end
+
+ def stub_for_sync_cookbooks
+ # --Client#setup_run_context
+ # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
+ #
+ Chef::CookbookSynchronizer.any_instance.should_receive(:sync_cookbooks)
+ Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
+ http_cookbook_sync.should_receive(:post).
+ with("environments/_default/cookbook_versions", {:run_list => ["override_recipe"]}).
+ and_return({})
+ end
+
+ def stub_for_node_save
+ # Expect NO node save
+ node.should_not_receive(:save)
+ end
+ end
end
end
+ describe "when a permanent run list is passed as an option" do
+
+ include_examples "a successful client run" do
+
+ let(:new_runlist) { "recipe[new_run_list_recipe]" }
+ let(:client_opts) { {:runlist => new_runlist} }
+
+ def stub_for_sync_cookbooks
+ # --Client#setup_run_context
+ # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
+ #
+ Chef::CookbookSynchronizer.any_instance.should_receive(:sync_cookbooks)
+ Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
+ http_cookbook_sync.should_receive(:post).
+ with("environments/_default/cookbook_versions", {:run_list => ["new_run_list_recipe"]}).
+ and_return({})
+ end
+
+ before do
+ # Client will try to compile and run the new_run_list_recipe, but we
+ # do not create a fixture for this.
+ Chef::RunContext::CookbookCompiler.any_instance.should_receive(:compile)
+ end
+
+ it "sets the new run list on the node" do
+ client.run
+ node.run_list.should == Chef::RunList.new(new_runlist)
+ end
+
+ end
+ end
+
+ end
+
+
+ describe "when handling run failures" do
+
it "should remove the run_lock on failure of #load_node" do
@run_lock = double("Chef::RunLock", :acquire => true)
Chef::RunLock.stub(:new).and_return(@run_lock)
@@ -261,64 +403,64 @@ shared_examples_for Chef::Client do
Chef::EventDispatch::Dispatcher.stub(:new).and_return(@events)
# @events is created on Chef::Client.new, so we need to recreate it after mocking
- @client = Chef::Client.new
- @client.stub(:load_node).and_raise(Exception)
+ client = Chef::Client.new
+ client.stub(:load_node).and_raise(Exception)
@run_lock.should_receive(:release)
if(Chef::Config[:client_fork] && !windows?)
- @client.should_receive(:fork) do |&block|
+ client.should_receive(:fork) do |&block|
block.call
end
end
- lambda { @client.run }.should raise_error(Exception)
+ lambda { client.run }.should raise_error(Exception)
end
+ end
- describe "when notifying other objects of the status of the chef run" do
- before do
- Chef::Client.clear_notifications
- Chef::Node.stub(:find_or_create).and_return(@node)
- @node.stub(:save)
- @client.load_node
- @client.build_node
- end
-
- it "notifies observers that the run has started" do
- notified = false
- Chef::Client.when_run_starts do |run_status|
- run_status.node.should == @node
- notified = true
- end
+ describe "when notifying other objects of the status of the chef run" do
+ before do
+ Chef::Client.clear_notifications
+ Chef::Node.stub(:find_or_create).and_return(node)
+ node.stub(:save)
+ client.load_node
+ client.build_node
+ end
- @client.run_started
- notified.should be_true
+ it "notifies observers that the run has started" do
+ notified = false
+ Chef::Client.when_run_starts do |run_status|
+ run_status.node.should == node
+ notified = true
end
- it "notifies observers that the run has completed successfully" do
- notified = false
- Chef::Client.when_run_completes_successfully do |run_status|
- run_status.node.should == @node
- notified = true
- end
+ client.run_started
+ notified.should be_true
+ end
- @client.run_completed_successfully
- notified.should be_true
+ it "notifies observers that the run has completed successfully" do
+ notified = false
+ Chef::Client.when_run_completes_successfully do |run_status|
+ run_status.node.should == node
+ notified = true
end
- it "notifies observers that the run failed" do
- notified = false
- Chef::Client.when_run_fails do |run_status|
- run_status.node.should == @node
- notified = true
- end
+ client.run_completed_successfully
+ notified.should be_true
+ end
- @client.run_failed
- notified.should be_true
+ it "notifies observers that the run failed" do
+ notified = false
+ Chef::Client.when_run_fails do |run_status|
+ run_status.node.should == node
+ notified = true
end
+
+ client.run_failed
+ notified.should be_true
end
end
describe "build_node" do
it "should expand the roles and recipes for the node" do
- @node.run_list << "role[role_containing_cookbook1]"
+ node.run_list << "role[role_containing_cookbook1]"
role_containing_cookbook1 = Chef::Role.new
role_containing_cookbook1.name("role_containing_cookbook1")
role_containing_cookbook1.run_list << "cookbook1"
@@ -330,37 +472,33 @@ shared_examples_for Chef::Client do
Chef::REST.should_receive(:new).and_return(mock_chef_rest)
# check pre-conditions.
- @node[:roles].should be_nil
- @node[:recipes].should be_nil
+ node[:roles].should be_nil
+ node[:recipes].should be_nil
- @client.policy_builder.stub(:node).and_return(@node)
+ client.policy_builder.stub(:node).and_return(node)
# chefspec and possibly others use the return value of this method
- @client.build_node.should == @node
+ client.build_node.should == node
# check post-conditions.
- @node[:roles].should_not be_nil
- @node[:roles].length.should == 1
- @node[:roles].should include("role_containing_cookbook1")
- @node[:recipes].should_not be_nil
- @node[:recipes].length.should == 1
- @node[:recipes].should include("cookbook1")
+ node[:roles].should_not be_nil
+ node[:roles].length.should == 1
+ node[:roles].should include("role_containing_cookbook1")
+ node[:recipes].should_not be_nil
+ node[:recipes].length.should == 1
+ node[:recipes].should include("cookbook1")
end
end
describe "windows_admin_check" do
- before do
- @client = Chef::Client.new
- end
-
context "platform is not windows" do
before do
Chef::Platform.stub(:windows?).and_return(false)
end
it "shouldn't be called" do
- @client.should_not_receive(:has_admin_privileges?)
- @client.do_windows_admin_check
+ client.should_not_receive(:has_admin_privileges?)
+ client.do_windows_admin_check
end
end
@@ -370,91 +508,46 @@ shared_examples_for Chef::Client do
end
it "should be called" do
- @client.should_receive(:has_admin_privileges?)
- @client.do_windows_admin_check
+ client.should_receive(:has_admin_privileges?)
+ client.do_windows_admin_check
end
context "admin privileges exist" do
before do
- @client.should_receive(:has_admin_privileges?).and_return(true)
+ client.should_receive(:has_admin_privileges?).and_return(true)
end
it "should not log a warning message" do
Chef::Log.should_not_receive(:warn)
- @client.do_windows_admin_check
+ client.do_windows_admin_check
end
context "fatal admin check is configured" do
it "should not raise an exception" do
- @client.do_windows_admin_check.should_not raise_error
+ client.do_windows_admin_check #should not raise
end
end
end
context "admin privileges doesn't exist" do
before do
- @client.should_receive(:has_admin_privileges?).and_return(false)
+ client.should_receive(:has_admin_privileges?).and_return(false)
end
it "should log a warning message" do
Chef::Log.should_receive(:warn)
- @client.do_windows_admin_check
+ client.do_windows_admin_check
end
context "fatal admin check is configured" do
it "should raise an exception" do
- @client.do_windows_admin_check.should_not raise_error
+ client.do_windows_admin_check # should not raise
end
end
end
end
end
- describe "when a run list override is provided" do
- before do
- @node = Chef::Node.new
- @node.name(@fqdn)
- @node.chef_environment("_default")
- @node.automatic_attrs[:platform] = "example-platform"
- @node.automatic_attrs[:platform_version] = "example-platform-1.0"
- end
-
- it "should permit spaces in overriding run list" do
- @client = Chef::Client.new(nil, :override_runlist => 'role[a], role[b]')
- end
-
- it "should override the run list and skip the final node save" do
- @client = Chef::Client.new(nil, :override_runlist => 'role[test_role]')
- @client.node = @node
-
- @node.run_list << "role[role_containing_cookbook1]"
-
- override_role = Chef::Role.new
- override_role.name 'test_role'
- override_role.run_list << 'cookbook1'
-
- original_runlist = @node.run_list.dup
-
- mock_chef_rest = double("Chef::REST")
- mock_chef_rest.should_receive(:get_rest).with("roles/test_role").and_return(override_role)
- Chef::REST.should_receive(:new).and_return(mock_chef_rest)
-
- @node.should_not_receive(:save)
-
- @client.policy_builder.stub(:node).and_return(@node)
- @client.policy_builder.build_node
-
- @node[:roles].should_not be_nil
- @node[:roles].should eql(['test_role'])
- @node[:recipes].should eql(['cookbook1'])
-
- @client.save_updated_node
-
- @node.run_list.should == original_runlist
-
- end
- end
-
describe "assert_cookbook_path_not_empty" do
before do
Chef::Config[:solo] = true
@@ -463,24 +556,46 @@ shared_examples_for Chef::Client do
context "when any directory of cookbook_path contains no cookbook" do
it "raises CookbookNotFound error" do
expect do
- @client.send(:assert_cookbook_path_not_empty, nil)
+ client.send(:assert_cookbook_path_not_empty, nil)
end.to raise_error(Chef::Exceptions::CookbookNotFound, 'None of the cookbook paths set in Chef::Config[:cookbook_path], ["/path/to/invalid/cookbook_path"], contain any cookbooks')
end
end
end
-end
+ describe "setting node name" do
+ context "when machinename, hostname and fqdn are all set" do
+ it "favors the fqdn" do
+ expect(client.node_name).to eql(fqdn)
+ end
+ end
-describe Chef::Client do
- Chef::Config[:client_fork] = false
- it_behaves_like Chef::Client
-end
+ context "when fqdn is missing" do
+ # ohai 7 should always have machinename == return of hostname
+ let(:fqdn) { nil }
+ it "favors the machinename" do
+ expect(client.node_name).to eql(machinename)
+ end
+ end
-describe "Chef::Client Forked" do
- before do
- Chef::Config[:client_fork] = true
- end
+ context "when fqdn and machinename are missing" do
+ # ohai 6 will not have machinename, return the short hostname
+ let(:fqdn) { nil }
+ let(:machinename) { nil }
+ it "falls back to hostname" do
+ expect(client.node_name).to eql(hostname)
+ end
+ end
+
+ context "when they're all missing" do
+ let(:machinename) { nil }
+ let(:hostname) { nil }
+ let(:fqdn) { nil }
- it_behaves_like Chef::Client
+ it "throws an exception" do
+ expect { client.node_name }.to raise_error(Chef::Exceptions::CannotDetermineNodeName)
+ end
+ end
+ end
end
+
diff --git a/spec/unit/cookbook/metadata_spec.rb b/spec/unit/cookbook/metadata_spec.rb
index cba2aff5da..88c4a1a5f5 100644
--- a/spec/unit/cookbook/metadata_spec.rb
+++ b/spec/unit/cookbook/metadata_spec.rb
@@ -402,7 +402,7 @@ describe Chef::Cookbook::Metadata do
@meta.attributes["db/mysql/databases"][:recipes].should == []
end
- it "should allow the default value to be a string, array, or hash" do
+ it "should allow the default value to be a string, array, hash, boolean or numeric" do
lambda {
@meta.attribute("db/mysql/databases", :default => [])
}.should_not raise_error
@@ -413,10 +413,54 @@ describe Chef::Cookbook::Metadata do
@meta.attribute("db/mysql/databases", :default => "alice in chains")
}.should_not raise_error
lambda {
+ @meta.attribute("db/mysql/databases", :default => 1337)
+ }.should_not raise_error
+ lambda {
+ @meta.attribute("db/mysql/databases", :default => true)
+ }.should_not raise_error
+ lambda {
@meta.attribute("db/mysql/databases", :required => :not_gonna_do_it)
}.should raise_error(ArgumentError)
end
+ it "should limit the types allowed in the choice array" do
+ options = {
+ :type => "string",
+ :choice => [ "test1", "test2" ],
+ :default => "test1"
+ }
+ lambda {
+ @meta.attribute("test_cookbook/test", options)
+ }.should_not raise_error
+
+ options = {
+ :type => "boolean",
+ :choice => [ true, false ],
+ :default => true
+ }
+ lambda {
+ @meta.attribute("test_cookbook/test", options)
+ }.should_not raise_error
+
+ options = {
+ :type => "numeric",
+ :choice => [ 1337, 420 ],
+ :default => 1337
+ }
+ lambda {
+ @meta.attribute("test_cookbook/test", options)
+ }.should_not raise_error
+
+ options = {
+ :type => "numeric",
+ :choice => [ true, "false" ],
+ :default => false
+ }
+ lambda {
+ @meta.attribute("test_cookbook/test", options)
+ }.should raise_error
+ end
+
it "should error if default used with calculated" do
lambda {
attrs = {
diff --git a/spec/unit/cookbook_spec.rb b/spec/unit/cookbook_spec.rb
index ca4f4adc08..9bcea97d98 100644
--- a/spec/unit/cookbook_spec.rb
+++ b/spec/unit/cookbook_spec.rb
@@ -68,16 +68,6 @@ describe Chef::CookbookVersion do
@cookbook.preferred_filename(@node, :files, 'a-filename', 'the-checksum').should be_nil
end
- it "should allow you to include a fully-qualified recipe using the DSL" do
- # DSL method include_recipe allows multiple arguments, so extract the first
- @node.should_receive(:loaded_recipe).with(:openldap, "gigantor")
- recipe = @run_context.include_recipe("openldap::gigantor").first
-
- recipe.recipe_name.should == "gigantor"
- recipe.cookbook_name.should == :openldap
- @run_context.resource_collection[0].name.should == "blanket"
- end
-
it "should raise an ArgumentException if you try to load a bad recipe name" do
lambda { @cookbook.load_recipe("doesnt_exist", @node) }.should raise_error(ArgumentError)
end
diff --git a/spec/unit/dsl/reboot_pending_spec.rb b/spec/unit/dsl/reboot_pending_spec.rb
new file mode 100644
index 0000000000..8576ae168a
--- /dev/null
+++ b/spec/unit/dsl/reboot_pending_spec.rb
@@ -0,0 +1,100 @@
+#
+# 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/dsl/reboot_pending"
+require "spec_helper"
+
+describe Chef::DSL::RebootPending do
+ describe "reboot_pending?" do
+ describe "in isoloation" do
+ let(:recipe) { Object.new.extend(Chef::DSL::RebootPending) }
+
+ before do
+ recipe.stub(:platform?).and_return(false)
+ end
+
+ context "platform is windows" do
+ before do
+ recipe.stub(:platform?).with('windows').and_return(true)
+ recipe.stub(:registry_key_exists?).and_return(false)
+ recipe.stub(:registry_value_exists?).and_return(false)
+ end
+
+ it 'should return true if "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations" exists' do
+ recipe.stub(:registry_value_exists?).with('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations' }).and_return(true)
+ expect(recipe.reboot_pending?).to be_true
+ end
+
+ it 'should return true if "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" exists' do
+ recipe.stub(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired').and_return(true)
+ expect(recipe.reboot_pending?).to be_true
+ end
+
+ it 'should return true if key "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired" exists' do
+ recipe.stub(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired').and_return(true)
+ expect(recipe.reboot_pending?).to be_true
+ end
+
+ it 'should return true if value "HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile" contains specific data' do
+ recipe.stub(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(true)
+ recipe.stub(:registry_get_values).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(
+ [{:name => "Flags", :type => :dword, :data => 3}])
+ expect(recipe.reboot_pending?).to be_true
+ end
+ end
+
+ context "platform is ubuntu" do
+ before do
+ recipe.stub(:platform?).with('ubuntu').and_return(true)
+ end
+
+ it 'should return true if /var/run/reboot-required exists' do
+ File.stub(:exists?).with('/var/run/reboot-required').and_return(true)
+ expect(recipe.reboot_pending?).to be_true
+ end
+
+ it 'should return false if /var/run/reboot-required does not exist' do
+ File.stub(:exists?).with('/var/run/reboot-required').and_return(false)
+ expect(recipe.reboot_pending?).to be_false
+ end
+ end
+
+ context "platform is not supported" do
+ it 'should raise an exception' do
+ recipe.stub_chain(:node, :[]).with(:platform).and_return('msdos')
+ expect { recipe.reboot_pending? }.to raise_error(Chef::Exceptions::UnsupportedPlatform)
+ end
+ end
+ end # describe in isolation
+
+ describe "in a recipe" do
+ it "responds to reboot_pending?" do
+ # Chef::Recipe.new(cookbook_name, recipe_name, run_context(node, cookbook_collection, events))
+ recipe = Chef::Recipe.new(nil,nil,Chef::RunContext.new(Chef::Node.new, {}, nil))
+ expect(recipe).to respond_to(:reboot_pending?)
+ end
+ end # describe in a recipe
+
+ describe "in a resource" do
+ it "responds to reboot_pending?" do
+ resource = Chef::Resource::new("Crackerjack::Timing", nil)
+ expect(resource).to respond_to(:reboot_pending?)
+ end
+ end # describe in a resource
+ end
+end
diff --git a/spec/unit/knife/client_bulk_delete_spec.rb b/spec/unit/knife/client_bulk_delete_spec.rb
index bedd4911c5..f490851cbc 100644
--- a/spec/unit/knife/client_bulk_delete_spec.rb
+++ b/spec/unit/knife/client_bulk_delete_spec.rb
@@ -19,60 +19,143 @@
require 'spec_helper'
describe Chef::Knife::ClientBulkDelete do
- before(:each) do
- Chef::Log.logger = Logger.new(StringIO.new)
-
- Chef::Config[:node_name] = "webmonkey.example.com"
- @knife = Chef::Knife::ClientBulkDelete.new
- @knife.name_args = ["."]
- @stdout = StringIO.new
- @knife.ui.stub(:stdout).and_return(@stdout)
- @knife.ui.stub(:confirm).and_return(true)
- @clients = Hash.new
- %w{tim dan stephen}.each do |client_name|
+ let(:stdout_io) { StringIO.new }
+ let(:stdout) {stdout_io.string}
+
+ let(:knife) {
+ k = Chef::Knife::ClientBulkDelete.new
+ k.name_args = name_args
+ k.config = option_args
+ k.ui.stub(:stdout).and_return(stdout_io)
+ k.ui.stub(:confirm).and_return(knife_confirm)
+ k
+ }
+
+ let(:name_args) { [ "." ] }
+ let(:option_args) { {} }
+
+ let(:knife_confirm) { true }
+
+ let(:nonvalidator_client_names) { %w{tim dan stephen} }
+ let(:nonvalidator_clients) {
+ clients = Hash.new
+
+ nonvalidator_client_names.each do |client_name|
client = Chef::ApiClient.new()
client.name(client_name)
client.stub(:destroy).and_return(true)
- @clients[client_name] = client
+ clients[client_name] = client
+ end
+
+ clients
+ }
+
+ let(:validator_client_names) { %w{myorg-validator} }
+ let(:validator_clients) {
+ clients = Hash.new
+
+ validator_client_names.each do |validator_client_name|
+ validator_client = Chef::ApiClient.new()
+ validator_client.name(validator_client_name)
+ validator_client.stub(:validator).and_return(true)
+ validator_client.stub(:destroy).and_return(true)
+ clients[validator_client_name] = validator_client
end
- Chef::ApiClient.stub(:list).and_return(@clients)
+
+ clients
+ }
+
+ let(:client_names) { nonvalidator_client_names + validator_client_names}
+ let(:clients) {
+ nonvalidator_clients.merge(validator_clients)
+ }
+
+ before(:each) do
+ Chef::ApiClient.stub(:list).and_return(clients)
end
describe "run" do
+ describe "without a regex" do
+ let(:name_args) { [ ] }
- it "should get the list of the clients" do
- Chef::ApiClient.should_receive(:list).and_return(@clients)
- @knife.run
+ it "should exit if the regex is not provided" do
+ lambda { knife.run }.should raise_error(SystemExit)
+ end
end
- it "should print the clients you are about to delete" do
- @knife.run
- @stdout.string.should match(/#{@knife.ui.list(@clients.keys.sort, :columns_down)}/)
- end
+ describe "with any clients" do
+ it "should get the list of the clients" do
+ Chef::ApiClient.should_receive(:list)
+ knife.run
+ end
- it "should confirm you really want to delete them" do
- @knife.ui.should_receive(:confirm)
- @knife.run
- end
+ it "should print the name of the clients" do
+ knife.run
+ client_names.each do |client_name|
+ stdout.should include(client_name)
+ end
+ end
- it "should delete each client" do
- @clients.each_value do |c|
- c.should_receive(:destroy)
+ it "should confirm you really want to delete them" do
+ knife.ui.should_receive(:confirm)
+ knife.run
end
- @knife.run
- end
- it "should only delete clients that match the regex" do
- @knife.name_args = ["tim"]
- @clients["tim"].should_receive(:destroy)
- @clients["stephen"].should_not_receive(:destroy)
- @clients["dan"].should_not_receive(:destroy)
- @knife.run
+ describe "without --delete-validators" do
+ it "should mention that validator clients wont be deleted" do
+ knife.run
+ stdout.should include("Following clients are validators and will not be deleted.")
+ info = stdout.index "Following clients are validators and will not be deleted."
+ val = stdout.index "myorg-validator"
+ (val > info).should be_true
+ end
+
+ it "should only delete nonvalidator clients" do
+ nonvalidator_clients.each_value do |c|
+ c.should_receive(:destroy)
+ end
+
+ validator_clients.each_value do |c|
+ c.should_not_receive(:destroy)
+ end
+
+ knife.run
+ end
+ end
+
+ describe "with --delete-validators" do
+ let(:option_args) { {:delete_validators => true} }
+
+ it "should mention that validator clients will be deleted" do
+ knife.run
+ stdout.should include("The following validators will be deleted")
+ end
+
+ it "should confirm twice" do
+ knife.ui.should_receive(:confirm).twice
+ knife.run
+ end
+
+ it "should delete all clients" do
+ clients.each_value do |c|
+ c.should_receive(:destroy)
+ end
+
+ knife.run
+ end
+ end
end
- it "should exit if the regex is not provided" do
- @knife.name_args = []
- lambda { @knife.run }.should raise_error(SystemExit)
+ describe "with some clients" do
+ let(:name_args) { [ "^ti" ] }
+
+ it "should only delete clients that match the regex" do
+ clients["tim"].should_receive(:destroy)
+ clients["stephen"].should_not_receive(:destroy)
+ clients["dan"].should_not_receive(:destroy)
+ clients["myorg-validator"].should_not_receive(:destroy)
+ knife.run
+ end
end
end
end
diff --git a/spec/unit/knife/client_create_spec.rb b/spec/unit/knife/client_create_spec.rb
index 69c55ba015..897cee8974 100644
--- a/spec/unit/knife/client_create_spec.rb
+++ b/spec/unit/knife/client_create_spec.rb
@@ -25,7 +25,9 @@ describe Chef::Knife::ClientCreate do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::ClientCreate.new
@knife.config = {
- :file => nil
+ :file => nil,
+ :admin => false,
+ :validator => false
}
@knife.name_args = [ "adam" ]
@client = Chef::ApiClient.new
@@ -49,6 +51,16 @@ describe Chef::Knife::ClientCreate do
@knife.run
end
+ it "by default it is not an admin" do
+ @client.should_receive(:admin).with(false)
+ @knife.run
+ end
+
+ it "by default it is not a validator" do
+ @client.should_receive(:validator).with(false)
+ @knife.run
+ end
+
it "should allow you to edit the data" do
@knife.should_receive(:edit_data).with(@client)
@knife.run
@@ -70,5 +82,21 @@ describe Chef::Knife::ClientCreate do
end
end
+ describe "with -a or --admin" do
+ it "should create an admin client" do
+ @knife.config[:admin] = true
+ @client.should_receive(:admin).with(true)
+ @knife.run
+ end
+ end
+
+ describe "with --validator" do
+ it "should create an validator client" do
+ @knife.config[:validator] = true
+ @client.should_receive(:validator).with(true)
+ @knife.run
+ end
+ end
+
end
end
diff --git a/spec/unit/knife/client_delete_spec.rb b/spec/unit/knife/client_delete_spec.rb
index 9ebccbae15..01b49b3d7c 100644
--- a/spec/unit/knife/client_delete_spec.rb
+++ b/spec/unit/knife/client_delete_spec.rb
@@ -21,12 +21,16 @@ require 'spec_helper'
describe Chef::Knife::ClientDelete do
before(:each) do
@knife = Chef::Knife::ClientDelete.new
+ # defaults
+ @knife.config = {
+ :delete_validators => false
+ }
@knife.name_args = [ 'adam' ]
end
describe 'run' do
it 'should delete the client' do
- @knife.should_receive(:delete_object).with(Chef::ApiClient, 'adam')
+ @knife.should_receive(:delete_object).with(Chef::ApiClient, 'adam', 'client')
@knife.run
end
@@ -37,4 +41,43 @@ describe Chef::Knife::ClientDelete do
lambda { @knife.run }.should raise_error(SystemExit)
end
end
+
+ describe 'with a validator' do
+ before(:each) do
+ Chef::Knife::UI.stub(:confirm).and_return(true)
+ @knife.stub(:confirm).and_return(true)
+ @client = Chef::ApiClient.new
+ Chef::ApiClient.should_receive(:load).and_return(@client)
+ end
+
+ it 'should delete non-validator client if --force is not set' do
+ @knife.config[:delete_validators] = false
+ @client.should_receive(:destroy).and_return(@client)
+ @knife.should_receive(:msg)
+
+ @knife.run
+ end
+
+ it 'should delete non-validator client if --force is set' do
+ @knife.config[:delete_validators] = true
+ @client.should_receive(:destroy).and_return(@client)
+ @knife.should_receive(:msg)
+
+ @knife.run
+ end
+
+ it 'should not delete validator client if --force is not set' do
+ @client.validator(true)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run}.should raise_error(SystemExit)
+ end
+
+ it 'should delete validator client if --force is set' do
+ @knife.config[:delete_validators] = true
+ @client.should_receive(:destroy).and_return(@client)
+ @knife.should_receive(:msg)
+
+ @knife.run
+ end
+ end
end
diff --git a/spec/unit/knife/core/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb
index 47261e2068..3b166443b6 100644
--- a/spec/unit/knife/core/bootstrap_context_spec.rb
+++ b/spec/unit/knife/core/bootstrap_context_spec.rb
@@ -47,7 +47,6 @@ describe Chef::Knife::Core::BootstrapContext do
it "generates the config file data" do
expected=<<-EXPECTED
-log_level :auto
log_location STDOUT
chef_server_url "http://chef.example.com:4444"
validation_client_name "chef-validator-testing"
@@ -56,6 +55,10 @@ EXPECTED
bootstrap_context.config_content.should eq expected
end
+ it "does not set a default log_level" do
+ expect(bootstrap_context.config_content).not_to match(/log_level/)
+ end
+
describe "alternate chef-client path" do
let(:chef_config){ {:chef_client_path => '/usr/local/bin/chef-client'} }
it "runs chef-client from another path when specified" do
diff --git a/spec/unit/knife/core/ui_spec.rb b/spec/unit/knife/core/ui_spec.rb
index c626747918..f9c1ac9fd0 100644
--- a/spec/unit/knife/core/ui_spec.rb
+++ b/spec/unit/knife/core/ui_spec.rb
@@ -437,6 +437,15 @@ EOM
}.should raise_error(SystemExit) { |e| e.status.should == 3 }
end
+ it "should not exit 3 if you answer n and return false if asked for it" do
+ @ui.stdin.stub(:readline).and_return("n")
+ value = nil
+ lambda {
+ value = @ui.confirm(@question,true,false)
+ }.should_not raise_error(SystemExit)
+ value.should be(false)
+ end
+
describe "with --y or --yes passed" do
it "should return true" do
@ui.config[:yes] = true
diff --git a/spec/unit/knife/ssh_spec.rb b/spec/unit/knife/ssh_spec.rb
index eff7c9ba5b..9247db3c90 100644
--- a/spec/unit/knife/ssh_spec.rb
+++ b/spec/unit/knife/ssh_spec.rb
@@ -54,7 +54,7 @@ describe Chef::Knife::Ssh do
@knife.config[:attribute] = "ipaddress"
@knife.config[:override_attribute] = "ipaddress"
configure_query([@node_foo, @node_bar])
- @knife.should_receive(:session_from_list).with(['10.0.0.1', '10.0.0.2'])
+ @knife.should_receive(:session_from_list).with([['10.0.0.1', nil], ['10.0.0.2', nil]])
@knife.configure_session
end
@@ -62,14 +62,17 @@ describe Chef::Knife::Ssh do
@knife.config[:attribute] = "config_file" # this value will be the config file
@knife.config[:override_attribute] = "ipaddress" # this is the value of the command line via #configure_attribute
configure_query([@node_foo, @node_bar])
- @knife.should_receive(:session_from_list).with(['10.0.0.1', '10.0.0.2'])
+ @knife.should_receive(:session_from_list).with([['10.0.0.1', nil], ['10.0.0.2', nil]])
@knife.configure_session
end
end
it "searchs for and returns an array of fqdns" do
configure_query([@node_foo, @node_bar])
- @knife.should_receive(:session_from_list).with(['foo.example.org', 'bar.example.org'])
+ @knife.should_receive(:session_from_list).with([
+ ['foo.example.org', nil],
+ ['bar.example.org', nil]
+ ])
@knife.configure_session
end
@@ -83,7 +86,10 @@ describe Chef::Knife::Ssh do
it "returns an array of cloud public hostnames" do
configure_query([@node_foo, @node_bar])
- @knife.should_receive(:session_from_list).with(['ec2-10-0-0-1.compute-1.amazonaws.com', 'ec2-10-0-0-2.compute-1.amazonaws.com'])
+ @knife.should_receive(:session_from_list).with([
+ ['ec2-10-0-0-1.compute-1.amazonaws.com', nil],
+ ['ec2-10-0-0-2.compute-1.amazonaws.com', nil]
+ ])
@knife.configure_session
end
@@ -179,12 +185,17 @@ describe Chef::Knife::Ssh do
end
it "uses the port from an ssh config file" do
- @knife.session_from_list(['the.b.org'])
+ @knife.session_from_list([['the.b.org', nil]])
@knife.session.servers[0].port.should == 23
end
+ it "uses the port from a cloud attr" do
+ @knife.session_from_list([['the.b.org', 123]])
+ @knife.session.servers[0].port.should == 123
+ end
+
it "uses the user from an ssh config file" do
- @knife.session_from_list(['the.b.org'])
+ @knife.session_from_list([['the.b.org', 123]])
@knife.session.servers[0].user.should == "locutus"
end
end
diff --git a/spec/unit/node/attribute_spec.rb b/spec/unit/node/attribute_spec.rb
index ef3fc60cc6..bab2e33aa9 100644
--- a/spec/unit/node/attribute_spec.rb
+++ b/spec/unit/node/attribute_spec.rb
@@ -488,6 +488,13 @@ describe Chef::Node::Attribute do
end
end
+ describe "dup" do
+ it "array can be duped even if some elements can't" do
+ @attributes.default[:foo] = %w[foo bar baz] + Array(1..3) + [nil, true, false, [ "el", 0, nil ] ]
+ @attributes.default[:foo].dup
+ end
+ end
+
describe "has_key?" do
it "should return true if an attribute exists" do
@attributes.has_key?("music").should == true
diff --git a/spec/unit/node/immutable_collections_spec.rb b/spec/unit/node/immutable_collections_spec.rb
index 0c2b878cd2..f30c1970f7 100644
--- a/spec/unit/node/immutable_collections_spec.rb
+++ b/spec/unit/node/immutable_collections_spec.rb
@@ -86,7 +86,7 @@ end
describe Chef::Node::ImmutableArray do
before do
- @immutable_array = Chef::Node::ImmutableArray.new(%w[foo bar baz])
+ @immutable_array = Chef::Node::ImmutableArray.new(%w[foo bar baz] + Array(1..3) + [nil, true, false, [ "el", 0, nil ] ])
end
##
@@ -130,6 +130,10 @@ describe Chef::Node::ImmutableArray do
end
end
+ it "can be duped even if some elements can't" do
+ @immutable_array.dup
+ end
+
it "returns a mutable version of itself when duped" do
mutable = @immutable_array.dup
mutable[0] = :value
diff --git a/spec/unit/platform_spec.rb b/spec/unit/platform_spec.rb
index e0386a1a61..72d0a9184a 100644
--- a/spec/unit/platform_spec.rb
+++ b/spec/unit/platform_spec.rb
@@ -37,7 +37,8 @@ describe "Chef::Platform supports" do
:mswin,
:mingw32,
:windows,
- :gcel
+ :gcel,
+ :ibm_powerkvm
].each do |platform|
it "#{platform}" do
Chef::Platform.platforms.should have_key(platform)
diff --git a/spec/unit/provider/group_spec.rb b/spec/unit/provider/group_spec.rb
index 9ff9f85c7c..b138f6b210 100644
--- a/spec/unit/provider/group_spec.rb
+++ b/spec/unit/provider/group_spec.rb
@@ -96,6 +96,11 @@ describe Chef::Provider::User do
@provider.compare_group.should be_false
end
+ it "should coerce an integer to a string for comparison" do
+ @current_resource.stub!(:gid).and_return("500")
+ @provider.compare_group.should be_false
+ end
+
it "should return false if append is true and the group member(s) already exists" do
@current_resource.members << "extra_user"
@new_resource.stub(:append).and_return(true)
diff --git a/spec/unit/provider/ifconfig/debian_spec.rb b/spec/unit/provider/ifconfig/debian_spec.rb
index c2e2d1bfd1..c6a37fdd5b 100644
--- a/spec/unit/provider/ifconfig/debian_spec.rb
+++ b/spec/unit/provider/ifconfig/debian_spec.rb
@@ -53,38 +53,264 @@ describe Chef::Provider::Ifconfig::Debian do
let(:config_filename_ifcfg) { "/etc/network/interfaces.d/ifcfg-#{new_resource.device}" }
- describe "generate_config for action_add" do
+ describe "generate_config" do
- let(:config_file_ifaces) { StringIO.new }
+ context "when writing a file" do
+ let(:config_file_ifcfg) { StringIO.new }
- let(:config_file_ifcfg) { StringIO.new }
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
- before do
- expect(FileUtils).to receive(:cp)
- expect(File).to receive(:open).with(config_filename_ifaces).and_return(StringIO.new)
- expect(File).to receive(:open).with(config_filename_ifaces, "w").and_yield(config_file_ifaces)
- expect(File).to receive(:new).with(config_filename_ifcfg, "w").and_return(config_file_ifcfg)
- expect(File).to receive(:exist?).with(config_filename_ifaces).and_return(true)
- end
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
+
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
+
+ before do
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
+ expect(File).to receive(:new).with(config_filename_ifcfg, "w").and_return(config_file_ifcfg)
+ end
+
+ it "should write a network-script" do
+ provider.run_action(:add)
+ expect(config_file_ifcfg.string).to match(/^iface eth0 inet static\s*$/)
+ expect(config_file_ifcfg.string).to match(/^\s+address 10\.0\.0\.1\s*$/)
+ expect(config_file_ifcfg.string).to match(/^\s+netmask 255\.255\.254\.0\s*$/)
+ end
+
+ context "when the interface_dot_d directory does not exist" do
+ before do
+ FileUtils.rmdir tempdir_path
+ expect(File.exists?(tempdir_path)).to be_false
+ end
+
+ it "should create the /etc/network/interfaces.d directory" do
+ provider.run_action(:add)
+ expect(File.exists?(tempdir_path)).to be_true
+ expect(File.directory?(tempdir_path)).to be_true
+ end
- it "should create network-scripts directory" do
- expect(File).to receive(:directory?).with(File.dirname(config_filename_ifcfg)).and_return(false)
- expect(Dir).to receive(:mkdir).with(File.dirname(config_filename_ifcfg))
- provider.run_action(:add)
+ it "should mark the resource as updated" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
+
+ context "when the interface_dot_d directory exists" do
+ before do
+ expect(File.exists?(tempdir_path)).to be_true
+ end
+
+ it "should still mark the resource as updated (we still write a file to it)" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
end
- it "should write configure network-scripts directory" do
- expect(File).to receive(:directory?).with(File.dirname(config_filename_ifcfg)).and_return(true)
- provider.run_action(:add)
- expect(config_file_ifaces.string).to match(/^\s*source\s+\/etc\/network\/interfaces[.]d\/[*]\s*$/)
+ context "when the file is up-to-date" do
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
+
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
+
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
+
+ before do
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
+ config_file_ifcfg = StringIO.new(<<-EOF
+iface eth0 inet static
+ address 10.0.0.1
+ netmask 255.255.254.0
+EOF
+ )
+ expect(File).to receive(:new).with(config_filename_ifcfg, "w").and_return(config_file_ifcfg)
+ expect(File.exists?(tempdir_path)).to be_true # since the file exists, the enclosing dir must also exist
+ end
+
+ context "when the /etc/network/interfaces file has the source line" do
+ let(:expected_string) do
+ <<-EOF
+a line
+source #{tempdir_path}/*
+another line
+EOF
+ end
+
+ before do
+ tempfile.write(expected_string)
+ tempfile.close
+ end
+
+ it "should preserve all the contents" do
+ provider.run_action(:add)
+ expect(IO.read(tempfile.path)).to eq(expected_string)
+ end
+
+ it "should not mark the resource as updated" do
+ provider.run_action(:add)
+ pending "superclass ifconfig provider is not idempotent"
+ expect(new_resource.updated_by_last_action?).to be_false
+ end
+ end
+
+ context "when the /etc/network/interfaces file does not have the source line" do
+ let(:expected_string) do
+ <<-EOF
+a line
+another line
+source #{tempdir_path}/*
+EOF
+ end
+
+ before do
+ tempfile.write("a line\nanother line\n")
+ tempfile.close
+ end
+
+ it "should preserve the original contents and add the source line" do
+ provider.run_action(:add)
+ expect(IO.read(tempfile.path)).to eq(expected_string)
+ end
+
+ it "should mark the resource as updated" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
end
- it "should write a network-script" do
- expect(File).to receive(:directory?).with(File.dirname(config_filename_ifcfg)).and_return(true)
- provider.run_action(:add)
- expect(config_file_ifcfg.string).to match(/^iface eth0 inet static\s*$/)
- expect(config_file_ifcfg.string).to match(/^\s+address 10\.0\.0\.1\s*$/)
- expect(config_file_ifcfg.string).to match(/^\s+netmask 255\.255\.254\.0\s*$/)
+ describe "when running under why run" do
+
+ before do
+ Chef::Config[:why_run] = true
+ end
+
+ after do
+ Chef::Config[:why_run] = false
+ end
+
+ context "when writing a file" do
+ let(:config_file_ifcfg) { StringIO.new }
+
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
+
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
+
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
+
+ before do
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
+ expect(File).not_to receive(:new).with(config_filename_ifcfg, "w")
+ end
+
+ it "should write a network-script" do
+ provider.run_action(:add)
+ expect(config_file_ifcfg.string).not_to match(/^iface eth0 inet static\s*$/)
+ expect(config_file_ifcfg.string).not_to match(/^\s+address 10\.0\.0\.1\s*$/)
+ expect(config_file_ifcfg.string).not_to match(/^\s+netmask 255\.255\.254\.0\s*$/)
+ end
+
+ context "when the interface_dot_d directory does not exist" do
+ before do
+ FileUtils.rmdir tempdir_path
+ expect(File.exists?(tempdir_path)).to be_false
+ end
+
+ it "should not create the /etc/network/interfaces.d directory" do
+ provider.run_action(:add)
+ expect(File.exists?(tempdir_path)).not_to be_true
+ end
+
+ it "should mark the resource as updated" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
+
+ context "when the interface_dot_d directory exists" do
+ before do
+ expect(File.exists?(tempdir_path)).to be_true
+ end
+
+ it "should still mark the resource as updated (we still write a file to it)" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
+ end
+
+ context "when the file is up-to-date" do
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
+
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
+
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
+
+ before do
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
+ config_file_ifcfg = StringIO.new(<<-EOF
+iface eth0 inet static
+ address 10.0.0.1
+ netmask 255.255.254.0
+ EOF
+ )
+ expect(File).not_to receive(:new).with(config_filename_ifcfg, "w")
+ expect(File.exists?(tempdir_path)).to be_true # since the file exists, the enclosing dir must also exist
+ end
+
+ context "when the /etc/network/interfaces file has the source line" do
+ let(:expected_string) do
+ <<-EOF
+a line
+source #{tempdir_path}/*
+another line
+ EOF
+ end
+
+ before do
+ tempfile.write(expected_string)
+ tempfile.close
+ end
+
+ it "should preserve all the contents" do
+ provider.run_action(:add)
+ expect(IO.read(tempfile.path)).to eq(expected_string)
+ end
+
+ it "should not mark the resource as updated" do
+ provider.run_action(:add)
+ pending "superclass ifconfig provider is not idempotent"
+ expect(new_resource.updated_by_last_action?).to be_false
+ end
+ end
+
+ context "when the /etc/network/interfaces file does not have the source line" do
+ let(:expected_string) do
+ <<-EOF
+a line
+another line
+source #{tempdir_path}/*
+ EOF
+ end
+
+ before do
+ tempfile.write("a line\nanother line\n")
+ tempfile.close
+ end
+
+ it "should preserve the original contents and not add the source line" do
+ provider.run_action(:add)
+ expect(IO.read(tempfile.path)).to eq("a line\nanother line\n")
+ end
+
+ it "should mark the resource as updated" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
+ end
end
end
@@ -98,4 +324,5 @@ describe Chef::Provider::Ifconfig::Debian do
provider.run_action(:delete)
end
end
+
end
diff --git a/spec/unit/provider/ohai_spec.rb b/spec/unit/provider/ohai_spec.rb
index 8b8a6b5939..2085f44309 100644
--- a/spec/unit/provider/ohai_spec.rb
+++ b/spec/unit/provider/ohai_spec.rb
@@ -41,9 +41,8 @@ describe Chef::Provider::Ohai do
:newdata => "somevalue"
}
}
- mock_ohai.stub(:all_plugins).and_return(true)
- mock_ohai.stub(:require_plugin).and_return(true)
- mock_ohai.stub(:data).and_return(mock_ohai[:data],
+ mock_ohai.stub!(:all_plugins).and_return(true)
+ mock_ohai.stub!(:data).and_return(mock_ohai[:data],
mock_ohai[:data2])
Ohai::System.stub(:new).and_return(mock_ohai)
Chef::Platform.stub(:find_platform_and_version).and_return({ "platform" => @platform,
diff --git a/spec/unit/provider/service/macosx_spec.rb b/spec/unit/provider/service/macosx_spec.rb
index 65639f2084..1e9656aeac 100644
--- a/spec/unit/provider/service/macosx_spec.rb
+++ b/spec/unit/provider/service/macosx_spec.rb
@@ -46,14 +46,32 @@ describe Chef::Provider::Service::Macosx do
let(:events) {Chef::EventDispatch::Dispatcher.new}
let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:provider) { described_class.new(new_resource, run_context) }
- let(:stdout) { StringIO.new }
+ let(:launchctl_stdout) { StringIO.new }
+ let(:plutil_stdout) { String.new <<-XML }
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>io.redis.redis-server</string>
+</dict>
+</plist>
+XML
["redis-server", "io.redis.redis-server"].each do |service_name|
before do
Dir.stub(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist"], [])
provider.stub(:shell_out!).
with("launchctl list", {:group => 1001, :user => 101}).
- and_return(double("ouput", :stdout => stdout))
+ and_return(double("Status", :stdout => launchctl_stdout))
+ provider.stub(:shell_out).
+ with(/launchctl list /,
+ {:group => nil, :user => nil}).
+ and_return(double("Status",
+ :stdout => launchctl_stdout, :exitstatus => 0))
+ provider.stub(:shell_out!).
+ with(/plutil -convert xml1 -o/).
+ and_return(double("Status", :stdout => plutil_stdout))
File.stub(:stat).and_return(double("stat", :gid => 1001, :uid => 101))
end
@@ -64,7 +82,7 @@ describe Chef::Provider::Service::Macosx do
describe "#load_current_resource" do
context "when launchctl returns pid in service list" do
- let(:stdout) { StringIO.new <<-SVC_LIST }
+ let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
12761 - 0x100114220.old.machinit.thing
7777 - io.redis.redis-server
- - com.lol.stopped-thing
@@ -84,21 +102,21 @@ describe Chef::Provider::Service::Macosx do
end
describe "running unsupported actions" do
+ let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
+12761 - 0x100114220.old.machinit.thing
+7777 - io.redis.redis-server
+- - com.lol.stopped-thing
+SVC_LIST
+
before do
Dir.stub(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist"], [])
end
- it "should throw an exception when enable action is attempted" do
- lambda {provider.run_action(:enable)}.should raise_error(Chef::Exceptions::UnsupportedAction)
- end
it "should throw an exception when reload action is attempted" do
lambda {provider.run_action(:reload)}.should raise_error(Chef::Exceptions::UnsupportedAction)
end
- it "should throw an exception when disable action is attempted" do
- lambda {provider.run_action(:disable)}.should raise_error(Chef::Exceptions::UnsupportedAction)
- end
end
context "when launchctl returns empty service pid" do
- let(:stdout) { StringIO.new <<-SVC_LIST }
+ let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
12761 - 0x100114220.old.machinit.thing
- - io.redis.redis-server
- - com.lol.stopped-thing
@@ -118,7 +136,7 @@ describe Chef::Provider::Service::Macosx do
end
context "when launchctl doesn't return service entry at all" do
- let(:stdout) { StringIO.new <<-SVC_LIST }
+ let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
12761 - 0x100114220.old.machinit.thing
- - com.lol.stopped-thing
SVC_LIST
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index b0cd04b245..2bdf470143 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -339,6 +339,7 @@ describe Chef::Recipe do
describe "include_recipe" do
it "should evaluate another recipe with include_recipe" do
node.should_receive(:loaded_recipe).with(:openldap, "gigantor")
+ run_context.stub(:unreachable_cookbook?).with(:openldap).and_return(false)
run_context.include_recipe "openldap::gigantor"
res = run_context.resource_collection.resources(:cat => "blanket")
res.name.should eql("blanket")
@@ -347,6 +348,7 @@ describe Chef::Recipe do
it "should load the default recipe for a cookbook if include_recipe is called without a ::" do
node.should_receive(:loaded_recipe).with(:openldap, "default")
+ run_context.stub(:unreachable_cookbook?).with(:openldap).and_return(false)
run_context.include_recipe "openldap"
res = run_context.resource_collection.resources(:cat => "blanket")
res.name.should eql("blanket")
@@ -355,12 +357,14 @@ describe Chef::Recipe do
it "should store that it has seen a recipe in the run_context" do
node.should_receive(:loaded_recipe).with(:openldap, "default")
+ run_context.stub(:unreachable_cookbook?).with(:openldap).and_return(false)
run_context.include_recipe "openldap"
run_context.loaded_recipe?("openldap").should be_true
end
it "should not include the same recipe twice" do
node.should_receive(:loaded_recipe).with(:openldap, "default").exactly(:once)
+ run_context.stub(:unreachable_cookbook?).with(:openldap).and_return(false)
cookbook_collection[:openldap].should_receive(:load_recipe).with("default", run_context)
recipe.include_recipe "openldap"
cookbook_collection[:openldap].should_not_receive(:load_recipe).with("default", run_context)
diff --git a/spec/unit/run_context/cookbook_compiler_spec.rb b/spec/unit/run_context/cookbook_compiler_spec.rb
index 52f4772206..5c50c3dd4b 100644
--- a/spec/unit/run_context/cookbook_compiler_spec.rb
+++ b/spec/unit/run_context/cookbook_compiler_spec.rb
@@ -170,5 +170,17 @@ describe Chef::RunContext::CookbookCompiler do
:"circular-dep1",
:"test-with-circular-deps"]
end
+
+ it "determines if a cookbook is in the list of cookbooks reachable by dependency" do
+ node.run_list("test-with-deps::default", "test-with-deps::server")
+ compiler.cookbook_order.should == [:dependency1, :dependency2, :"test-with-deps"]
+ compiler.unreachable_cookbook?(:dependency1).should be_false
+ compiler.unreachable_cookbook?(:dependency2).should be_false
+ compiler.unreachable_cookbook?(:'test-with-deps').should be_false
+ compiler.unreachable_cookbook?(:'circular-dep1').should be_true
+ compiler.unreachable_cookbook?(:'circular-dep2').should be_true
+ end
+
+
end
end
diff --git a/spec/unit/run_context_spec.rb b/spec/unit/run_context_spec.rb
index 39b8a8a50d..813102527b 100644
--- a/spec/unit/run_context_spec.rb
+++ b/spec/unit/run_context_spec.rb
@@ -79,6 +79,13 @@ describe Chef::RunContext do
@node.include_attribute("test::george")
end
+ it "raises an error when attempting to include_recipe from a cookbook not reachable by run list or dependencies" do
+ @node.should_receive(:loaded_recipe).with(:ancient, "aliens")
+ lambda do
+ @run_context.include_recipe("ancient::aliens")
+ # In CHEF-5120, this becomes a Chef::Exceptions::MissingCookbookDependency error:
+ end.should raise_error(Chef::Exceptions::CookbookNotFound)
+ end
end