summaryrefslogtreecommitdiff
path: root/spec/unit/knife
diff options
context:
space:
mode:
Diffstat (limited to 'spec/unit/knife')
-rw-r--r--spec/unit/knife/bootstrap_spec.rb214
-rw-r--r--spec/unit/knife/client_bulk_delete_spec.rb78
-rw-r--r--spec/unit/knife/client_create_spec.rb74
-rw-r--r--spec/unit/knife/client_delete_spec.rb40
-rw-r--r--spec/unit/knife/client_edit_spec.rb40
-rw-r--r--spec/unit/knife/client_list_spec.rb34
-rw-r--r--spec/unit/knife/client_reregister_spec.rb61
-rw-r--r--spec/unit/knife/client_show_spec.rb42
-rw-r--r--spec/unit/knife/config_file_selection_spec.rb118
-rw-r--r--spec/unit/knife/configure_client_spec.rb83
-rw-r--r--spec/unit/knife/configure_spec.rb229
-rw-r--r--spec/unit/knife/cookbook_bulk_delete_spec.rb87
-rw-r--r--spec/unit/knife/cookbook_create_spec.rb271
-rw-r--r--spec/unit/knife/cookbook_delete_spec.rb239
-rw-r--r--spec/unit/knife/cookbook_download_spec.rb217
-rw-r--r--spec/unit/knife/cookbook_list_spec.rb88
-rw-r--r--spec/unit/knife/cookbook_metadata_from_file_spec.rb65
-rw-r--r--spec/unit/knife/cookbook_metadata_spec.rb179
-rw-r--r--spec/unit/knife/cookbook_show_spec.rb223
-rw-r--r--spec/unit/knife/cookbook_site_download_spec.rb151
-rw-r--r--spec/unit/knife/cookbook_site_install_spec.rb138
-rw-r--r--spec/unit/knife/cookbook_site_share_spec.rb146
-rw-r--r--spec/unit/knife/cookbook_site_unshare_spec.rb77
-rw-r--r--spec/unit/knife/cookbook_test_spec.rb84
-rw-r--r--spec/unit/knife/cookbook_upload_spec.rb183
-rw-r--r--spec/unit/knife/core/bootstrap_context_spec.rb128
-rw-r--r--spec/unit/knife/core/cookbook_scm_repo_spec.rb187
-rw-r--r--spec/unit/knife/core/object_loader_spec.rb81
-rw-r--r--spec/unit/knife/core/subcommand_loader_spec.rb54
-rw-r--r--spec/unit/knife/core/ui_spec.rb309
-rw-r--r--spec/unit/knife/data_bag_create_spec.rb105
-rw-r--r--spec/unit/knife/data_bag_edit_spec.rb89
-rw-r--r--spec/unit/knife/data_bag_from_file_spec.rb191
-rw-r--r--spec/unit/knife/data_bag_show_spec.rb112
-rw-r--r--spec/unit/knife/environment_create_spec.rb91
-rw-r--r--spec/unit/knife/environment_delete_spec.rb71
-rw-r--r--spec/unit/knife/environment_edit_spec.rb79
-rw-r--r--spec/unit/knife/environment_from_file_spec.rb89
-rw-r--r--spec/unit/knife/environment_list_spec.rb54
-rw-r--r--spec/unit/knife/environment_show_spec.rb52
-rw-r--r--spec/unit/knife/index_rebuild_spec.rb65
-rw-r--r--spec/unit/knife/knife_help.rb92
-rw-r--r--spec/unit/knife/node_bulk_delete_spec.rb97
-rw-r--r--spec/unit/knife/node_delete_spec.rb68
-rw-r--r--spec/unit/knife/node_edit_spec.rb88
-rw-r--r--spec/unit/knife/node_from_file_spec.rb59
-rw-r--r--spec/unit/knife/node_list_spec.rb63
-rw-r--r--spec/unit/knife/node_run_list_add_spec.rb125
-rw-r--r--spec/unit/knife/node_run_list_remove_spec.rb74
-rw-r--r--spec/unit/knife/node_show_spec.rb48
-rw-r--r--spec/unit/knife/role_bulk_delete_spec.rb80
-rw-r--r--spec/unit/knife/role_create_spec.rb80
-rw-r--r--spec/unit/knife/role_delete_spec.rb67
-rw-r--r--spec/unit/knife/role_edit_spec.rb79
-rw-r--r--spec/unit/knife/role_from_file_spec.rb69
-rw-r--r--spec/unit/knife/role_list_spec.rb56
-rw-r--r--spec/unit/knife/ssh_spec.rb182
-rw-r--r--spec/unit/knife/status_spec.rb43
-rw-r--r--spec/unit/knife/tag_create_spec.rb23
-rw-r--r--spec/unit/knife/tag_delete_spec.rb25
-rw-r--r--spec/unit/knife/tag_list_spec.rb23
61 files changed, 6359 insertions, 0 deletions
diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb
new file mode 100644
index 0000000000..b2247bb5a4
--- /dev/null
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -0,0 +1,214 @@
+#
+# Author:: Ian Meyer (<ianmmeyer@gmail.com>)
+# Copyright:: Copyright (c) 2010 Ian Meyer
+# 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'
+
+Chef::Knife::Bootstrap.load_deps
+require 'net/ssh'
+
+describe Chef::Knife::Bootstrap do
+ before(:each) do
+ Chef::Log.logger = Logger.new(StringIO.new)
+ @knife = Chef::Knife::Bootstrap.new
+ @knife.config[:template_file] = File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb"))
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @stderr = StringIO.new
+ @knife.ui.stub!(:stderr).and_return(@stderr)
+ end
+
+ it "should return a name of default bootstrap template" do
+ @knife.find_template.should be_a_kind_of(String)
+ end
+
+ it "should error if template can not be found" do
+ @knife.config[:template_file] = false
+ @knife.config[:distro] = 'penultimate'
+ lambda { @knife.find_template }.should raise_error
+ end
+
+ it "should look for templates early in the run" do
+ File.stub(:exists?).and_return(true)
+ @knife.name_args = ['shatner']
+ @knife.stub!(:read_template).and_return("")
+ @knife.stub!(:knife_ssh).and_return(true)
+ @knife_ssh = @knife.knife_ssh
+ @knife.should_receive(:find_template).ordered
+ @knife.should_receive(:knife_ssh).ordered
+ @knife_ssh.should_receive(:run) # rspec appears to keep order per object
+ @knife.run
+ end
+
+ it "should load the specified template" do
+ @knife.config[:distro] = 'fedora13-gems'
+ lambda { @knife.find_template }.should_not raise_error
+ end
+
+ it "should load the specified template from a Ruby gem" do
+ @knife.config[:template_file] = false
+ Gem.stub(:find_files).and_return(["/Users/schisamo/.rvm/gems/ruby-1.9.2-p180@chef-0.10/gems/knife-windows-0.5.4/lib/chef/knife/bootstrap/fake-bootstrap-template.erb"])
+ File.stub(:exists?).and_return(true)
+ IO.stub(:read).and_return('random content')
+ @knife.config[:distro] = 'fake-bootstrap-template'
+ lambda { @knife.find_template }.should_not raise_error
+ end
+
+ it "should return an empty run_list" do
+ @knife.instance_variable_set("@template_file", @knife.config[:template_file])
+ template_string = @knife.read_template
+ @knife.render_template(template_string).should == '{"run_list":[]}'
+ end
+
+ it "should have role[base] in the run_list" do
+ @knife.instance_variable_set("@template_file", @knife.config[:template_file])
+ template_string = @knife.read_template
+ @knife.parse_options(["-r","role[base]"])
+ @knife.render_template(template_string).should == '{"run_list":["role[base]"]}'
+ end
+
+ it "should have role[base] and recipe[cupcakes] in the run_list" do
+ @knife.instance_variable_set("@template_file", @knife.config[:template_file])
+ template_string = @knife.read_template
+ @knife.parse_options(["-r", "role[base],recipe[cupcakes]"])
+ @knife.render_template(template_string).should == '{"run_list":["role[base]","recipe[cupcakes]"]}'
+ end
+
+ it "should have foo => {bar => baz} in the first_boot" do
+ @knife.instance_variable_set("@template_file", @knife.config[:template_file])
+ template_string = @knife.read_template
+ @knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
+ expected_hash = Yajl::Parser.new.parse('{"foo":{"bar":"baz"},"run_list":[]}')
+ actual_hash = Yajl::Parser.new.parse(@knife.render_template(template_string))
+ actual_hash.should == expected_hash
+ end
+
+ it "should create a hint file when told to" do
+ @knife.config[:template_file] = File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test-hints.erb"))
+ @knife.instance_variable_set("@template_file", @knife.config[:template_file])
+ template_string = @knife.read_template
+ @knife.parse_options(["--hint", "openstack"])
+ @knife.render_template(template_string).should match /\/etc\/chef\/ohai\/hints\/openstack.json/
+ end
+
+ it "should populate a hint file with JSON when given a file to read" do
+ @knife.stub(:find_template).and_return(true)
+ @knife.config[:template_file] = File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test-hints.erb"))
+ ::File.stub!(:read).and_return('{ "foo" : "bar" }')
+ @knife.instance_variable_set("@template_file", @knife.config[:template_file])
+ template_string = @knife.read_template
+ @knife.stub!(:read_template).and_return('{ "foo" : "bar" }')
+ @knife.parse_options(["--hint", "openstack=hints/openstack.json"])
+ @knife.render_template(template_string).should match /\{\"foo\":\"bar\"\}/
+ end
+
+
+ it "should take the node name from ARGV" do
+ @knife.name_args = ['barf']
+ @knife.name_args.first.should == "barf"
+ end
+
+ describe "when configuring the underlying knife ssh command" do
+ before do
+ @knife.name_args = ["foo.example.com"]
+ @knife.config[:ssh_user] = "rooty"
+ @knife.config[:ssh_password] = "open_sesame"
+ Chef::Config[:knife][:ssh_port] = "4001"
+ @knife.config[:identity_file] = "~/.ssh/me.rsa"
+ @knife.stub!(:read_template).and_return("")
+ @knife_ssh = @knife.knife_ssh
+ end
+
+ it "configures the hostname" do
+ @knife_ssh.name_args.first.should == "foo.example.com"
+ end
+
+ it "configures the ssh user" do
+ @knife_ssh.config[:ssh_user].should == 'rooty'
+ end
+
+ it "configures the ssh password" do
+ @knife_ssh.config[:ssh_password].should == 'open_sesame'
+ end
+
+ it "configures the ssh port" do
+ @knife_ssh.config[:ssh_port].should == '4001'
+ end
+
+ it "configures the ssh identity file" do
+ @knife_ssh.config[:identity_file].should == '~/.ssh/me.rsa'
+ end
+ end
+
+ describe "when falling back to password auth when host key auth fails" do
+ before do
+ @knife.name_args = ["foo.example.com"]
+ @knife.config[:ssh_user] = "rooty"
+ @knife.config[:identity_file] = "~/.ssh/me.rsa"
+ @knife.stub!(:read_template).and_return("")
+ @knife_ssh = @knife.knife_ssh
+ end
+
+ it "prompts the user for a password " do
+ @knife.stub!(:knife_ssh).and_return(@knife_ssh)
+ @knife_ssh.stub!(:get_password).and_return('typed_in_password')
+ alternate_knife_ssh = @knife.knife_ssh_with_password_auth
+ alternate_knife_ssh.config[:ssh_password].should == 'typed_in_password'
+ end
+
+ it "configures knife not to use the identity file that didn't work previously" do
+ @knife.stub!(:knife_ssh).and_return(@knife_ssh)
+ @knife_ssh.stub!(:get_password).and_return('typed_in_password')
+ alternate_knife_ssh = @knife.knife_ssh_with_password_auth
+ alternate_knife_ssh.config[:identity_file].should be_nil
+ end
+ end
+
+ describe "when running the bootstrap" do
+ before do
+ @knife.name_args = ["foo.example.com"]
+ @knife.config[:ssh_user] = "rooty"
+ @knife.config[:identity_file] = "~/.ssh/me.rsa"
+ @knife.stub!(:read_template).and_return("")
+ @knife_ssh = @knife.knife_ssh
+ @knife.stub!(:knife_ssh).and_return(@knife_ssh)
+ end
+
+ it "verifies that a server to bootstrap was given as a command line arg" do
+ @knife.name_args = nil
+ lambda { @knife.run }.should raise_error(SystemExit)
+ @stderr.string.should match /ERROR:.+FQDN or ip/
+ end
+
+ it "configures the underlying ssh command and then runs it" do
+ @knife_ssh.should_receive(:run)
+ @knife.run
+ end
+
+ it "falls back to password based auth when auth fails the first time" do
+ @knife.stub!(:puts)
+
+ @fallback_knife_ssh = @knife_ssh.dup
+ @knife_ssh.should_receive(:run).and_raise(Net::SSH::AuthenticationFailed.new("no ssh for you"))
+ @knife.stub!(:knife_ssh_with_password_auth).and_return(@fallback_knife_ssh)
+ @fallback_knife_ssh.should_receive(:run)
+ @knife.run
+ end
+
+ end
+
+end
diff --git a/spec/unit/knife/client_bulk_delete_spec.rb b/spec/unit/knife/client_bulk_delete_spec.rb
new file mode 100644
index 0000000000..55351554d1
--- /dev/null
+++ b/spec/unit/knife/client_bulk_delete_spec.rb
@@ -0,0 +1,78 @@
+#
+# Author:: Stephen Delano (<stephen@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+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|
+ client = Chef::ApiClient.new()
+ client.name(client_name)
+ client.stub!(:destroy).and_return(true)
+ @clients[client_name] = client
+ end
+ Chef::ApiClient.stub!(:list).and_return(@clients)
+ end
+
+ describe "run" do
+
+ it "should get the list of the clients" do
+ Chef::ApiClient.should_receive(:list).and_return(@clients)
+ @knife.run
+ 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
+
+ it "should confirm you really want to delete them" do
+ @knife.ui.should_receive(:confirm)
+ @knife.run
+ end
+
+ it "should delete each client" do
+ @clients.each_value do |c|
+ c.should_receive(:destroy)
+ 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
+ end
+
+ it "should exit if the regex is not provided" do
+ @knife.name_args = []
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+ end
+end
diff --git a/spec/unit/knife/client_create_spec.rb b/spec/unit/knife/client_create_spec.rb
new file mode 100644
index 0000000000..c049748074
--- /dev/null
+++ b/spec/unit/knife/client_create_spec.rb
@@ -0,0 +1,74 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+Chef::Knife::ClientCreate.load_deps
+
+describe Chef::Knife::ClientCreate do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::ClientCreate.new
+ @knife.config = {
+ :file => nil
+ }
+ @knife.name_args = [ "adam" ]
+ @client = Chef::ApiClient.new
+ @client.stub!(:save).and_return({ 'private_key' => '' })
+ @knife.stub!(:edit_data).and_return(@client)
+ @knife.stub!(:puts)
+ Chef::ApiClient.stub!(:new).and_return(@client)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+ it "should create a new Client" do
+ Chef::ApiClient.should_receive(:new).and_return(@client)
+ @knife.run
+ @stdout.string.should match /created client.+adam/i
+ end
+
+ it "should set the Client name" do
+ @client.should_receive(:name).with("adam")
+ @knife.run
+ end
+
+ it "should allow you to edit the data" do
+ @knife.should_receive(:edit_data).with(@client)
+ @knife.run
+ end
+
+ it "should save the Client" do
+ @client.should_receive(:save)
+ @knife.run
+ end
+
+ describe "with -f or --file" do
+ it "should write the private key to a file" do
+ @knife.config[:file] = "/tmp/monkeypants"
+ @client.stub!(:save).and_return({ 'private_key' => "woot" })
+ filehandle = mock("Filehandle")
+ filehandle.should_receive(:print).with('woot')
+ File.should_receive(:open).with("/tmp/monkeypants", "w").and_yield(filehandle)
+ @knife.run
+ end
+ end
+
+ end
+end
diff --git a/spec/unit/knife/client_delete_spec.rb b/spec/unit/knife/client_delete_spec.rb
new file mode 100644
index 0000000000..865f19f713
--- /dev/null
+++ b/spec/unit/knife/client_delete_spec.rb
@@ -0,0 +1,40 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::ClientDelete do
+ before(:each) do
+ @knife = Chef::Knife::ClientDelete.new
+ @knife.name_args = [ 'adam' ]
+ end
+
+ describe 'run' do
+ it 'should delete the client' do
+ @knife.should_receive(:delete_object).with(Chef::ApiClient, 'adam')
+ @knife.run
+ end
+
+ it 'should print usage and exit when a client name is not provided' do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+ end
+end
diff --git a/spec/unit/knife/client_edit_spec.rb b/spec/unit/knife/client_edit_spec.rb
new file mode 100644
index 0000000000..1308d14fd5
--- /dev/null
+++ b/spec/unit/knife/client_edit_spec.rb
@@ -0,0 +1,40 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::ClientEdit do
+ before(:each) do
+ @knife = Chef::Knife::ClientEdit.new
+ @knife.name_args = [ 'adam' ]
+ end
+
+ describe 'run' do
+ it 'should edit the client' do
+ @knife.should_receive(:edit_object).with(Chef::ApiClient, 'adam')
+ @knife.run
+ end
+
+ it 'should print usage and exit when a client name is not provided' do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+ end
+end
diff --git a/spec/unit/knife/client_list_spec.rb b/spec/unit/knife/client_list_spec.rb
new file mode 100644
index 0000000000..6237a0d14c
--- /dev/null
+++ b/spec/unit/knife/client_list_spec.rb
@@ -0,0 +1,34 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::ClientList do
+ before(:each) do
+ @knife = Chef::Knife::ClientList.new
+ @knife.name_args = [ 'adam' ]
+ end
+
+ describe 'run' do
+ it 'should list the clients' do
+ Chef::ApiClient.should_receive(:list)
+ @knife.should_receive(:format_list_for_display)
+ @knife.run
+ end
+ end
+end
diff --git a/spec/unit/knife/client_reregister_spec.rb b/spec/unit/knife/client_reregister_spec.rb
new file mode 100644
index 0000000000..0d284c0f58
--- /dev/null
+++ b/spec/unit/knife/client_reregister_spec.rb
@@ -0,0 +1,61 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::ClientReregister do
+ before(:each) do
+ @knife = Chef::Knife::ClientReregister.new
+ @knife.name_args = [ 'adam' ]
+ @client_mock = mock('client_mock')
+ @client_mock.stub!(:save).and_return({ 'private_key' => 'foo_key' })
+ Chef::ApiClient.stub!(:load).and_return(@client_mock)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe 'run' do
+ it 'should load and save the client' do
+ Chef::ApiClient.should_receive(:load).with('adam').and_return(@client_mock)
+ @client_mock.should_receive(:save).with(true).and_return({'private_key' => 'foo_key'})
+ @knife.run
+ end
+
+ it 'should output the private key' do
+ @knife.run
+ @stdout.string.should match /foo_key/
+ end
+
+ it 'should print usage and exit when a client name is not provided' do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ describe 'with -f or --file' do
+ it 'should write the private key to a file' do
+ @knife.config[:file] = '/tmp/monkeypants'
+ filehandle = mock('Filehandle')
+ filehandle.should_receive(:print).with('foo_key')
+ File.should_receive(:open).with('/tmp/monkeypants', 'w').and_yield(filehandle)
+ @knife.run
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/client_show_spec.rb b/spec/unit/knife/client_show_spec.rb
new file mode 100644
index 0000000000..5bac3b4af6
--- /dev/null
+++ b/spec/unit/knife/client_show_spec.rb
@@ -0,0 +1,42 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::ClientShow do
+ before(:each) do
+ @knife = Chef::Knife::ClientShow.new
+ @knife.name_args = [ 'adam' ]
+ @client_mock = mock('client_mock')
+ end
+
+ describe 'run' do
+ it 'should list the client' do
+ Chef::ApiClient.should_receive(:load).with('adam').and_return(@client_mock)
+ @knife.should_receive(:format_for_display).with(@client_mock)
+ @knife.run
+ end
+
+ it 'should print usage and exit when a client name is not provided' do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+ end
+end
diff --git a/spec/unit/knife/config_file_selection_spec.rb b/spec/unit/knife/config_file_selection_spec.rb
new file mode 100644
index 0000000000..3ee18d82d0
--- /dev/null
+++ b/spec/unit/knife/config_file_selection_spec.rb
@@ -0,0 +1,118 @@
+#
+# Author:: Nicolas Vinot (<aeris@imirhil.fr>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
+require 'tmpdir'
+
+describe Chef::Knife do
+ before :each do
+ Chef::Config.stub!(:from_file).and_return(true)
+ end
+
+ it "configure knife from KNIFE_HOME env variable" do
+ env_config = File.expand_path(File.join(Dir.tmpdir, 'knife.rb'))
+ File.stub!(:exist?).and_return(false)
+ File.stub!(:exist?).with(env_config).and_return(true)
+
+ ENV['KNIFE_HOME'] = Dir.tmpdir
+ @knife = Chef::Knife.new
+ @knife.configure_chef
+ @knife.config[:config_file].should == env_config
+ end
+
+ it "configure knife from PWD" do
+ pwd_config = "#{Dir.pwd}/knife.rb"
+ File.stub!(:exist?).and_return do | arg |
+ [ pwd_config ].include? arg
+ end
+
+ @knife = Chef::Knife.new
+ @knife.configure_chef
+ @knife.config[:config_file].should == pwd_config
+ end
+
+ it "configure knife from UPWARD" do
+ upward_dir = File.expand_path "#{Dir.pwd}/.chef"
+ upward_config = File.expand_path "#{upward_dir}/knife.rb"
+ File.stub!(:exist?).and_return do | arg |
+ [ upward_config ].include? arg
+ end
+ Chef::Knife.stub!(:chef_config_dir).and_return(upward_dir)
+
+ @knife = Chef::Knife.new
+ @knife.configure_chef
+ @knife.config[:config_file].should == upward_config
+ end
+
+ it "configure knife from HOME" do
+ home_config = File.expand_path(File.join("#{ENV['HOME']}", "/.chef/knife.rb"))
+ File.stub!(:exist?).and_return do | arg |
+ [ home_config ].include? arg
+ end
+
+ @knife = Chef::Knife.new
+ @knife.configure_chef
+ @knife.config[:config_file].should == home_config
+ end
+
+ it "configure knife from nothing" do
+ ::File.stub!(:exist?).and_return(false)
+ @knife = Chef::Knife.new
+ @knife.ui.should_receive(:warn).with("No knife configuration file found")
+ @knife.configure_chef
+ @knife.config[:config_file].should be_nil
+ end
+
+ it "configure knife precedence" do
+ env_config = File.join(Dir.tmpdir, 'knife.rb')
+ pwd_config = "#{Dir.pwd}/knife.rb"
+ upward_dir = File.expand_path "#{Dir.pwd}/.chef"
+ upward_config = File.expand_path "#{upward_dir}/knife.rb"
+ home_config = File.expand_path(File.join("#{ENV['HOME']}", "/.chef/knife.rb"))
+ configs = [ env_config, pwd_config, upward_config, home_config ]
+ File.stub!(:exist?).and_return do | arg |
+ configs.include? arg
+ end
+ Chef::Knife.stub!(:chef_config_dir).and_return(upward_dir)
+ ENV['KNIFE_HOME'] = Dir.tmpdir
+
+ @knife = Chef::Knife.new
+ @knife.configure_chef
+ @knife.config[:config_file].should == env_config
+
+ configs.delete env_config
+ @knife.config.delete :config_file
+ @knife.configure_chef
+ @knife.config[:config_file].should == pwd_config
+
+ configs.delete pwd_config
+ @knife.config.delete :config_file
+ @knife.configure_chef
+ @knife.config[:config_file].should == upward_config
+
+ configs.delete upward_config
+ @knife.config.delete :config_file
+ @knife.configure_chef
+ @knife.config[:config_file].should == home_config
+
+ configs.delete home_config
+ @knife.config.delete :config_file
+ @knife.configure_chef
+ @knife.config[:config_file].should be_nil
+ end
+end
diff --git a/spec/unit/knife/configure_client_spec.rb b/spec/unit/knife/configure_client_spec.rb
new file mode 100644
index 0000000000..ba832103bc
--- /dev/null
+++ b/spec/unit/knife/configure_client_spec.rb
@@ -0,0 +1,83 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::ConfigureClient do
+ before do
+ @knife = Chef::Knife::ConfigureClient.new
+ Chef::Config[:chef_server_url] = 'https://chef.example.com'
+ Chef::Config[:validation_client_name] = 'chef-validator'
+ Chef::Config[:validation_key] = '/etc/chef/validation.pem'
+
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe 'run' do
+ it 'should print usage and exit when a directory is not provided' do
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal).with(/must provide the directory/)
+ lambda {
+ @knife.run
+ }.should raise_error SystemExit
+ end
+
+ describe 'when specifing a directory' do
+ before do
+ @knife.name_args = ['/home/bob/.chef']
+ @client_file = StringIO.new
+ @validation_file = StringIO.new
+ File.should_receive(:open).with('/home/bob/.chef/client.rb', 'w').
+ and_yield(@client_file)
+ File.should_receive(:open).with('/home/bob/.chef/validation.pem', 'w').
+ and_yield(@validation_file)
+ IO.should_receive(:read).and_return('foo_bar_baz')
+ end
+
+ it 'should recursively create the directory' do
+ FileUtils.should_receive(:mkdir_p).with('/home/bob/.chef')
+ @knife.run
+ end
+
+ it 'should write out the config file' do
+ FileUtils.stub!(:mkdir_p)
+ @knife.run
+ @client_file.string.should match /log_level\s+\:info/
+ @client_file.string.should match /log_location\s+STDOUT/
+ @client_file.string.should match /chef_server_url\s+'https\:\/\/chef\.example\.com'/
+ @client_file.string.should match /validation_client_name\s+'chef-validator'/
+ end
+
+ it 'should write out the validation.pem file' do
+ FileUtils.stub!(:mkdir_p)
+ @knife.run
+ @validation_file.string.should match /foo_bar_baz/
+ end
+
+ it 'should print information on what is being configured' do
+ FileUtils.stub!(:mkdir_p)
+ @knife.run
+ @stdout.string.should match /creating client configuration/i
+ @stdout.string.should match /writing client\.rb/i
+ @stdout.string.should match /writing validation\.pem/i
+ end
+ end
+ end
+
+end
diff --git a/spec/unit/knife/configure_spec.rb b/spec/unit/knife/configure_spec.rb
new file mode 100644
index 0000000000..85ee996dd5
--- /dev/null
+++ b/spec/unit/knife/configure_spec.rb
@@ -0,0 +1,229 @@
+require 'spec_helper'
+
+describe Chef::Knife::Configure do
+ before do
+ @original_config = Chef::Config.configuration.dup
+
+ Chef::Log.logger = Logger.new(StringIO.new)
+
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::Configure.new
+ @rest_client = mock("null rest client", :post_rest => { :result => :true })
+ @knife.stub!(:rest).and_return(@rest_client)
+
+ @out = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@out)
+ @knife.config[:config_file] = '/home/you/.chef/knife.rb'
+
+ @in = StringIO.new("\n" * 7)
+ @knife.ui.stub!(:stdin).and_return(@in)
+
+ @err = StringIO.new
+ @knife.ui.stub!(:stderr).and_return(@err)
+
+ @ohai = Ohai::System.new
+ @ohai.stub(:require_plugin)
+ @ohai[:fqdn] = "foo.example.org"
+ Ohai::System.stub!(:new).and_return(@ohai)
+ end
+
+ after do
+ Chef::Config.configuration.replace(@original_config)
+ end
+
+ it "asks the user for the URL of the chef server" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape('Please enter the chef server URL: [http://foo.example.org:4000]'))
+ @knife.chef_server.should == 'http://foo.example.org:4000'
+ end
+
+ it "asks the user for the clientname they want for the new client if -i is specified" do
+ @knife.config[:initial] = true
+ Etc.stub!(:getlogin).and_return("a-new-user")
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape("Please enter a clientname for the new client: [a-new-user]"))
+ @knife.new_client_name.should == Etc.getlogin
+ end
+
+ it "should not ask the user for the clientname they want for the new client if -i and --node_name are specified" do
+ @knife.config[:initial] = true
+ @knife.config[:node_name] = 'testnode'
+ Etc.stub!(:getlogin).and_return("a-new-user")
+ @knife.ask_user_for_config
+ @out.string.should_not match(Regexp.escape("Please enter a clientname for the new client"))
+ @knife.new_client_name.should == 'testnode'
+ end
+
+ it "asks the user for the existing API username or clientname if -i is not specified" do
+ Etc.stub!(:getlogin).and_return("a-new-user")
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape("Please enter an existing username or clientname for the API: [a-new-user]"))
+ @knife.new_client_name.should == Etc.getlogin
+ end
+
+ it "asks the user for the existing admin client's name if -i is specified" do
+ @knife.config[:initial] = true
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape("Please enter the existing admin clientname: [chef-webui]"))
+ @knife.admin_client_name.should == 'chef-webui'
+ end
+
+ it "should not ask the user for the existing admin client's name if -i and --admin-client_name are specified" do
+ @knife.config[:initial] = true
+ @knife.config[:admin_client_name] = 'my-webui'
+ @knife.ask_user_for_config
+ @out.string.should_not match(Regexp.escape("Please enter the existing admin clientname:"))
+ @knife.admin_client_name.should == 'my-webui'
+ end
+
+ it "should not ask the user for the existing admin client's name if -i is not specified" do
+ @knife.ask_user_for_config
+ @out.string.should_not match(Regexp.escape("Please enter the existing admin clientname: [chef-webui]"))
+ @knife.admin_client_name.should_not == 'chef-webui'
+ end
+
+ it "asks the user for the location of the existing admin key if -i is specified" do
+ @knife.config[:initial] = true
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape("Please enter the location of the existing admin client's private key: [/etc/chef/webui.pem]"))
+ if windows?
+ @knife.admin_client_key.should == 'C:/etc/chef/webui.pem'
+ else
+ @knife.admin_client_key.should == '/etc/chef/webui.pem'
+ end
+ end
+
+ it "should not ask the user for the location of the existing admin key if -i and --admin_client_key are specified" do
+ @knife.config[:initial] = true
+ @knife.config[:admin_client_key] = '/home/you/.chef/my-webui.pem'
+ @knife.ask_user_for_config
+ @out.string.should_not match(Regexp.escape("Please enter the location of the existing admin client's private key:"))
+ if windows?
+ @knife.admin_client_key.should == 'C:/home/you/.chef/my-webui.pem'
+ else
+ @knife.admin_client_key.should == '/home/you/.chef/my-webui.pem'
+ end
+ end
+
+ it "should not ask the user for the location of the existing admin key if -i is not specified" do
+ @knife.ask_user_for_config
+ @out.string.should_not match(Regexp.escape("Please enter the location of the existing admin client's private key: [/etc/chef/webui.pem]"))
+ if windows?
+ @knife.admin_client_key.should_not == 'C:/etc//chef/webui.pem'
+ else
+ @knife.admin_client_key.should_not == '/etc/chef/webui.pem'
+ end
+ end
+
+ it "asks the user for the location of a chef repo" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape("Please enter the path to a chef repository (or leave blank):"))
+ @knife.chef_repo.should == ''
+ end
+
+ it "asks the users for the name of the validation client" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape("Please enter the validation clientname: [chef-validator]"))
+ @knife.validation_client_name.should == 'chef-validator'
+ end
+
+ it "should not ask the users for the name of the validation client if --validation_client_name is specified" do
+ @knife.config[:validation_client_name] = 'my-validator'
+ @knife.ask_user_for_config
+ @out.string.should_not match(Regexp.escape("Please enter the validation clientname:"))
+ @knife.validation_client_name.should == 'my-validator'
+ end
+
+ it "asks the users for the location of the validation key" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape("Please enter the location of the validation key: [/etc/chef/validation.pem]"))
+ if windows?
+ @knife.validation_key.should == 'C:/etc/chef/validation.pem'
+ else
+ @knife.validation_key.should == '/etc/chef/validation.pem'
+ end
+ end
+
+ it "should not ask the users for the location of the validation key if --validation_key is specified" do
+ @knife.config[:validation_key] = '/home/you/.chef/my-validation.pem'
+ @knife.ask_user_for_config
+ @out.string.should_not match(Regexp.escape("Please enter the location of the validation key:"))
+ if windows?
+ @knife.validation_key.should == 'C:/home/you/.chef/my-validation.pem'
+ else
+ @knife.validation_key.should == '/home/you/.chef/my-validation.pem'
+ end
+ end
+
+ it "should not ask the user for anything if -i and all other properties are specified" do
+ @knife.config[:initial] = true
+ @knife.config[:chef_server_url] = 'http://localhost:5000'
+ @knife.config[:node_name] = 'testnode'
+ @knife.config[:admin_client_name] = 'my-webui'
+ @knife.config[:admin_client_key] = '/home/you/.chef/my-webui.pem'
+ @knife.config[:validation_client_name] = 'my-validator'
+ @knife.config[:validation_key] = '/home/you/.chef/my-validation.pem'
+ @knife.config[:repository] = ''
+ @knife.config[:client_key] = '/home/you/a-new-user.pem'
+ Etc.stub!(:getlogin).and_return('a-new-user')
+
+ @knife.ask_user_for_config
+ @out.string.should match(/\s*/)
+
+ @knife.new_client_name.should == 'testnode'
+ @knife.chef_server.should == 'http://localhost:5000'
+ @knife.admin_client_name.should == 'my-webui'
+ if windows?
+ @knife.admin_client_key.should == 'C:/home/you/.chef/my-webui.pem'
+ @knife.validation_key.should == 'C:/home/you/.chef/my-validation.pem'
+ @knife.new_client_key.should == 'C:/home/you/a-new-user.pem'
+ else
+ @knife.admin_client_key.should == '/home/you/.chef/my-webui.pem'
+ @knife.validation_key.should == '/home/you/.chef/my-validation.pem'
+ @knife.new_client_key.should == '/home/you/a-new-user.pem'
+ end
+ @knife.validation_client_name.should == 'my-validator'
+ @knife.chef_repo.should == ''
+ end
+
+ it "writes the new data to a config file" do
+ File.stub!(:expand_path).with("/home/you/.chef/knife.rb").and_return("/home/you/.chef/knife.rb")
+ File.stub!(:expand_path).with("/home/you/.chef/#{Etc.getlogin}.pem").and_return("/home/you/.chef/#{Etc.getlogin}.pem")
+ File.stub!(:expand_path).with("/etc/chef/validation.pem").and_return("/etc/chef/validation.pem")
+ File.stub!(:expand_path).with("/etc/chef/webui.pem").and_return("/etc/chef/webui.pem")
+ FileUtils.should_receive(:mkdir_p).with("/home/you/.chef")
+ config_file = StringIO.new
+ ::File.should_receive(:open).with("/home/you/.chef/knife.rb", "w").and_yield config_file
+ @knife.config[:repository] = '/home/you/chef-repo'
+ @knife.run
+ config_file.string.should match(/^node_name[\s]+'#{Etc.getlogin}'$/)
+ config_file.string.should match(%r{^client_key[\s]+'/home/you/.chef/#{Etc.getlogin}.pem'$})
+ config_file.string.should match(/^validation_client_name\s+'chef-validator'$/)
+ config_file.string.should match(%r{^validation_key\s+'/etc/chef/validation.pem'$})
+ config_file.string.should match(%r{^chef_server_url\s+'http://foo.example.org:4000'$})
+ config_file.string.should match(%r{cookbook_path\s+\[ '/home/you/chef-repo/cookbooks' \]})
+ end
+
+ it "creates a new client when given the --initial option" do
+ File.stub!(:expand_path).with("/home/you/.chef/knife.rb").and_return("/home/you/.chef/knife.rb")
+ File.stub!(:expand_path).with("/home/you/.chef/a-new-user.pem").and_return("/home/you/.chef/a-new-user.pem")
+ File.stub!(:expand_path).with("/etc/chef/validation.pem").and_return("/etc/chef/validation.pem")
+ File.stub!(:expand_path).with("/etc/chef/webui.pem").and_return("/etc/chef/webui.pem")
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ client_command = Chef::Knife::ClientCreate.new
+ client_command.should_receive(:run)
+
+ Etc.stub!(:getlogin).and_return("a-new-user")
+
+ Chef::Knife::ClientCreate.stub!(:new).and_return(client_command)
+ FileUtils.should_receive(:mkdir_p).with("/home/you/.chef")
+ ::File.should_receive(:open).with("/home/you/.chef/knife.rb", "w")
+ @knife.config[:initial] = true
+ @knife.run
+ client_command.name_args.should == Array("a-new-user")
+ client_command.config[:admin].should be_true
+ client_command.config[:file].should == "/home/you/.chef/a-new-user.pem"
+ client_command.config[:yes].should be_true
+ client_command.config[:disable_editing].should be_true
+ end
+end
diff --git a/spec/unit/knife/cookbook_bulk_delete_spec.rb b/spec/unit/knife/cookbook_bulk_delete_spec.rb
new file mode 100644
index 0000000000..ced2a9a4e4
--- /dev/null
+++ b/spec/unit/knife/cookbook_bulk_delete_spec.rb
@@ -0,0 +1,87 @@
+#
+# Author:: Stephen Delano (<stephen@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::CookbookBulkDelete do
+ before(:each) do
+ Chef::Log.logger = Logger.new(StringIO.new)
+
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::CookbookBulkDelete.new
+ @knife.config = {:print_after => nil}
+ @knife.name_args = ["."]
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @knife.ui.stub!(:confirm).and_return(true)
+ @cookbooks = Hash.new
+ %w{cheezburger pizza lasagna}.each do |cookbook_name|
+ cookbook = Chef::CookbookVersion.new(cookbook_name)
+ @cookbooks[cookbook_name] = cookbook
+ end
+ @rest = mock("Chef::REST")
+ @rest.stub!(:get_rest).and_return(@cookbooks)
+ @rest.stub!(:delete_rest).and_return(true)
+ @knife.stub!(:rest).and_return(@rest)
+ Chef::CookbookVersion.stub!(:list).and_return(@cookbooks)
+
+ end
+
+
+
+ describe "when there are several cookbooks on the server" do
+ before do
+ @cheezburger = {'cheezburger' => {"url" => "file:///dev/null", "versions" => [{"url" => "file:///dev/null-cheez", "version" => "1.0.0"}]}}
+ @rest.stub!(:get_rest).with('cookbooks/cheezburger').and_return(@cheezburger)
+ @pizza = {'pizza' => {"url" => "file:///dev/null", "versions" => [{"url" => "file:///dev/null-pizza", "version" => "2.0.0"}]}}
+ @rest.stub!(:get_rest).with('cookbooks/pizza').and_return(@pizza)
+ @lasagna = {'lasagna' => {"url" => "file:///dev/null", "versions" => [{"url" => "file:///dev/null-lasagna", "version" => "3.0.0"}]}}
+ @rest.stub!(:get_rest).with('cookbooks/lasagna').and_return(@lasagna)
+ end
+
+ it "should print the cookbooks you are about to delete" do
+ expected = @knife.ui.list(@cookbooks.keys.sort, :columns_down)
+ @knife.run
+ @stdout.string.should match(/#{expected}/)
+ end
+
+ it "should confirm you really want to delete them" do
+ @knife.ui.should_receive(:confirm)
+ @knife.run
+ end
+
+ it "should delete each cookbook" do
+ {"cheezburger" => "1.0.0", "pizza" => "2.0.0", "lasagna" => '3.0.0'}.each do |cookbook_name, version|
+ @rest.should_receive(:delete_rest).with("cookbooks/#{cookbook_name}/#{version}")
+ end
+ @knife.run
+ end
+
+ it "should only delete cookbooks that match the regex" do
+ @knife.name_args = ["cheezburger"]
+ @rest.should_receive(:delete_rest).with('cookbooks/cheezburger/1.0.0')
+ @knife.run
+ end
+ end
+
+ it "should exit if the regex is not provided" do
+ @knife.name_args = []
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+end
diff --git a/spec/unit/knife/cookbook_create_spec.rb b/spec/unit/knife/cookbook_create_spec.rb
new file mode 100644
index 0000000000..dc1d7d7f79
--- /dev/null
+++ b/spec/unit/knife/cookbook_create_spec.rb
@@ -0,0 +1,271 @@
+#
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'tmpdir'
+
+describe Chef::Knife::CookbookCreate do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::CookbookCreate.new
+ @knife.config = {}
+ @knife.name_args = ["foobar"]
+ @stdout = StringIO.new
+ @knife.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+
+ # Fixes CHEF-2579
+ it "should expand the path of the cookbook directory" do
+ File.should_receive(:expand_path).with("~/tmp/monkeypants")
+ @knife.config = {:cookbook_path => "~/tmp/monkeypants"}
+ @knife.stub!(:create_cookbook)
+ @knife.stub!(:create_readme)
+ @knife.stub!(:create_changelog)
+ @knife.stub!(:create_metadata)
+ @knife.run
+ end
+
+ it "should create a new cookbook with default values to copyright name, email, readme format and license if those are not supplied" do
+ @dir = Dir.tmpdir
+ @knife.config = {:cookbook_path => @dir}
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "YOUR_COMPANY_NAME", "none")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "md")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "YOUR_COMPANY_NAME", "YOUR_EMAIL", "none", "md")
+ @knife.run
+ end
+
+ it "should create a new cookbook with specified company name in the copyright section if one is specified" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "md")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "YOUR_EMAIL", "none", "md")
+ @knife.run
+ end
+
+ it "should create a new cookbook with specified copyright name and email if they are specified" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "md")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "none", "md")
+ @knife.run
+ end
+
+ it "should create a new cookbook with specified copyright name and email and license information (true) if they are specified" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => "apachev2"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "apachev2")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "md")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "apachev2", "md")
+ @knife.run
+ end
+
+ it "should create a new cookbook with specified copyright name and email and license information (false) if they are specified" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => false
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "md")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "none", "md")
+ @knife.run
+ end
+
+ it "should create a new cookbook with specified copyright name and email and license information ('false' as string) if they are specified" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => "false"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "md")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "none", "md")
+ @knife.run
+ end
+
+ it "should allow specifying a gpl2 license" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => "gplv2"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "gplv2")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "md")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "gplv2", "md")
+ @knife.run
+ end
+
+ it "should allow specifying a gplv3 license" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => "gplv3"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "gplv3")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "md")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "gplv3", "md")
+ @knife.run
+ end
+
+ it "should allow specifying the mit license" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => "mit"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "md")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "md")
+ @knife.run
+ end
+
+ it "should allow specifying the rdoc readme format" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => "mit",
+ :readme_format => "rdoc"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "rdoc")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "rdoc")
+ @knife.run
+ end
+
+ it "should allow specifying the mkd readme format" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => "mit",
+ :readme_format => "mkd"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "mkd")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "mkd")
+ @knife.run
+ end
+
+ it "should allow specifying the txt readme format" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => "mit",
+ :readme_format => "txt"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "txt")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "txt")
+ @knife.run
+ end
+
+ it "should allow specifying an arbitrary readme format" do
+ @dir = Dir.tmpdir
+ @knife.config = {
+ :cookbook_path => @dir,
+ :cookbook_copyright => "Opscode, Inc",
+ :cookbook_email => "nuo@opscode.com",
+ :cookbook_license => "mit",
+ :readme_format => "foo"
+ }
+ @knife.name_args=["foobar"]
+ @knife.should_receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit")
+ @knife.should_receive(:create_readme).with(@dir, @knife.name_args.first, "foo")
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.should_receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "foo")
+ @knife.run
+ end
+
+ it "should create a CHANGELOG file" do
+ @dir = Dir.tmpdir
+ @knife.should_receive(:create_changelog).with(@dir, @knife.name_args.first)
+ @knife.run
+ end
+
+ context "when the cookbooks path is not specified in the config file nor supplied via parameter" do
+ before do
+ @old_cookbook_path = Chef::Config[:cookbook_path]
+ Chef::Config[:cookbook_path] = nil
+ end
+
+ it "should throw an argument error" do
+ @dir = Dir.tmpdir
+ lambda{@knife.run}.should raise_error(ArgumentError)
+ end
+
+ after do
+ Chef::Config[:cookbook_path] = @old_cookbook_path
+ end
+ end
+
+ end
+end
diff --git a/spec/unit/knife/cookbook_delete_spec.rb b/spec/unit/knife/cookbook_delete_spec.rb
new file mode 100644
index 0000000000..afaa3b69a5
--- /dev/null
+++ b/spec/unit/knife/cookbook_delete_spec.rb
@@ -0,0 +1,239 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::CookbookDelete do
+ before(:each) do
+ @knife = Chef::Knife::CookbookDelete.new
+ @knife.name_args = ['foobar']
+ @knife.cookbook_name = 'foobar'
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @stderr = StringIO.new
+ @knife.ui.stub!(:stderr).and_return(@stderr)
+ end
+
+ describe 'run' do
+ it 'should print usage and exit when a cookbook name is not provided' do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ describe 'when specifying a cookbook name' do
+ it 'should delete the cookbook without a specific version' do
+ @knife.should_receive(:delete_without_explicit_version)
+ @knife.run
+ end
+
+ describe 'and a version' do
+ it 'should delete the specific version of the cookbook' do
+ @knife.name_args << '1.0.0'
+ @knife.should_receive(:delete_explicit_version)
+ @knife.run
+ end
+ end
+
+ describe 'with -a or --all' do
+ it 'should delete all versions of the cookbook' do
+ @knife.config[:all] = true
+ @knife.should_receive(:delete_all_versions)
+ @knife.run
+ end
+ end
+
+ describe 'with -p or --purge' do
+ it 'should prompt to purge the files' do
+ @knife.config[:purge] = true
+ @knife.should_receive(:confirm).
+ with(/.+Are you sure you want to purge files.+/)
+ @knife.should_receive(:delete_without_explicit_version)
+ @knife.run
+ end
+ end
+ end
+ end
+
+ describe 'delete_explicit_version' do
+ it 'should delete the specific cookbook version' do
+ @knife.cookbook_name = 'foobar'
+ @knife.version = '1.0.0'
+ @knife.should_receive(:delete_object).with(Chef::CookbookVersion,
+ 'foobar version 1.0.0',
+ 'cookbook').and_yield()
+ @knife.should_receive(:delete_request).with('cookbooks/foobar/1.0.0')
+ @knife.delete_explicit_version
+ end
+ end
+
+ describe 'delete_all_versions' do
+ it 'should prompt to delete all versions of the cookbook' do
+ @knife.cookbook_name = 'foobar'
+ @knife.should_receive(:confirm).with('Do you really want to delete all versions of foobar')
+ @knife.should_receive(:delete_all_without_confirmation)
+ @knife.delete_all_versions
+ end
+ end
+
+ describe 'delete_all_without_confirmation' do
+ it 'should delete all versions without confirmation' do
+ versions = ['1.0.0', '1.1.0']
+ @knife.should_receive(:available_versions).and_return(versions)
+ versions.each do |v|
+ @knife.should_receive(:delete_version_without_confirmation).with(v)
+ end
+ @knife.delete_all_without_confirmation
+ end
+ end
+
+ describe 'delete_without_explicit_version' do
+ it 'should exit if there are no available versions' do
+ @knife.should_receive(:available_versions).and_return(nil)
+ lambda { @knife.delete_without_explicit_version }.should raise_error(SystemExit)
+ end
+
+ it 'should delete the version if only one is found' do
+ @knife.should_receive(:available_versions).at_least(:once).and_return(['1.0.0'])
+ @knife.should_receive(:delete_explicit_version)
+ @knife.delete_without_explicit_version
+ end
+
+ it 'should ask which version(s) to delete if multiple are found' do
+ @knife.should_receive(:available_versions).at_least(:once).and_return(['1.0.0', '1.1.0'])
+ @knife.should_receive(:ask_which_versions_to_delete).and_return(['1.0.0', '1.1.0'])
+ @knife.should_receive(:delete_versions_without_confirmation).with(['1.0.0', '1.1.0'])
+ @knife.delete_without_explicit_version
+ end
+ end
+
+ describe 'available_versions' do
+ before(:each) do
+ @rest_mock = mock('rest')
+ @knife.should_receive(:rest).and_return(@rest_mock)
+ @cookbook_data = { 'foobar' => { 'versions' => [{'version' => '1.0.0'},
+ {'version' => '1.1.0'},
+ {'version' => '2.0.0'} ]}
+ }
+ end
+
+ it 'should return the list of versions of the cookbook' do
+ @rest_mock.should_receive(:get_rest).with('cookbooks/foobar').and_return(@cookbook_data)
+ @knife.available_versions.should == ['1.0.0', '1.1.0', '2.0.0']
+ end
+
+ it 'should raise if an error other than HTTP 404 is returned' do
+ exception = Net::HTTPServerException.new('500 Internal Server Error', '500')
+ @rest_mock.should_receive(:get_rest).and_raise(exception)
+ lambda { @knife.available_versions }.should raise_error Net::HTTPServerException
+ end
+
+ describe "if the cookbook can't be found" do
+ before(:each) do
+ @rest_mock.should_receive(:get_rest).
+ and_raise(Net::HTTPServerException.new('404 Not Found', '404'))
+ end
+
+ it 'should print an error' do
+ @knife.available_versions
+ @stderr.string.should match /error.+cannot find a cookbook named foobar/i
+ end
+
+ it 'should return nil' do
+ @knife.available_versions.should == nil
+ end
+ end
+ end
+
+ describe 'ask_which_version_to_delete' do
+ before(:each) do
+ @knife.stub!(:available_versions).and_return(['1.0.0', '1.1.0', '2.0.0'])
+ end
+
+ it 'should prompt the user to select a version' do
+ prompt = /Which version\(s\) do you want to delete\?.+1\. foobar 1\.0\.0.+2\. foobar 1\.1\.0.+3\. foobar 2\.0\.0.+4\. All versions.+/m
+ @knife.should_receive(:ask_question).with(prompt).and_return('1')
+ @knife.ask_which_versions_to_delete
+ end
+
+ it "should print an error and exit if a version wasn't specified" do
+ @knife.should_receive(:ask_question).and_return('')
+ @knife.ui.should_receive(:error).with(/no versions specified/i)
+ lambda { @knife.ask_which_versions_to_delete }.should raise_error(SystemExit)
+ end
+
+ it 'should print an error if an invalid choice was selected' do
+ @knife.should_receive(:ask_question).and_return('100')
+ @knife.ui.should_receive(:error).with(/100 is not a valid choice/i)
+ @knife.ask_which_versions_to_delete
+ end
+
+ it 'should return the selected versions' do
+ @knife.should_receive(:ask_question).and_return('1, 3')
+ @knife.ask_which_versions_to_delete.should == ['1.0.0', '2.0.0']
+ end
+
+ it "should return all of the versions if 'all' was selected" do
+ @knife.should_receive(:ask_question).and_return('4')
+ @knife.ask_which_versions_to_delete.should == [:all]
+ end
+ end
+
+ describe 'delete_version_without_confirmation' do
+ it 'should delete the cookbook version' do
+ @knife.should_receive(:delete_request).with('cookbooks/foobar/1.0.0')
+ @knife.delete_version_without_confirmation('1.0.0')
+ end
+
+ it 'should output that the cookbook was deleted' do
+ @knife.stub!(:delete_request)
+ @knife.delete_version_without_confirmation('1.0.0')
+ @stdout.string.should match /deleted cookbook\[foobar\]\[1.0.0\]/im
+ end
+
+ describe 'with --print-after' do
+ it 'should display the cookbook data' do
+ object = ''
+ @knife.config[:print_after] = true
+ @knife.stub!(:delete_request).and_return(object)
+ @knife.should_receive(:format_for_display).with(object)
+ @knife.delete_version_without_confirmation('1.0.0')
+ end
+ end
+ end
+
+ describe 'delete_versions_without_confirmation' do
+ it 'should delete each version without confirmation' do
+ versions = ['1.0.0', '1.1.0']
+ versions.each do |v|
+ @knife.should_receive(:delete_version_without_confirmation).with(v)
+ end
+ @knife.delete_versions_without_confirmation(versions)
+ end
+
+ describe 'with -a or --all' do
+ it 'should delete all versions without confirmation' do
+ versions = [:all]
+ @knife.should_receive(:delete_all_without_confirmation)
+ @knife.delete_versions_without_confirmation(versions)
+ end
+ end
+ end
+
+end
diff --git a/spec/unit/knife/cookbook_download_spec.rb b/spec/unit/knife/cookbook_download_spec.rb
new file mode 100644
index 0000000000..6ae3fbecd2
--- /dev/null
+++ b/spec/unit/knife/cookbook_download_spec.rb
@@ -0,0 +1,217 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::CookbookDownload do
+ before(:each) do
+ @knife = Chef::Knife::CookbookDownload.new
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe 'run' do
+ it 'should print usage and exit when a cookbook name is not provided' do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal).with(/must specify a cookbook name/)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ describe 'with a cookbook name' do
+ before(:each) do
+ @knife.name_args = ['foobar']
+ @knife.config[:download_directory] = '/var/tmp/chef'
+ @rest_mock = mock('rest')
+ @knife.stub(:rest).and_return(@rest_mock)
+
+ @manifest_data = {
+ :recipes => [
+ {'path' => 'recipes/foo.rb',
+ 'url' => 'http://example.org/files/foo.rb'},
+ {'path' => 'recipes/bar.rb',
+ 'url' => 'http://example.org/files/bar.rb'}
+ ],
+ :templates => [
+ {'path' => 'templates/default/foo.erb',
+ 'url' => 'http://example.org/files/foo.erb'},
+ {'path' => 'templates/default/bar.erb',
+ 'url' => 'http://example.org/files/bar.erb'}
+ ],
+ :attributes => [
+ {'path' => 'attributes/default.rb',
+ 'url' => 'http://example.org/files/default.rb'}
+ ]
+ }
+
+ @cookbook_mock = mock('cookbook')
+ @cookbook_mock.stub!(:version).and_return('1.0.0')
+ @cookbook_mock.stub!(:manifest).and_return(@manifest_data)
+ @rest_mock.should_receive(:get_rest).with('cookbooks/foobar/1.0.0').
+ and_return(@cookbook_mock)
+ end
+
+ it 'should determine which version if one was not explicitly specified'do
+ @cookbook_mock.stub!(:manifest).and_return({})
+ @knife.should_receive(:determine_version).and_return('1.0.0')
+ File.should_receive(:exists?).with('/var/tmp/chef/foobar-1.0.0').and_return(false)
+ Chef::CookbookVersion.stub!(:COOKBOOK_SEGEMENTS).and_return([])
+ @knife.run
+ end
+
+ describe 'and a version' do
+ before(:each) do
+ @knife.name_args << '1.0.0'
+ @files = @manifest_data.values.map { |v| v.map { |i| i['path'] } }.flatten.uniq
+ @files_mocks = {}
+ @files.map { |f| File.basename(f) }.flatten.uniq.each do |f|
+ @files_mocks[f] = mock("#{f}_mock")
+ @files_mocks[f].stub!(:path).and_return("/var/tmp/#{f}")
+ end
+ end
+
+ it 'should print an error and exit if the cookbook download directory already exists' do
+ File.should_receive(:exists?).with('/var/tmp/chef/foobar-1.0.0').and_return(true)
+ @knife.ui.should_receive(:fatal).with(/\/var\/tmp\/chef\/foobar-1\.0\.0 exists/i)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ describe 'when downloading the cookbook' do
+ before(:each) do
+ @files.map { |f| File.dirname(f) }.flatten.uniq.each do |dir|
+ FileUtils.should_receive(:mkdir_p).with("/var/tmp/chef/foobar-1.0.0/#{dir}").
+ at_least(:once)
+ end
+
+ @files_mocks.each_pair do |file, mock|
+ @rest_mock.should_receive(:get_rest).with("http://example.org/files/#{file}", true).
+ and_return(mock)
+ end
+
+ @rest_mock.should_receive(:sign_on_redirect=).with(false).at_least(:once)
+ @files.each do |f|
+ FileUtils.should_receive(:mv).
+ with("/var/tmp/#{File.basename(f)}", "/var/tmp/chef/foobar-1.0.0/#{f}")
+ end
+ end
+
+ it "should download the cookbook when the cookbook download directory doesn't exist" do
+ File.should_receive(:exists?).with('/var/tmp/chef/foobar-1.0.0').and_return(false)
+ @knife.run
+ ['attributes', 'recipes', 'templates'].each do |segment|
+ @stdout.string.should match /downloading #{segment}/im
+ end
+ @stdout.string.should match /downloading foobar cookbook version 1\.0\.0/im
+ @stdout.string.should match /cookbook downloaded to \/var\/tmp\/chef\/foobar-1\.0\.0/im
+ end
+
+ describe 'with -f or --force' do
+ it 'should remove the existing the cookbook download directory if it exists' do
+ @knife.config[:force] = true
+ File.should_receive(:exists?).with('/var/tmp/chef/foobar-1.0.0').and_return(true)
+ FileUtils.should_receive(:rm_rf).with('/var/tmp/chef/foobar-1.0.0')
+ @knife.run
+ end
+ end
+ end
+
+ end
+ end
+
+ end
+
+ describe 'determine_version' do
+ it 'should return and set the version if there is only one version' do
+ @knife.should_receive(:available_versions).at_least(:once).and_return(['1.0.0'])
+ @knife.determine_version.should == '1.0.0'
+ @knife.version.should == '1.0.0'
+ end
+
+ it 'should ask which version to download and return it if there is more than one' do
+ @knife.should_receive(:available_versions).and_return(['1.0.0', '2.0.0'])
+ @knife.should_receive(:ask_which_version).and_return('1.0.0')
+ @knife.determine_version.should == '1.0.0'
+ end
+
+ describe 'with -N or --latest' do
+ it 'should return and set the version to the latest version' do
+ @knife.config[:latest] = true
+ @knife.should_receive(:available_versions).at_least(:once).
+ and_return(['1.0.0', '2.0.0', '1.1.0'])
+ @knife.determine_version
+ @knife.version.to_s.should == '2.0.0'
+ end
+ end
+ end
+
+ describe 'available_versions' do
+ before(:each) do
+ @knife.cookbook_name = 'foobar'
+ end
+
+ it 'should return the available vesions' do
+ Chef::CookbookVersion.should_receive(:available_versions).
+ with('foobar').
+ and_return(['1.1.0', '2.0.0', '1.0.0'])
+ @knife.available_versions.should == [Chef::Version.new('1.0.0'),
+ Chef::Version.new('1.1.0'),
+ Chef::Version.new('2.0.0')]
+ end
+
+ it 'should avoid multiple API calls to the server' do
+ Chef::CookbookVersion.should_receive(:available_versions).
+ once.
+ with('foobar').
+ and_return(['1.1.0', '2.0.0', '1.0.0'])
+ @knife.available_versions
+ @knife.available_versions
+ end
+ end
+
+ describe 'ask_which_version' do
+ before(:each) do
+ @knife.cookbook_name = 'foobar'
+ @knife.stub!(:available_versions).and_return(['1.0.0', '1.1.0', '2.0.0'])
+ end
+
+ it 'should prompt the user to select a version' do
+ prompt = /Which version do you want to download\?.+1\. foobar 1\.0\.0.+2\. foobar 1\.1\.0.+3\. foobar 2\.0\.0.+/m
+ @knife.should_receive(:ask_question).with(prompt).and_return('1')
+ @knife.ask_which_version
+ end
+
+ it "should set the version to the user's selection" do
+ @knife.should_receive(:ask_question).and_return('1')
+ @knife.ask_which_version
+ @knife.version.should == '1.0.0'
+ end
+
+ it "should print an error and exit if a version wasn't specified" do
+ @knife.should_receive(:ask_question).and_return('')
+ @knife.ui.should_receive(:error).with(/is not a valid value/i)
+ lambda { @knife.ask_which_version }.should raise_error(SystemExit)
+ end
+
+ it 'should print an error if an invalid choice was selected' do
+ @knife.should_receive(:ask_question).and_return('100')
+ @knife.ui.should_receive(:error).with(/'100' is not a valid value/i)
+ lambda { @knife.ask_which_version }.should raise_error(SystemExit)
+ end
+ end
+
+end
diff --git a/spec/unit/knife/cookbook_list_spec.rb b/spec/unit/knife/cookbook_list_spec.rb
new file mode 100644
index 0000000000..db6f061bd1
--- /dev/null
+++ b/spec/unit/knife/cookbook_list_spec.rb
@@ -0,0 +1,88 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::CookbookList do
+ before do
+ @knife = Chef::Knife::CookbookList.new
+ @rest_mock = mock('rest')
+ @knife.stub!(:rest).and_return(@rest_mock)
+ @cookbook_names = ['apache2', 'mysql']
+ @base_url = 'https://server.example.com/cookbooks'
+ @cookbook_data = {}
+ @cookbook_names.each do |item|
+ @cookbook_data[item] = {'url' => "#{@base_url}/#{item}",
+ 'versions' => [{'version' => '1.0.1',
+ 'url' => "#{@base_url}/#{item}/1.0.1"}]}
+ end
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe 'run' do
+ it 'should display the latest version of the cookbooks' do
+ @rest_mock.should_receive(:get_rest).with('/cookbooks?num_versions=1').
+ and_return(@cookbook_data)
+ @knife.run
+ @cookbook_names.each do |item|
+ @stdout.string.should match /#{item}\s+1\.0\.1/
+ end
+ end
+
+ it 'should query cookbooks for the configured environment' do
+ @knife.config[:environment] = 'production'
+ @rest_mock.should_receive(:get_rest).
+ with('/environments/production/cookbooks?num_versions=1').
+ and_return(@cookbook_data)
+ @knife.run
+ end
+
+ describe 'with -w or --with-uri' do
+ it 'should display the cookbook uris' do
+ @knife.config[:with_uri] = true
+ @rest_mock.stub(:get_rest).and_return(@cookbook_data)
+ @knife.run
+ @cookbook_names.each do |item|
+ pattern = /#{Regexp.escape(@cookbook_data[item]['versions'].first['url'])}/
+ @stdout.string.should match pattern
+ end
+ end
+ end
+
+ describe 'with -a or --all' do
+ before do
+ @cookbook_names.each do |item|
+ @cookbook_data[item]['versions'] << {'version' => '1.0.0',
+ 'url' => "#{@base_url}/#{item}/1.0.0"}
+ end
+ end
+
+ it 'should display all versions of the cookbooks' do
+ @knife.config[:all_versions] = true
+ @rest_mock.should_receive(:get_rest).with('/cookbooks?num_versions=all').
+ and_return(@cookbook_data)
+ @knife.run
+ @cookbook_names.each do |item|
+ @stdout.string.should match /#{item}\s+1\.0\.1\s+1\.0\.0/
+ end
+ end
+ end
+
+ end
+end
diff --git a/spec/unit/knife/cookbook_metadata_from_file_spec.rb b/spec/unit/knife/cookbook_metadata_from_file_spec.rb
new file mode 100644
index 0000000000..60555d89dc
--- /dev/null
+++ b/spec/unit/knife/cookbook_metadata_from_file_spec.rb
@@ -0,0 +1,65 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::CookbookMetadataFromFile do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @src = File.expand_path(File.join(CHEF_SPEC_DATA, "metadata", "quick_start", "metadata.rb"))
+ @tgt = File.expand_path(File.join(CHEF_SPEC_DATA, "metadata", "quick_start", "metadata.json"))
+ @knife = Chef::Knife::CookbookMetadataFromFile.new
+ @knife.name_args = [ @src ]
+ @knife.stub!(:to_json_pretty).and_return(true)
+ @md = Chef::Cookbook::Metadata.new
+ Chef::Cookbook::Metadata.stub(:new).and_return(@md)
+ $stdout.stub!(:write)
+ end
+
+ after do
+ if File.exists?(@tgt)
+ File.unlink(@tgt)
+ end
+ end
+
+ describe "run" do
+ it "should determine cookbook name from path" do
+ @md.should_receive(:name).with()
+ @md.should_receive(:name).with("quick_start")
+ @knife.run
+ end
+
+ it "should load the metadata source" do
+ @md.should_receive(:from_file).with(@src)
+ @knife.run
+ end
+
+ it "should write out the metadata to the correct location" do
+ File.should_receive(:open).with(@tgt, "w")
+ @knife.run
+ end
+
+ it "should generate json from the metadata" do
+ Chef::JSONCompat.should_receive(:to_json_pretty).with(@md)
+ @knife.run
+ end
+
+ end
+end
diff --git a/spec/unit/knife/cookbook_metadata_spec.rb b/spec/unit/knife/cookbook_metadata_spec.rb
new file mode 100644
index 0000000000..c664326a3d
--- /dev/null
+++ b/spec/unit/knife/cookbook_metadata_spec.rb
@@ -0,0 +1,179 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2011 Thomas Bishop
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::CookbookMetadata do
+ before(:each) do
+ @knife = Chef::Knife::CookbookMetadata.new
+ @knife.name_args = ['foobar']
+ @cookbook_dir = Dir.mktmpdir
+ @json_data = '{ "version": "1.0.0" }'
+ @stdout = StringIO.new
+ @stderr = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @knife.ui.stub!(:stderr).and_return(@stderr)
+ end
+
+ describe 'run' do
+ it 'should print an error and exit if a cookbook name was not provided' do
+ @knife.name_args = []
+ @knife.ui.should_receive(:error).with(/you must specify the cookbook.+use the --all/i)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it 'should print an error and exit if an empty cookbook name was provided' do
+ @knife.name_args = ['']
+ @knife.ui.should_receive(:error).with(/you must specify the cookbook.+use the --all/i)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it 'should generate the metadata for the cookbook' do
+ @knife.should_receive(:generate_metadata).with('foobar')
+ @knife.run
+ end
+
+ describe 'with -a or --all' do
+ before(:each) do
+ @knife.config[:all] = true
+ @foo = Chef::CookbookVersion.new('foo')
+ @foo.version = '1.0.0'
+ @bar = Chef::CookbookVersion.new('bar')
+ @bar.version = '2.0.0'
+ @cookbook_loader = {
+ "foo" => @foo,
+ "bar" => @bar
+ }
+ @cookbook_loader.should_receive(:load_cookbooks).and_return(@cookbook_loader)
+ @knife.should_receive(:generate_metadata).with('foo')
+ @knife.should_receive(:generate_metadata).with('bar')
+ end
+
+ it 'should generate the metadata for each cookbook' do
+ Chef::Config[:cookbook_path] = @cookbook_dir
+ Chef::CookbookLoader.should_receive(:new).with(@cookbook_dir).and_return(@cookbook_loader)
+ @knife.run
+ end
+
+ describe 'and with -o or --cookbook-path' do
+ it 'should look in the provided path and generate cookbook metadata' do
+ @knife.config[:cookbook_path] = '/opt/chef/cookbooks'
+ Chef::CookbookLoader.should_receive(:new).with('/opt/chef/cookbooks').and_return(@cookbook_loader)
+ @knife.run
+ end
+ end
+ end
+
+ end
+
+ describe 'generate_metadata' do
+ before(:each) do
+ @knife.config[:cookbook_path] = @cookbook_dir
+ File.stub!(:expand_path).with("#{@cookbook_dir}/foobar/metadata.rb").
+ and_return("#{@cookbook_dir}/foobar/metadata.rb")
+ end
+
+ it 'should generate the metadata from metadata.rb if it exists' do
+ File.should_receive(:exists?).with("#{@cookbook_dir}/foobar/metadata.rb").
+ and_return(true)
+ @knife.should_receive(:generate_metadata_from_file).with('foobar', "#{@cookbook_dir}/foobar/metadata.rb")
+ @knife.run
+ end
+
+ it 'should validate the metadata json if metadata.rb does not exist' do
+ File.should_receive(:exists?).with("#{@cookbook_dir}/foobar/metadata.rb").
+ and_return(false)
+ @knife.should_receive(:validate_metadata_json).with(@cookbook_dir, 'foobar')
+ @knife.run
+ end
+ end
+
+ describe 'generate_metadata_from_file' do
+ before(:each) do
+ @metadata_mock = mock('metadata')
+ @json_file_mock = mock('json_file')
+ end
+
+ it 'should generate the metatdata json from metatdata.rb' do
+ Chef::Cookbook::Metadata.stub!(:new).and_return(@metadata_mock)
+ @metadata_mock.should_receive(:name).with('foobar')
+ @metadata_mock.should_receive(:from_file).with("#{@cookbook_dir}/foobar/metadata.rb")
+ File.should_receive(:open).with("#{@cookbook_dir}/foobar/metadata.json", 'w').
+ and_yield(@json_file_mock)
+ @json_file_mock.should_receive(:write).with(@json_data)
+ Chef::JSONCompat.should_receive(:to_json_pretty).with(@metadata_mock).
+ and_return(@json_data)
+ @knife.generate_metadata_from_file('foobar', "#{@cookbook_dir}/foobar/metadata.rb")
+ @stdout.string.should match /generating metadata for foobar from #{@cookbook_dir}\/foobar\/metadata\.rb/im
+ end
+
+ { Chef::Exceptions::ObsoleteDependencySyntax => 'obsolote dependency',
+ Chef::Exceptions::InvalidVersionConstraint => 'invalid version constraint'
+ }.each_pair do |klass, description|
+ it "should print an error and exit when an #{description} syntax exception is encountered" do
+ exception = klass.new("#{description} blah")
+ Chef::Cookbook::Metadata.stub!(:new).and_raise(exception)
+ lambda {
+ @knife.generate_metadata_from_file('foobar', "#{@cookbook_dir}/foobar/metadata.rb")
+ }.should raise_error(SystemExit)
+ @stderr.string.should match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
+ @stderr.string.should match /in #{@cookbook_dir}\/foobar\/metadata\.rb/im
+ @stderr.string.should match /#{description} blah/im
+ end
+ end
+ end
+
+ describe 'validate_metadata_json' do
+ it 'should validate the metadata json' do
+ File.should_receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json").
+ and_return(true)
+ IO.should_receive(:read).with("#{@cookbook_dir}/foobar/metadata.json").
+ and_return(@json_data)
+ Chef::Cookbook::Metadata.should_receive(:validate_json).with(@json_data)
+ @knife.validate_metadata_json(@cookbook_dir, 'foobar')
+ end
+
+ it 'should not try to validate the metadata json if the file does not exist' do
+ File.should_receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json").
+ and_return(false)
+ IO.should_not_receive(:read)
+ Chef::Cookbook::Metadata.should_not_receive(:validate_json)
+ @knife.validate_metadata_json(@cookbook_dir, 'foobar')
+ end
+
+ { Chef::Exceptions::ObsoleteDependencySyntax => 'obsolote dependency',
+ Chef::Exceptions::InvalidVersionConstraint => 'invalid version constraint'
+ }.each_pair do |klass, description|
+ it "should print an error and exit when an #{description} syntax exception is encountered" do
+ File.should_receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json").
+ and_return(true)
+ IO.should_receive(:read).with("#{@cookbook_dir}/foobar/metadata.json").
+ and_return(@json_data)
+ exception = klass.new("#{description} blah")
+ Chef::Cookbook::Metadata.stub!(:validate_json).and_raise(exception)
+ lambda {
+ @knife.validate_metadata_json(@cookbook_dir, 'foobar')
+ }.should raise_error(SystemExit)
+ @stderr.string.should match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
+ @stderr.string.should match /in #{@cookbook_dir}\/foobar\/metadata\.json/im
+ @stderr.string.should match /#{description} blah/im
+ end
+ end
+ end
+
+end
diff --git a/spec/unit/knife/cookbook_show_spec.rb b/spec/unit/knife/cookbook_show_spec.rb
new file mode 100644
index 0000000000..2f2d841fea
--- /dev/null
+++ b/spec/unit/knife/cookbook_show_spec.rb
@@ -0,0 +1,223 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, eersion 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.
+#
+
+# rename to cookbook not coookbook
+require 'spec_helper'
+
+describe Chef::Knife::CookbookShow do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::CookbookShow.new
+ @knife.config = { }
+ @knife.name_args = [ "cookbook_name" ]
+ @rest = mock(Chef::REST)
+ @knife.stub!(:rest).and_return(@rest)
+ @knife.stub!(:pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
+ end
+
+ describe "run" do
+ describe "with 0 arguments: help" do
+ it 'should should print usage and exit when given no arguments' do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+ end
+
+ describe "with 1 argument: versions" do
+ before(:each) do
+ @response = {
+ "cookbook_name" => {
+ "url" => "http://url/cookbooks/cookbook_name",
+ "versions" => [
+ { "version" => "0.10.0", "url" => "http://url/cookbooks/cookbook_name/0.10.0" },
+ { "version" => "0.9.0", "url" => "http://url/cookbookx/cookbook_name/0.9.0" },
+ { "version" => "0.8.0", "url" => "http://url/cookbooks/cookbook_name/0.8.0" }
+ ]
+ }
+ }
+ end
+
+ it "should show the raw cookbook data" do
+ @rest.should_receive(:get_rest).with("cookbooks/cookbook_name").and_return(@response)
+ @knife.should_receive(:format_cookbook_list_for_display).with(@response)
+ @knife.run
+ end
+
+ it "should respect the user-supplied environment" do
+ @knife.config[:environment] = "foo"
+ @rest.should_receive(:get_rest).with("environments/foo/cookbooks/cookbook_name").and_return(@response)
+ @knife.should_receive(:format_cookbook_list_for_display).with(@response)
+ @knife.run
+ end
+ end
+
+ describe "with 2 arguments: name and version" do
+ before(:each) do
+ @knife.name_args << "0.1.0"
+ @response = { "0.1.0" => { "recipes" => {"default.rb" => ""} } }
+ end
+
+ it "should show the specific part of a cookbook" do
+ @rest.should_receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@response)
+ @knife.should_receive(:output).with(@response)
+ @knife.run
+ end
+ end
+
+ describe "with 3 arguments: name, version, and segment" do
+ before(:each) do
+ @knife.name_args = [ "cookbook_name", "0.1.0", "recipes" ]
+ @cookbook_response = Chef::CookbookVersion.new("cookbook_name")
+ @manifest = {
+ "recipes" => [
+ {
+ :name => "default.rb",
+ :path => "recipes/default.rb",
+ :checksum => "1234",
+ :url => "http://example.org/files/default.rb"
+ }
+ ]
+ }
+ @cookbook_response.manifest = @manifest
+ @response = {"name"=>"default.rb", "url"=>"http://example.org/files/default.rb", "checksum"=>"1234", "path"=>"recipes/default.rb"}
+ end
+
+ it "should print the json of the part" do
+ @rest.should_receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
+ @knife.should_receive(:output).with(@cookbook_response.manifest["recipes"])
+ @knife.run
+ end
+ end
+
+ describe "with 4 arguments: name, version, segment and filename" do
+ before(:each) do
+ @knife.name_args = [ "cookbook_name", "0.1.0", "recipes", "default.rb" ]
+ @cookbook_response = Chef::CookbookVersion.new("cookbook_name")
+ @cookbook_response.manifest = {
+ "recipes" => [
+ {
+ :name => "default.rb",
+ :path => "recipes/default.rb",
+ :checksum => "1234",
+ :url => "http://example.org/files/default.rb"
+ }
+ ]
+ }
+ @response = "Example recipe text"
+ end
+
+ it "should print the raw result of the request (likely a file!)" do
+ @rest.should_receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
+ @rest.should_receive(:get_rest).with("http://example.org/files/default.rb", true).and_return(StringIO.new(@response))
+ @knife.should_receive(:pretty_print).with(@response)
+ @knife.run
+ end
+ end
+
+ describe "with 4 arguments: name, version, segment and filename -- with specificity" do
+ before(:each) do
+ @knife.name_args = [ "cookbook_name", "0.1.0", "files", "afile.rb" ]
+ @cookbook_response = Chef::CookbookVersion.new("cookbook_name")
+ @cookbook_response.manifest = {
+ "files" => [
+ {
+ :name => "afile.rb",
+ :path => "files/host-examplehost.example.org/afile.rb",
+ :checksum => "1111",
+ :specificity => "host-examplehost.example.org",
+ :url => "http://example.org/files/1111"
+ },
+ {
+ :name => "afile.rb",
+ :path => "files/ubuntu-9.10/afile.rb",
+ :checksum => "2222",
+ :specificity => "ubuntu-9.10",
+ :url => "http://example.org/files/2222"
+ },
+ {
+ :name => "afile.rb",
+ :path => "files/ubuntu/afile.rb",
+ :checksum => "3333",
+ :specificity => "ubuntu",
+ :url => "http://example.org/files/3333"
+ },
+ {
+ :name => "afile.rb",
+ :path => "files/default/afile.rb",
+ :checksum => "4444",
+ :specificity => "default",
+ :url => "http://example.org/files/4444"
+ },
+ ]
+ }
+
+ @response = "Example recipe text"
+ end
+
+ describe "with --fqdn" do
+ it "should pass the fqdn" do
+ @knife.config[:platform] = "example_platform"
+ @knife.config[:platform_version] = "1.0"
+ @knife.config[:fqdn] = "examplehost.example.org"
+ @rest.should_receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
+ @rest.should_receive(:get_rest).with("http://example.org/files/1111", true).and_return(StringIO.new(@response))
+ @knife.should_receive(:pretty_print).with(@response)
+ @knife.run
+ end
+ end
+
+ describe "and --platform" do
+ it "should pass the platform" do
+ @knife.config[:platform] = "ubuntu"
+ @knife.config[:platform_version] = "1.0"
+ @knife.config[:fqdn] = "differenthost.example.org"
+ @rest.should_receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
+ @rest.should_receive(:get_rest).with("http://example.org/files/3333", true).and_return(StringIO.new(@response))
+ @knife.should_receive(:pretty_print).with(@response)
+ @knife.run
+ end
+ end
+
+ describe "and --platform-version" do
+ it "should pass the platform" do
+ @knife.config[:platform] = "ubuntu"
+ @knife.config[:platform_version] = "9.10"
+ @knife.config[:fqdn] = "differenthost.example.org"
+ @rest.should_receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
+ @rest.should_receive(:get_rest).with("http://example.org/files/2222", true).and_return(StringIO.new(@response))
+ @knife.should_receive(:pretty_print).with(@response)
+ @knife.run
+ end
+ end
+
+ describe "with none of the arguments, it should use the default" do
+ it "should pass them all" do
+ @rest.should_receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
+ @rest.should_receive(:get_rest).with("http://example.org/files/4444", true).and_return(StringIO.new(@response))
+ @knife.should_receive(:pretty_print).with(@response)
+ @knife.run
+ end
+ end
+
+ end
+ end
+end
+
diff --git a/spec/unit/knife/cookbook_site_download_spec.rb b/spec/unit/knife/cookbook_site_download_spec.rb
new file mode 100644
index 0000000000..a3d43c5b4a
--- /dev/null
+++ b/spec/unit/knife/cookbook_site_download_spec.rb
@@ -0,0 +1,151 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright (c) 2012 Thomas Bishop
+# 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 File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
+
+describe Chef::Knife::CookbookSiteDownload do
+
+ describe 'run' do
+ before do
+ @knife = Chef::Knife::CookbookSiteDownload.new
+ @knife.name_args = ['apache2']
+ @noauth_rest = mock 'no auth rest'
+ @stdout = StringIO.new
+ @cookbook_api_url = 'http://cookbooks.opscode.com/api/v1/cookbooks'
+ @version = '1.0.2'
+ @version_us = @version.gsub '.', '_'
+ @current_data = { 'deprecated' => false,
+ 'latest_version' => "#{@cookbook_api_url}/apache2/versions/#{@version_us}",
+ 'replacement' => 'other_apache2' }
+
+ @knife.ui.stub(:stdout).and_return(@stdout)
+ @knife.stub(:noauth_rest).and_return(@noauth_rest)
+ @noauth_rest.should_receive(:get_rest).
+ with("#{@cookbook_api_url}/apache2").
+ and_return(@current_data)
+ end
+
+ context 'when the cookbook is deprecated and not forced' do
+ before do
+ @current_data['deprecated'] = true
+ end
+
+ it 'should warn with info about the replacement' do
+ @knife.ui.should_receive(:warn).
+ with(/.+deprecated.+replaced by other_apache2.+/i)
+ @knife.ui.should_receive(:warn).
+ with(/use --force.+download.+/i)
+ @knife.run
+ end
+ end
+
+ context 'when' do
+ before do
+ @cookbook_data = { 'version' => @version,
+ 'file' => "http://example.com/apache2_#{@version_us}.tgz" }
+ @temp_file = stub :path => "/tmp/apache2_#{@version_us}.tgz"
+ @file = File.join(Dir.pwd, "apache2-#{@version}.tar.gz")
+
+ @noauth_rest.should_receive(:sign_on_redirect=).with(false)
+ end
+
+ context 'downloading the latest version' do
+ before do
+ @noauth_rest.should_receive(:get_rest).
+ with(@current_data['latest_version']).
+ and_return(@cookbook_data)
+ @noauth_rest.should_receive(:get_rest).
+ with(@cookbook_data['file'], true).
+ and_return(@temp_file)
+ end
+
+ context 'and it is deprecated and with --force' do
+ before do
+ @current_data['deprecated'] = true
+ @knife.config[:force] = true
+ end
+
+ it 'should download the latest version' do
+ @knife.ui.should_receive(:warn).
+ with(/.+deprecated.+replaced by other_apache2.+/i)
+ FileUtils.should_receive(:cp).with(@temp_file.path, @file)
+ @knife.run
+ @stdout.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i
+ @stdout.string.should match /cookbook save.+#{Regexp.escape(@file)}/i
+ end
+
+ end
+
+ it 'should download the latest version' do
+ FileUtils.should_receive(:cp).with(@temp_file.path, @file)
+ @knife.run
+ @stdout.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i
+ @stdout.string.should match /cookbook save.+#{Regexp.escape(@file)}/i
+ end
+
+ context 'with -f or --file' do
+ before do
+ @file = '/opt/chef/cookbooks/apache2.tar.gz'
+ @knife.config[:file] = @file
+ FileUtils.should_receive(:cp).with(@temp_file.path, @file)
+ end
+
+ it 'should download the cookbook to the desired file' do
+ @knife.run
+ @stdout.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i
+ @stdout.string.should match /cookbook save.+#{Regexp.escape(@file)}/i
+ end
+ end
+
+ it 'should provide an accessor to the version' do
+ FileUtils.stub(:cp).and_return(true)
+ @knife.version.should == @version
+ @knife.run
+ end
+ end
+
+ context 'downloading a cookbook of a specific version' do
+ before do
+ @version = '1.0.1'
+ @version_us = @version.gsub '.', '_'
+ @cookbook_data = { 'version' => @version,
+ 'file' => "http://example.com/apache2_#{@version_us}.tgz" }
+ @temp_file = stub :path => "/tmp/apache2_#{@version_us}.tgz"
+ @file = File.join(Dir.pwd, "apache2-#{@version}.tar.gz")
+ @knife.name_args << @version
+ end
+
+ it 'should download the desired version' do
+ @noauth_rest.should_receive(:get_rest).
+ with("#{@cookbook_api_url}/apache2/versions/#{@version_us}").
+ and_return(@cookbook_data)
+ @noauth_rest.should_receive(:get_rest).
+ with(@cookbook_data['file'], true).
+ and_return(@temp_file)
+ FileUtils.should_receive(:cp).with(@temp_file.path, @file)
+ @knife.run
+ @stdout.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i
+ @stdout.string.should match /cookbook save.+#{Regexp.escape(@file)}/i
+ end
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/unit/knife/cookbook_site_install_spec.rb b/spec/unit/knife/cookbook_site_install_spec.rb
new file mode 100644
index 0000000000..2ec87b8d16
--- /dev/null
+++ b/spec/unit/knife/cookbook_site_install_spec.rb
@@ -0,0 +1,138 @@
+#
+# Author:: Steven Danna (<steve@opscode.com>)
+# Copyright:: Copyright (c) 2011 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+
+describe Chef::Knife::CookbookSiteInstall do
+ before(:each) do
+ require 'chef/knife/core/cookbook_scm_repo'
+ @knife = Chef::Knife::CookbookSiteInstall.new
+ @knife.config = {}
+ if Chef::Platform.windows?
+ @install_path = 'C:/tmp/chef'
+ else
+ @install_path = '/var/tmp/chef'
+ end
+ @knife.config[:cookbook_path] = [ @install_path ]
+
+ @stdout = StringIO.new
+ @stderr = StringIO.new
+ @knife.stub!(:stderr).and_return(@stdout)
+ @knife.stub!(:stdout).and_return(@stdout)
+
+ #Assume all external commands would have succeed. :(
+ File.stub!(:unlink)
+ File.stub!(:rmtree)
+ @knife.stub!(:shell_out!).and_return(true)
+
+ #CookbookSiteDownload Stup
+ @downloader = {}
+ @knife.stub!(:download_cookbook_to).and_return(@downloader)
+ @downloader.stub!(:version).and_return do
+ if @knife.name_args.size == 2
+ @knife.name_args[1]
+ else
+ "0.3.0"
+ end
+ end
+
+ #Stubs for CookbookSCMRepo
+ @repo = stub(:sanity_check => true, :reset_to_default_state => true,
+ :prepare_to_import => true, :finalize_updates_to => true,
+ :merge_updates_from => true)
+ Chef::Knife::CookbookSCMRepo.stub!(:new).and_return(@repo)
+ end
+
+
+ describe "run" do
+
+ it "should return an error if a cookbook name is not provided" do
+ @knife.name_args = []
+ @knife.ui.should_receive(:error).with("Please specify a cookbook to download and install.")
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it "should return an error if more than two arguments are given" do
+ @knife.name_args = ["foo", "bar", "baz"]
+ @knife.ui.should_receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it "should return an error if the second argument is not a version" do
+ @knife.name_args = ["getting-started", "1pass"]
+ @knife.ui.should_receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it "should return an error if the second argument is a four-digit version" do
+ @knife.name_args = ["getting-started", "0.0.0.1"]
+ @knife.ui.should_receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it "should return an error if the second argument is a one-digit version" do
+ @knife.name_args = ["getting-started", "1"]
+ @knife.ui.should_receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+
+ it "should install the specified version if second argument is a three-digit version" do
+ @knife.name_args = ["getting-started", "0.1.0"]
+ @knife.config[:no_deps] = true
+ upstream_file = File.join(@install_path, "getting-started.tar.gz")
+ @knife.should_receive(:download_cookbook_to).with(upstream_file)
+ @knife.should_receive(:extract_cookbook).with(upstream_file, "0.1.0")
+ @knife.should_receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
+ @repo.should_receive(:merge_updates_from).with("getting-started", "0.1.0")
+ @knife.run
+ end
+
+ it "should install the specified version if second argument is a two-digit version" do
+ @knife.name_args = ["getting-started", "0.1"]
+ @knife.config[:no_deps] = true
+ upstream_file = File.join(@install_path, "getting-started.tar.gz")
+ @knife.should_receive(:download_cookbook_to).with(upstream_file)
+ @knife.should_receive(:extract_cookbook).with(upstream_file, "0.1")
+ @knife.should_receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
+ @repo.should_receive(:merge_updates_from).with("getting-started", "0.1")
+ @knife.run
+ end
+
+ it "should install the latest version if only a cookbook name is given" do
+ @knife.name_args = ["getting-started"]
+ @knife.config[:no_deps] = true
+ upstream_file = File.join(@install_path, "getting-started.tar.gz")
+ @knife.should_receive(:download_cookbook_to).with(upstream_file)
+ @knife.should_receive(:extract_cookbook).with(upstream_file, "0.3.0")
+ @knife.should_receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
+ @repo.should_receive(:merge_updates_from).with("getting-started", "0.3.0")
+ @knife.run
+ end
+
+ it "should not create/reset git branches if use_current_branch is set" do
+ @knife.name_args = ["getting-started"]
+ @knife.config[:use_current_branch] = true
+ @knife.config[:no_deps] = true
+ upstream_file = File.join(@install_path, "getting-started.tar.gz")
+ @repo.should_not_receive(:prepare_to_import)
+ @repo.should_not_receive(:reset_to_default_state)
+ @knife.run
+ end
+ end
+end
diff --git a/spec/unit/knife/cookbook_site_share_spec.rb b/spec/unit/knife/cookbook_site_share_spec.rb
new file mode 100644
index 0000000000..3b912af0c5
--- /dev/null
+++ b/spec/unit/knife/cookbook_site_share_spec.rb
@@ -0,0 +1,146 @@
+#
+# Author:: Stephen Delano (<stephen@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/cookbook_uploader'
+require 'chef/cookbook_site_streaming_uploader'
+
+describe Chef::Knife::CookbookSiteShare do
+
+ before(:each) do
+ @knife = Chef::Knife::CookbookSiteShare.new
+ @knife.name_args = ['cookbook_name', 'AwesomeSausage']
+
+ @cookbook = Chef::CookbookVersion.new('cookbook_name')
+
+ @cookbook_loader = mock('Chef::CookbookLoader')
+ @cookbook_loader.stub!(:cookbook_exists?).and_return(true)
+ @cookbook_loader.stub!(:[]).and_return(@cookbook)
+ Chef::CookbookLoader.stub!(:new).and_return(@cookbook_loader)
+
+ @cookbook_uploader = Chef::CookbookUploader.new('herpderp', File.join(CHEF_SPEC_DATA, 'cookbooks'), :rest => "norest")
+ Chef::CookbookUploader.stub!(:new).and_return(@cookbook_uploader)
+ @cookbook_uploader.stub!(:validate_cookbooks).and_return(true)
+ Chef::CookbookSiteStreamingUploader.stub!(:create_build_dir).and_return(Dir.mktmpdir)
+
+ Chef::Mixin::Command.stub(:run_command).and_return(true)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe 'run' do
+
+ before(:each) do
+ @knife.stub!(:do_upload).and_return(true)
+ end
+
+ it 'should should print usage and exit when given no arguments' do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it 'should print usage and exit when given only 1 argument' do
+ @knife.name_args = ['cookbook_name']
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it 'should check if the cookbook exists' do
+ @cookbook_loader.should_receive(:cookbook_exists?)
+ @knife.run
+ end
+
+ it "should exit and log to error if the cookbook doesn't exist" do
+ @cookbook_loader.stub(:cookbook_exists?).and_return(false)
+ @knife.ui.should_receive(:error)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it 'should make a tarball of the cookbook' do
+ Chef::Mixin::Command.should_receive(:run_command) { |args|
+ args[:command].should match /tar -czf/
+ }
+ @knife.run
+ end
+
+ it 'should exit and log to error when the tarball creation fails' do
+ Chef::Mixin::Command.stub!(:run_command).and_raise(Chef::Exceptions::Exec)
+ @knife.ui.should_receive(:error)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it 'should upload the cookbook and clean up the tarball' do
+ @knife.should_receive(:do_upload)
+ FileUtils.should_receive(:rm_rf)
+ @knife.run
+ end
+ end
+
+ describe 'do_upload' do
+
+ before(:each) do
+ @upload_response = mock('Net::HTTPResponse')
+ Chef::CookbookSiteStreamingUploader.stub!(:post).and_return(@upload_response)
+
+ @stdout = StringIO.new
+ @stderr = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @knife.ui.stub!(:stderr).and_return(@stderr)
+ File.stub(:open).and_return(true)
+ end
+
+ it 'should post the cookbook to "http://cookbooks.opscode.com"' do
+ response_text = {:uri => 'http://cookbooks.opscode.com/cookbooks/cookbook_name'}.to_json
+ @upload_response.stub!(:body).and_return(response_text)
+ @upload_response.stub!(:code).and_return(201)
+ Chef::CookbookSiteStreamingUploader.should_receive(:post).with(/cookbooks\.opscode\.com/, anything(), anything(), anything())
+ @knife.run
+ end
+
+ it 'should alert the user when a version already exists' do
+ response_text = {:error_messages => ['Version already exists']}.to_json
+ @upload_response.stub!(:body).and_return(response_text)
+ @upload_response.stub!(:code).and_return(409)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ @stderr.string.should match(/ERROR(.+)cookbook already exists/)
+ end
+
+ it 'should pass any errors on to the user' do
+ response_text = {:error_messages => ["You're holding it wrong"]}.to_json
+ @upload_response.stub!(:body).and_return(response_text)
+ @upload_response.stub!(:code).and_return(403)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ @stderr.string.should match("ERROR(.*)You're holding it wrong")
+ end
+
+ it 'should print the body if no errors are exposed on failure' do
+ response_text = {:system_error => "Your call was dropped", :reason => "There's a map for that"}.to_json
+ @upload_response.stub!(:body).and_return(response_text)
+ @upload_response.stub!(:code).and_return(500)
+ @knife.ui.should_receive(:error).with(/#{Regexp.escape(response_text)}/)#.ordered
+ @knife.ui.should_receive(:error).with(/Unknown error/)#.ordered
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ end
+
+end
diff --git a/spec/unit/knife/cookbook_site_unshare_spec.rb b/spec/unit/knife/cookbook_site_unshare_spec.rb
new file mode 100644
index 0000000000..ffba2ec664
--- /dev/null
+++ b/spec/unit/knife/cookbook_site_unshare_spec.rb
@@ -0,0 +1,77 @@
+#
+# Author:: Stephen Delano (<stephen@opscode.com>)
+# Author:: Tim Hinderliter (<tim@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::CookbookSiteUnshare do
+
+ before(:each) do
+ @knife = Chef::Knife::CookbookSiteUnshare.new
+ @knife.name_args = ['cookbook_name']
+ @knife.stub!(:confirm).and_return(true)
+
+ @rest = mock('Chef::REST')
+ @rest.stub!(:delete_rest).and_return(true)
+ @knife.stub!(:rest).and_return(@rest)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe 'run' do
+
+ describe 'with no cookbook argument' do
+ it 'should print the usage and exit' do
+ @knife.name_args = []
+ @knife.ui.should_receive(:fatal)
+ @knife.should_receive(:show_usage)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+ end
+
+ it 'should confirm you want to unshare the cookbook' do
+ @knife.should_receive(:confirm)
+ @knife.run
+ end
+
+ it 'should send a delete request to the cookbook site' do
+ @rest.should_receive(:delete_rest)
+ @knife.run
+ end
+
+ it 'should log an error and exit when forbidden' do
+ exception = mock('403 "Forbidden"', :code => '403')
+ @rest.stub!(:delete_rest).and_raise(Net::HTTPServerException.new('403 "Forbidden"', exception))
+ @knife.ui.should_receive(:error)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it 'should re-raise any non-forbidden errors on delete_rest' do
+ exception = mock('500 "Application Error"', :code => '500')
+ @rest.stub(:delete_rest).and_raise(Net::HTTPServerException.new('500 "Application Error"', exception))
+ lambda { @knife.run }.should raise_error(Net::HTTPServerException)
+ end
+
+ it 'should log a success message' do
+ @knife.ui.should_receive(:info)
+ @knife.run
+ end
+
+ end
+
+end
diff --git a/spec/unit/knife/cookbook_test_spec.rb b/spec/unit/knife/cookbook_test_spec.rb
new file mode 100644
index 0000000000..24c658dc6c
--- /dev/null
+++ b/spec/unit/knife/cookbook_test_spec.rb
@@ -0,0 +1,84 @@
+#
+# Author:: Stephen Delano (<stephen@opscode.com>)$
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.$
+# Copyright:: Copyright (c) 2010 Matthew Kent
+# 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'
+Chef::Knife::CookbookTest.load_deps
+
+describe Chef::Knife::CookbookTest do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::CookbookTest.new
+ @knife.config[:cookbook_path] = File.join(CHEF_SPEC_DATA,'cookbooks')
+ @knife.cookbook_loader.stub!(:cookbook_exists?).and_return(true)
+ @cookbooks = []
+ %w{tats central_market jimmy_johns pho}.each do |cookbook_name|
+ @cookbooks << Chef::CookbookVersion.new(cookbook_name)
+ end
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+ it "should test the cookbook" do
+ @knife.stub!(:test_cookbook).and_return(true)
+ @knife.name_args = ["italian"]
+ @knife.should_receive(:test_cookbook).with("italian")
+ @knife.run
+ end
+
+ it "should test multiple cookbooks when provided" do
+ @knife.stub!(:test_cookbook).and_return(true)
+ @knife.name_args = ["tats", "jimmy_johns"]
+ @knife.should_receive(:test_cookbook).with("tats")
+ @knife.should_receive(:test_cookbook).with("jimmy_johns")
+ @knife.should_not_receive(:test_cookbook).with("central_market")
+ @knife.should_not_receive(:test_cookbook).with("pho")
+ @knife.run
+ end
+
+ it "should test both ruby and templates" do
+ @knife.name_args = ["example"]
+ @knife.config[:cookbook_path].should_not be_empty
+ Array(@knife.config[:cookbook_path]).reverse.each do |path|
+ @knife.should_receive(:test_ruby).with(an_instance_of(Chef::Cookbook::SyntaxCheck))
+ @knife.should_receive(:test_templates).with(an_instance_of(Chef::Cookbook::SyntaxCheck))
+ end
+ @knife.run
+ end
+
+ describe "with -a or --all" do
+ it "should test all of the cookbooks" do
+ @knife.stub!(:test_cookbook).and_return(true)
+ @knife.config[:all] = true
+ @loader = {}
+ @loader.stub!(:load_cookbooks).and_return(@loader)
+ @cookbooks.each do |cookbook|
+ @loader[cookbook.name] = cookbook
+ end
+ @knife.stub!(:cookbook_loader).and_return(@loader)
+ @loader.each do |key, cookbook|
+ @knife.should_receive(:test_cookbook).with(cookbook.name)
+ end
+ @knife.run
+ end
+ end
+
+ end
+end
diff --git a/spec/unit/knife/cookbook_upload_spec.rb b/spec/unit/knife/cookbook_upload_spec.rb
new file mode 100644
index 0000000000..4659e60371
--- /dev/null
+++ b/spec/unit/knife/cookbook_upload_spec.rb
@@ -0,0 +1,183 @@
+#
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Author:: Steven Danna (<steve@opscode.com>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+
+require 'chef/cookbook_uploader'
+require 'timeout'
+
+describe Chef::Knife::CookbookUpload do
+ before(:each) do
+ @knife = Chef::Knife::CookbookUpload.new
+ @knife.name_args = ['test_cookbook']
+
+ @cookbook = Chef::CookbookVersion.new('test_cookbook')
+
+ @cookbook_loader = {}
+ @cookbook_loader.stub!(:[]).and_return(@cookbook)
+ @cookbook_loader.stub!(:merged_cookbooks).and_return([])
+ @cookbook_loader.stub!(:load_cookbooks).and_return(@cookbook_loader)
+ Chef::CookbookLoader.stub!(:new).and_return(@cookbook_loader)
+
+ @output = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@output)
+ @knife.ui.stub!(:stderr).and_return(@output)
+ end
+
+ describe 'run' do
+ before(:each) do
+ @knife.stub!(:upload).and_return(true)
+ Chef::CookbookVersion.stub(:list_all_versions).and_return({})
+ end
+
+ it 'should print usage and exit when a cookbook name is not provided' do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ @knife.ui.should_receive(:fatal)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ describe 'when specifying a cookbook name' do
+ it 'should upload the cookbook' do
+ @knife.should_receive(:upload).once
+ @knife.run
+ end
+
+ it 'should report on success' do
+ @knife.should_receive(:upload).once
+ @knife.ui.should_receive(:info).with(/Uploaded 1 cookbook/)
+ @knife.run
+ end
+ end
+
+ describe 'when specifying the same cookbook name twice' do
+ it 'should upload the cookbook only once' do
+ @knife.name_args = ['test_cookbook', 'test_cookbook']
+ @knife.should_receive(:upload).once
+ @knife.run
+ end
+ end
+
+ describe 'when specifying a cookbook name among many' do
+ before(:each) do
+ @knife.name_args = ['test_cookbook1']
+ @cookbooks = {
+ 'test_cookbook1' => Chef::CookbookVersion.new('test_cookbook1'),
+ 'test_cookbook2' => Chef::CookbookVersion.new('test_cookbook2'),
+ 'test_cookbook3' => Chef::CookbookVersion.new('test_cookbook3')
+ }
+ @cookbook_loader = {}
+ @cookbook_loader.stub!(:merged_cookbooks).and_return([])
+ @cookbook_loader.stub(:[]) { |ckbk| @cookbooks[ckbk] }
+ Chef::CookbookLoader.stub!(:new).and_return(@cookbook_loader)
+ end
+
+ it "should read only one cookbook" do
+ @cookbook_loader.should_receive(:[]).once.with('test_cookbook1')
+ @knife.run
+ end
+
+ it "should not read all cookbooks" do
+ @cookbook_loader.should_not_receive(:load_cookbooks)
+ @knife.run
+ end
+
+ it "should upload only one cookbook" do
+ @knife.should_receive(:upload).exactly(1).times
+ @knife.run
+ end
+ end
+
+ # This is testing too much. We should break it up.
+ describe 'when specifying a cookbook name with dependencies' do
+ it "should upload all dependencies once" do
+ @knife.name_args = ["test_cookbook2"]
+ @knife.config[:depends] = true
+ @test_cookbook1 = Chef::CookbookVersion.new('test_cookbook1')
+ @test_cookbook2 = Chef::CookbookVersion.new('test_cookbook2')
+ @test_cookbook3 = Chef::CookbookVersion.new('test_cookbook3')
+ @test_cookbook2.metadata.depends("test_cookbook3")
+ @test_cookbook3.metadata.depends("test_cookbook1")
+ @test_cookbook3.metadata.depends("test_cookbook2")
+ @cookbook_loader.stub!(:[]) do |ckbk|
+ { "test_cookbook1" => @test_cookbook1,
+ "test_cookbook2" => @test_cookbook2,
+ "test_cookbook3" => @test_cookbook3 }[ckbk]
+ end
+ @knife.stub!(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2", "test_cookbook3"])
+ @knife.should_receive(:upload).exactly(3).times
+ Timeout::timeout(5) do
+ @knife.run
+ end.should_not raise_error(Timeout::Error)
+ end
+ end
+
+ it "should freeze the version of the cookbooks if --freeze is specified" do
+ @knife.config[:freeze] = true
+ @cookbook.should_receive(:freeze_version).once
+ @knife.run
+ end
+
+ describe 'with -a or --all' do
+ before(:each) do
+ @knife.config[:all] = true
+ @test_cookbook1 = Chef::CookbookVersion.new('test_cookbook1')
+ @test_cookbook2 = Chef::CookbookVersion.new('test_cookbook2')
+ @cookbook_loader.stub!(:each).and_yield("test_cookbook1", @test_cookbook1).and_yield("test_cookbook2", @test_cookbook2)
+ @cookbook_loader.stub!(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2"])
+ end
+
+ it 'should upload all cookbooks' do
+ @knife.should_receive(:upload).once
+ @knife.run
+ end
+
+ it 'should report on success' do
+ @knife.should_receive(:upload).once
+ @knife.ui.should_receive(:info).with(/Uploaded all cookbooks/)
+ @knife.run
+ end
+
+ it 'should update the version constraints for an environment' do
+ @knife.stub!(:assert_environment_valid!).and_return(true)
+ @knife.config[:environment] = "production"
+ @knife.should_receive(:update_version_constraints).once
+ @knife.run
+ end
+ end
+
+ describe 'when a frozen cookbook exists on the server' do
+ it 'should fail to replace it' do
+ @knife.stub!(:upload).and_raise(Chef::Exceptions::CookbookFrozen)
+ @knife.ui.should_receive(:error).with(/Failed to upload 1 cookbook/)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ it 'should not update the version constraints for an environment' do
+ @knife.stub!(:assert_environment_valid!).and_return(true)
+ @knife.config[:environment] = "production"
+ @knife.stub!(:upload).and_raise(Chef::Exceptions::CookbookFrozen)
+ @knife.ui.should_receive(:error).with(/Failed to upload 1 cookbook/)
+ @knife.ui.should_receive(:warn).with(/Not updating version constraints/)
+ @knife.should_not_receive(:update_version_constraints)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+ end
+ end # run
+end # Chef::Knife::CookbookUpload
diff --git a/spec/unit/knife/core/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb
new file mode 100644
index 0000000000..f8a58484a5
--- /dev/null
+++ b/spec/unit/knife/core/bootstrap_context_spec.rb
@@ -0,0 +1,128 @@
+#
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2011 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/knife/core/bootstrap_context'
+
+describe Chef::Knife::Core::BootstrapContext do
+ before do
+ @config = {:foo => :bar}
+ @run_list = Chef::RunList.new('recipe[tmux]', 'role[base]')
+ @chef_config = {:validation_key => File.join(CHEF_SPEC_DATA, 'ssl', 'private_key.pem')}
+ @chef_config[:chef_server_url] = 'http://chef.example.com:4444'
+ @chef_config[:validation_client_name] = 'chef-validator-testing'
+ @context = Chef::Knife::Core::BootstrapContext.new(@config, @run_list, @chef_config)
+ end
+
+ describe "to support compatability with existing templates" do
+ it "sets the @config instance variable" do
+ @context.instance_eval { @config }.should == {:foo => :bar}
+ end
+
+ it "sets the @run_list instance variable" do
+ @context.instance_eval { @run_list }.should equal(@run_list)
+ end
+ end
+
+ it "installs the same version of chef on the remote host" do
+ @context.bootstrap_version_string.should == "--version #{Chef::VERSION}"
+ end
+
+ it "runs chef with the first-boot.json in the _default environment" do
+ @context.start_chef.should == "chef-client -j /etc/chef/first-boot.json -E _default"
+ end
+
+ it "it runs chef-client from another path when specified" do
+ @chef_config[:chef_client_path] = '/usr/local/bin/chef-client'
+ @context.start_chef.should == "/usr/local/bin/chef-client -j /etc/chef/first-boot.json -E _default"
+ end
+
+ it "reads the validation key" do
+ @context.validation_key.should == IO.read(File.join(CHEF_SPEC_DATA, 'ssl', 'private_key.pem'))
+ end
+
+ it "generates the config file data" do
+ expected=<<-EXPECTED
+log_level :info
+log_location STDOUT
+chef_server_url "http://chef.example.com:4444"
+validation_client_name "chef-validator-testing"
+# Using default node name (fqdn)
+EXPECTED
+ @context.config_content.should == expected
+ end
+
+ describe "when an explicit node name is given" do
+ before do
+ @config[:chef_node_name] = 'foobar.example.com'
+ end
+ it "sets the node name in the client.rb" do
+ @context.config_content.should match(/node_name "foobar\.example\.com"/)
+ end
+ end
+
+ describe "when bootstrapping into a specific environment" do
+ before do
+ @chef_config[:environment] = "prodtastic"
+ end
+
+ it "starts chef in the configured environment" do
+ @context.start_chef.should == 'chef-client -j /etc/chef/first-boot.json -E prodtastic'
+ end
+ end
+
+ describe "when installing a prerelease version of chef" do
+ before do
+ @config[:prerelease] = true
+ end
+ it "supplies --prerelease as the version string" do
+ @context.bootstrap_version_string.should == '--prerelease'
+ end
+ end
+
+ describe "when installing an explicit version of chef" do
+ before do
+ @context = Chef::Knife::Core::BootstrapContext.new(@config, @run_list, :knife => { :bootstrap_version => '123.45.678' })
+ end
+
+ it "gives --version $VERSION as the version string" do
+ @context.bootstrap_version_string.should == '--version 123.45.678'
+ end
+ end
+
+ describe "when JSON attributes are given" do
+ before do
+ conf = @config.dup
+ conf[:first_boot_attributes] = {:baz => :quux}
+ @context = Chef::Knife::Core::BootstrapContext.new(conf, @run_list, @chef_config)
+ end
+
+ it "adds the attributes to first_boot" do
+ @context.first_boot.to_json.should == {:baz => :quux, :run_list => @run_list}.to_json
+ end
+ end
+
+ describe "when JSON attributes are NOT given" do
+ it "sets first_boot equal to run_list" do
+ @context.first_boot.to_json.should == {:run_list => @run_list}.to_json
+ end
+ end
+
+
+end
+
diff --git a/spec/unit/knife/core/cookbook_scm_repo_spec.rb b/spec/unit/knife/core/cookbook_scm_repo_spec.rb
new file mode 100644
index 0000000000..629164ad0a
--- /dev/null
+++ b/spec/unit/knife/core/cookbook_scm_repo_spec.rb
@@ -0,0 +1,187 @@
+#
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2011 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/knife/core/cookbook_scm_repo'
+
+describe Chef::Knife::CookbookSCMRepo do
+ before do
+ @repo_path = File.join(CHEF_SPEC_DATA, 'cookbooks')
+ @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
+ @ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
+ @cookbook_repo = Chef::Knife::CookbookSCMRepo.new(@repo_path, @ui, :default_branch => 'master')
+
+ @branch_list = Mixlib::ShellOut.new
+ @branch_list.stdout.replace(<<-BRANCHES)
+ chef-vendor-apache2
+ chef-vendor-build-essential
+ chef-vendor-dynomite
+ chef-vendor-ganglia
+ chef-vendor-graphite
+ chef-vendor-python
+ chef-vendor-absent-new
+BRANCHES
+ end
+
+ it "has a path to the cookbook repo" do
+ @cookbook_repo.repo_path.should == @repo_path
+ end
+
+ it "has a default branch" do
+ @cookbook_repo.default_branch.should == 'master'
+ end
+
+ describe "when sanity checking the repo" do
+ it "exits when the directory does not exist" do
+ ::File.should_receive(:directory?).with(@repo_path).and_return(false)
+ lambda {@cookbook_repo.sanity_check}.should raise_error(SystemExit)
+ end
+
+ describe "and the repo dir exists" do
+ before do
+ ::File.stub!(:directory?).with(@repo_path).and_return(true)
+ end
+
+ it "exits when there is no git repo" do
+ ::File.stub!(:directory?).with(/.*\.git/).and_return(false)
+ lambda {@cookbook_repo.sanity_check}.should raise_error(SystemExit)
+ end
+
+ describe "and the repo is a git repo" do
+ before do
+ ::File.stub!(:directory?).with(File.join(@repo_path, '.git')).and_return(true)
+ end
+
+ it "exits when the default branch doesn't exist" do
+ @nobranches = Mixlib::ShellOut.new.tap {|s|s.stdout.replace "\n"}
+ @cookbook_repo.should_receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@nobranches)
+ lambda {@cookbook_repo.sanity_check}.should raise_error(SystemExit)
+ end
+
+ describe "and the default branch exists" do
+ before do
+ @master_branch = Mixlib::ShellOut.new
+ @master_branch.stdout.replace "* master\n"
+ @cookbook_repo.should_receive(:shell_out!).with("git branch --no-color", :cwd => @repo_path).and_return(@master_branch)
+ end
+
+ it "exits when the git repo is dirty" do
+ @dirty_status = Mixlib::ShellOut.new
+ @dirty_status.stdout.replace(<<-DIRTY)
+ M chef/lib/chef/knife/cookbook_site_vendor.rb
+DIRTY
+ @cookbook_repo.should_receive(:shell_out!).with('git status --porcelain', :cwd => @repo_path).and_return(@dirty_status)
+ lambda {@cookbook_repo.sanity_check}.should raise_error(SystemExit)
+ end
+
+ describe "and the repo is clean" do
+ before do
+ @clean_status = Mixlib::ShellOut.new.tap {|s| s.stdout.replace("\n")}
+ @cookbook_repo.stub!(:shell_out!).with('git status --porcelain', :cwd => @repo_path).and_return(@clean_status)
+ end
+
+ it "passes the sanity check" do
+ @cookbook_repo.sanity_check
+ end
+
+ end
+ end
+ end
+ end
+ end
+
+ it "resets to default state by checking out the default branch" do
+ @cookbook_repo.should_receive(:shell_out!).with('git checkout master', :cwd => @repo_path)
+ @cookbook_repo.reset_to_default_state
+ end
+
+ it "determines if a the pristine copy branch exists" do
+ @cookbook_repo.should_receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list)
+ @cookbook_repo.branch_exists?("chef-vendor-apache2").should be_true
+ @cookbook_repo.should_receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list)
+ @cookbook_repo.branch_exists?("chef-vendor-nginx").should be_false
+ end
+
+ it "determines if a the branch not exists correctly without substring search" do
+ @cookbook_repo.should_receive(:shell_out!).twice.with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list)
+ @cookbook_repo.should_not be_branch_exists("chef-vendor-absent")
+ @cookbook_repo.should be_branch_exists("chef-vendor-absent-new")
+ end
+
+ describe "when the pristine copy branch does not exist" do
+ it "prepares for import by creating the pristine copy branch" do
+ @cookbook_repo.should_receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list)
+ @cookbook_repo.should_receive(:shell_out!).with('git checkout -b chef-vendor-nginx', :cwd => @repo_path)
+ @cookbook_repo.prepare_to_import("nginx")
+ end
+ end
+
+ describe "when the pristine copy branch does exist" do
+ it "prepares for import by checking out the pristine copy branch" do
+ @cookbook_repo.should_receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list)
+ @cookbook_repo.should_receive(:shell_out!).with('git checkout chef-vendor-apache2', :cwd => @repo_path)
+ @cookbook_repo.prepare_to_import("apache2")
+ end
+ end
+
+ describe "when the pristine copy branch was not updated by the changes" do
+ before do
+ @updates = Mixlib::ShellOut.new
+ @updates.stdout.replace("\n")
+ @cookbook_repo.stub!(:shell_out!).with('git status --porcelain -- apache2', :cwd => @repo_path).and_return(@updates)
+ end
+
+ it "shows no changes in the pristine copy" do
+ @cookbook_repo.updated?('apache2').should be_false
+ end
+
+ it "does nothing to finalize the updates" do
+ @cookbook_repo.finalize_updates_to('apache2', '1.2.3').should be_false
+ end
+ end
+
+ describe "when the pristine copy branch was updated by the changes" do
+ before do
+ @updates = Mixlib::ShellOut.new
+ @updates.stdout.replace(" M cookbooks/apache2/recipes/default.rb\n")
+ @cookbook_repo.stub!(:shell_out!).with('git status --porcelain -- apache2', :cwd => @repo_path).and_return(@updates)
+ end
+
+ it "shows changes in the pristine copy" do
+ @cookbook_repo.updated?('apache2').should be_true
+ end
+
+ it "commits the changes to the repo and tags the commit" do
+ @cookbook_repo.should_receive(:shell_out!).with("git add apache2", :cwd => @repo_path)
+ @cookbook_repo.should_receive(:shell_out!).with("git commit -m \"Import apache2 version 1.2.3\" -- apache2", :cwd => @repo_path)
+ @cookbook_repo.should_receive(:shell_out!).with("git tag -f cookbook-site-imported-apache2-1.2.3", :cwd => @repo_path)
+ @cookbook_repo.finalize_updates_to("apache2", "1.2.3").should be_true
+ end
+ end
+
+ describe "when a custom default branch is specified" do
+ before do
+ @cookbook_repo = Chef::Knife::CookbookSCMRepo.new(@repo_path, @ui, :default_branch => 'develop')
+ end
+
+ it "resets to default state by checking out the default branch" do
+ @cookbook_repo.should_receive(:shell_out!).with('git checkout develop', :cwd => @repo_path)
+ @cookbook_repo.reset_to_default_state
+ end
+ end
+end
diff --git a/spec/unit/knife/core/object_loader_spec.rb b/spec/unit/knife/core/object_loader_spec.rb
new file mode 100644
index 0000000000..b3456e2b15
--- /dev/null
+++ b/spec/unit/knife/core/object_loader_spec.rb
@@ -0,0 +1,81 @@
+#
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Author:: Juanje Ojeda (<juanje.ojeda@gmail.com>)
+# Copyright:: Copyright (c) 2011-2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/knife/core/object_loader'
+
+describe Chef::Knife::Core::ObjectLoader do
+ before(:each) do
+ @knife = Chef::Knife.new
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ Dir.chdir(File.join(CHEF_SPEC_DATA, 'object_loader'))
+ end
+
+ shared_examples_for "Chef object" do |chef_class|
+ it "should create a #{chef_class} object" do
+ @object.should be_a_kind_of(chef_class)
+ end
+
+ it "should has a attribute 'name'" do
+ @object.name.should eql('test')
+ end
+ end
+
+ {
+ 'nodes' => Chef::Node,
+ 'roles' => Chef::Role,
+ 'environments' => Chef::Environment
+ }.each do |repo_location, chef_class|
+
+ describe "when the file is a #{chef_class}" do
+ before do
+ @loader = Chef::Knife::Core::ObjectLoader.new(chef_class, @knife.ui)
+ end
+
+ describe "when the file is a Ruby" do
+ before do
+ @object = @loader.load_from(repo_location, 'test.rb')
+ end
+
+ it_behaves_like "Chef object", chef_class
+ end
+
+ #NOTE: This is check for the bug described at CHEF-2352
+ describe "when the file is a JSON" do
+ describe "and it has defined 'json_class'" do
+ before do
+ @object = @loader.load_from(repo_location, 'test_json_class.json')
+ end
+
+ it_behaves_like "Chef object", chef_class
+ end
+
+ describe "and it has not defined 'json_class'" do
+ before do
+ @object = @loader.load_from(repo_location, 'test.json')
+ end
+
+ it_behaves_like "Chef object", chef_class
+ end
+ end
+ end
+ end
+
+end
diff --git a/spec/unit/knife/core/subcommand_loader_spec.rb b/spec/unit/knife/core/subcommand_loader_spec.rb
new file mode 100644
index 0000000000..e39e0be041
--- /dev/null
+++ b/spec/unit/knife/core/subcommand_loader_spec.rb
@@ -0,0 +1,54 @@
+#
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2011 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::SubcommandLoader do
+ before do
+ @home = File.join(CHEF_SPEC_DATA, 'knife-home')
+ @env = {'HOME' => @home}
+ @loader = Chef::Knife::SubcommandLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands'), @env)
+ end
+
+ it "builds a list of the core subcommand file require paths" do
+ @loader.subcommand_files.should_not be_empty
+ @loader.subcommand_files.each do |require_path|
+ require_path.should match(/chef\/knife\/.*|plugins\/knife\/.*/)
+ end
+ end
+
+ it "finds files installed via rubygems" do
+ @loader.find_subcommands_via_rubygems.should include('chef/knife/node_create')
+ @loader.find_subcommands_via_rubygems.each {|rel_path, abs_path| abs_path.should match(%r[chef/knife/.+])}
+ end
+
+ it "finds files using a dirglob when rubygems is not available" do
+ @loader.find_subcommands_via_dirglob.should include('chef/knife/node_create')
+ @loader.find_subcommands_via_dirglob.each {|rel_path, abs_path| abs_path.should match(%r[chef/knife/.+])}
+ end
+
+ it "finds user-specific subcommands in the user's ~/.chef directory" do
+ expected_command = File.join(@home, '.chef', 'plugins', 'knife', 'example_home_subcommand.rb')
+ @loader.site_subcommands.should include(expected_command)
+ end
+
+ it "finds repo specific subcommands by searching for a .chef directory" do
+ expected_command = File.join(CHEF_SPEC_DATA, 'knife-site-subcommands', 'plugins', 'knife', 'example_subcommand.rb')
+ @loader.site_subcommands.should include(expected_command)
+ end
+end
diff --git a/spec/unit/knife/core/ui_spec.rb b/spec/unit/knife/core/ui_spec.rb
new file mode 100644
index 0000000000..784ad1f0d7
--- /dev/null
+++ b/spec/unit/knife/core/ui_spec.rb
@@ -0,0 +1,309 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Tim Hinderliter (<tim@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Author:: John Keiser (<jkeiser@opscode.com>)
+# Copyright:: Copyright (c) 2008, 2011, 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::UI do
+ before do
+ @out, @err, @in = StringIO.new, StringIO.new, StringIO.new
+ @config = {}
+ @ui = Chef::Knife::UI.new(@out, @err, @in, @config)
+ end
+
+ describe "format_list_for_display" do
+ it "should print the full hash if --with-uri is true" do
+ @ui.config[:with_uri] = true
+ @ui.format_list_for_display({ :marcy => :playground }).should == { :marcy => :playground }
+ end
+
+ it "should print only the keys if --with-uri is false" do
+ @ui.config[:with_uri] = false
+ @ui.format_list_for_display({ :marcy => :playground }).should == [ :marcy ]
+ end
+ end
+
+ describe "output" do
+ it "formats strings appropriately" do
+ @ui.output("hi")
+ @out.string.should == "hi\n"
+ end
+
+ it "formats hashes appropriately" do
+ @ui.output({'hi' => 'a', 'lo' => 'b' })
+ @out.string.should == <<EOM
+hi: a
+lo: b
+EOM
+ end
+
+ it "formats empty hashes appropriately" do
+ @ui.output({})
+ @out.string.should == "\n"
+ end
+
+ it "formats arrays appropriately" do
+ @ui.output([ 'a', 'b' ])
+ @out.string.should == <<EOM
+a
+b
+EOM
+ end
+
+ it "formats empty arrays appropriately" do
+ @ui.output([ ])
+ @out.string.should == "\n"
+ end
+
+ it "formats single-member arrays appropriately" do
+ @ui.output([ 'a' ])
+ @out.string.should == "a\n"
+ end
+
+ it "formats nested single-member arrays appropriately" do
+ @ui.output([ [ 'a' ] ])
+ @out.string.should == "a\n"
+ end
+
+ it "formats nested arrays appropriately" do
+ @ui.output([ [ 'a', 'b' ], [ 'c', 'd' ]])
+ @out.string.should == <<EOM
+a
+b
+
+c
+d
+EOM
+ end
+
+ it "formats nested arrays with single- and empty subarrays appropriately" do
+ @ui.output([ [ 'a', 'b' ], [ 'c' ], [], [ 'd', 'e' ]])
+ @out.string.should == <<EOM
+a
+b
+
+c
+
+
+d
+e
+EOM
+ end
+
+ it "formats arrays of hashes with extra lines in between for readability" do
+ @ui.output([ { 'a' => 'b', 'c' => 'd' }, { 'x' => 'y' }, { 'm' => 'n', 'o' => 'p' }])
+ @out.string.should == <<EOM
+a: b
+c: d
+
+x: y
+
+m: n
+o: p
+EOM
+ end
+
+ it "formats hashes with empty array members appropriately" do
+ @ui.output({ 'a' => [], 'b' => 'c' })
+ @out.string.should == <<EOM
+a:
+b: c
+EOM
+ end
+
+ it "formats hashes with single-member array values appropriately" do
+ @ui.output({ 'a' => [ 'foo' ], 'b' => 'c' })
+ @out.string.should == <<EOM
+a: foo
+b: c
+EOM
+ end
+
+ it "formats hashes with array members appropriately" do
+ @ui.output({ 'a' => [ 'foo', 'bar' ], 'b' => 'c' })
+ @out.string.should == <<EOM
+a:
+ foo
+ bar
+b: c
+EOM
+ end
+
+ it "formats hashes with single-member nested array values appropriately" do
+ @ui.output({ 'a' => [ [ 'foo' ] ], 'b' => 'c' })
+ @out.string.should == <<EOM
+a:
+ foo
+b: c
+EOM
+ end
+
+ it "formats hashes with nested array values appropriately" do
+ @ui.output({ 'a' => [ [ 'foo', 'bar' ], [ 'baz', 'bjork' ] ], 'b' => 'c' })
+ @out.string.should == <<EOM
+a:
+ foo
+ bar
+
+ baz
+ bjork
+b: c
+EOM
+ end
+
+ it "formats hashes with hash values appropriately" do
+ @ui.output({ 'a' => { 'aa' => 'bb', 'cc' => 'dd' }, 'b' => 'c' })
+ @out.string.should == <<EOM
+a:
+ aa: bb
+ cc: dd
+b: c
+EOM
+ end
+
+ it "formats hashes with empty hash values appropriately" do
+ @ui.output({ 'a' => { }, 'b' => 'c' })
+ @out.string.should == <<EOM
+a:
+b: c
+EOM
+ end
+ end
+
+ describe "format_for_display" do
+ it "should return the raw data" do
+ input = { :gi => :go }
+ @ui.format_for_display(input).should == input
+ end
+
+ describe "with --attribute passed" do
+ it "should return the deeply nested attribute" do
+ input = { "gi" => { "go" => "ge" }, "id" => "sample-data-bag-item" }
+ @ui.config[:attribute] = "gi.go"
+ @ui.format_for_display(input).should == { "sample-data-bag-item" => { "gi.go" => "ge" } }
+ end
+ end
+
+ describe "with --run-list passed" do
+ it "should return the run list" do
+ input = Chef::Node.new
+ input.name("sample-node")
+ input.run_list("role[monkey]", "role[churchmouse]")
+ @ui.config[:run_list] = true
+ response = @ui.format_for_display(input)
+ response["sample-node"]["run_list"][0].should == "role[monkey]"
+ response["sample-node"]["run_list"][1].should == "role[churchmouse]"
+ end
+ end
+ end
+
+ describe "format_cookbook_list_for_display" do
+ before(:each) do
+ @item = {
+ "cookbook_name" => {
+ "url" => "http://url/cookbooks/cookbook",
+ "versions" => [
+ { "version" => "3.0.0", "url" => "http://url/cookbooks/3.0.0" },
+ { "version" => "2.0.0", "url" => "http://url/cookbooks/2.0.0" },
+ { "version" => "1.0.0", "url" => "http://url/cookbooks/1.0.0" }
+ ]
+ }
+ }
+ end
+
+ it "should return an array of the cookbooks with versions" do
+ expected_response = [ "cookbook_name 3.0.0 2.0.0 1.0.0" ]
+ response = @ui.format_cookbook_list_for_display(@item)
+ response.should == expected_response
+ end
+
+ describe "with --with-uri" do
+ it "should return the URIs" do
+ response = {
+ "cookbook_name"=>{
+ "1.0.0" => "http://url/cookbooks/1.0.0",
+ "2.0.0" => "http://url/cookbooks/2.0.0",
+ "3.0.0" => "http://url/cookbooks/3.0.0"}
+ }
+ @ui.config[:with_uri] = true
+ @ui.format_cookbook_list_for_display(@item).should == response
+ end
+ end
+ end
+
+ describe "confirm" do
+ before(:each) do
+ @question = "monkeys rule"
+ @stdout = StringIO.new
+ @ui.stub(:stdout).and_return(@stdout)
+ @ui.stdin.stub!(:readline).and_return("y")
+ end
+
+ it "should return true if you answer Y" do
+ @ui.stdin.stub!(:readline).and_return("Y")
+ @ui.confirm(@question).should == true
+ end
+
+ it "should return true if you answer y" do
+ @ui.stdin.stub!(:readline).and_return("y")
+ @ui.confirm(@question).should == true
+ end
+
+ it "should exit 3 if you answer N" do
+ @ui.stdin.stub!(:readline).and_return("N")
+ lambda {
+ @ui.confirm(@question)
+ }.should raise_error(SystemExit) { |e| e.status.should == 3 }
+ end
+
+ it "should exit 3 if you answer n" do
+ @ui.stdin.stub!(:readline).and_return("n")
+ lambda {
+ @ui.confirm(@question)
+ }.should raise_error(SystemExit) { |e| e.status.should == 3 }
+ end
+
+ describe "with --y or --yes passed" do
+ it "should return true" do
+ @ui.config[:yes] = true
+ @ui.confirm(@question).should == true
+ end
+ end
+
+ describe "when asking for free-form user input" do
+ it "asks a question and returns the answer provided by the user" do
+ out = StringIO.new
+ @ui.stub!(:stdout).and_return(out)
+ @ui.stub!(:stdin).and_return(StringIO.new("http://mychefserver.example.com\n"))
+ @ui.ask_question("your chef server URL?").should == "http://mychefserver.example.com"
+ out.string.should == "your chef server URL?"
+ end
+
+ it "suggests a default setting and returns the default when the user's response only contains whitespace" do
+ out = StringIO.new
+ @ui.stub!(:stdout).and_return(out)
+ @ui.stub!(:stdin).and_return(StringIO.new(" \n"))
+ @ui.ask_question("your chef server URL? ", :default => 'http://localhost:4000').should == "http://localhost:4000"
+ out.string.should == "your chef server URL? [http://localhost:4000] "
+ end
+ end
+
+ end
+end
diff --git a/spec/unit/knife/data_bag_create_spec.rb b/spec/unit/knife/data_bag_create_spec.rb
new file mode 100644
index 0000000000..7d9433984f
--- /dev/null
+++ b/spec/unit/knife/data_bag_create_spec.rb
@@ -0,0 +1,105 @@
+#
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Author:: Seth Falcon (<seth@opscode.com>)
+# Copyright:: Copyright (c) 2009-2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'tempfile'
+
+module ChefSpecs
+ class ChefRest
+ attr_reader :args_received
+ def initialize
+ @args_received = []
+ end
+
+ def post_rest(*args)
+ @args_received << args
+ end
+ end
+end
+
+
+describe Chef::Knife::DataBagCreate do
+ before do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::DataBagCreate.new
+ @rest = ChefSpecs::ChefRest.new
+ @knife.stub!(:rest).and_return(@rest)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+
+ it "creates a data bag when given one argument" do
+ @knife.name_args = ['sudoing_admins']
+ @rest.should_receive(:post_rest).with("data", {"name" => "sudoing_admins"})
+ @knife.ui.should_receive(:info).with("Created data_bag[sudoing_admins]")
+
+ @knife.run
+ end
+
+ it "creates a data bag item when given two arguments" do
+ @knife.name_args = ['sudoing_admins', 'ME']
+ user_supplied_hash = {"login_name" => "alphaomega", "id" => "ME"}
+ data_bag_item = Chef::DataBagItem.from_hash(user_supplied_hash)
+ data_bag_item.data_bag("sudoing_admins")
+ @knife.should_receive(:create_object).and_yield(user_supplied_hash)
+ @rest.should_receive(:post_rest).with("data", {'name' => 'sudoing_admins'}).ordered
+ @rest.should_receive(:post_rest).with("data/sudoing_admins", data_bag_item).ordered
+
+ @knife.run
+ end
+
+ describe "encrypted data bag items" do
+ before(:each) do
+ @secret = "abc123SECRET"
+ @plain_data = {"login_name" => "alphaomega", "id" => "ME"}
+ @enc_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(@plain_data,
+ @secret)
+ @knife.name_args = ['sudoing_admins', 'ME']
+ @knife.should_receive(:create_object).and_yield(@plain_data)
+ data_bag_item = Chef::DataBagItem.from_hash(@enc_data)
+ data_bag_item.data_bag("sudoing_admins")
+ @rest.should_receive(:post_rest).with("data", {'name' => 'sudoing_admins'}).ordered
+ @rest.should_receive(:post_rest).with("data/sudoing_admins", data_bag_item).ordered
+
+ @secret_file = Tempfile.new("encrypted_data_bag_secret_file_test")
+ @secret_file.puts(@secret)
+ @secret_file.flush
+ end
+
+ after do
+ @secret_file.close
+ @secret_file.unlink
+ end
+
+ it "creates an encrypted data bag item via --secret" do
+ @knife.stub!(:config).and_return({:secret => @secret})
+ @knife.run
+ end
+
+ it "creates an encrypted data bag item via --secret_file" do
+ secret_file = Tempfile.new("encrypted_data_bag_secret_file_test")
+ secret_file.puts(@secret)
+ secret_file.flush
+ @knife.stub!(:config).and_return({:secret_file => secret_file.path})
+ @knife.run
+ end
+ end
+
+end
diff --git a/spec/unit/knife/data_bag_edit_spec.rb b/spec/unit/knife/data_bag_edit_spec.rb
new file mode 100644
index 0000000000..572722541a
--- /dev/null
+++ b/spec/unit/knife/data_bag_edit_spec.rb
@@ -0,0 +1,89 @@
+#
+# Author:: Seth Falcon (<seth@opscode.com>)
+# Copyright:: Copyright 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'tempfile'
+
+describe Chef::Knife::DataBagEdit do
+ before do
+ @plain_data = {"login_name" => "alphaomega", "id" => "item_name"}
+ @edited_data = {
+ "login_name" => "rho", "id" => "item_name",
+ "new_key" => "new_value" }
+
+ Chef::Config[:node_name] = "webmonkey.example.com"
+
+ @knife = Chef::Knife::DataBagEdit.new
+ @rest = mock('chef-rest-mock')
+ @knife.stub!(:rest).and_return(@rest)
+
+ @stdout = StringIO.new
+ @knife.stub!(:stdout).and_return(@stdout)
+ @log = Chef::Log
+ @knife.name_args = ['bag_name', 'item_name']
+ end
+
+ it "requires data bag and item arguments" do
+ @knife.name_args = []
+ lambda { @knife.run }.should raise_error(SystemExit)
+ @stdout.string.should match(/^You must supply the data bag and an item to edit/)
+ end
+
+ it "saves edits on a data bag item" do
+ Chef::DataBagItem.stub!(:load).with('bag_name', 'item_name').and_return(@plain_data)
+ @knife.should_receive(:edit_data).with(@plain_data).and_return(@edited_data)
+ @rest.should_receive(:put_rest).with("data/bag_name/item_name", @edited_data).ordered
+ @knife.run
+ end
+
+ describe "encrypted data bag items" do
+ before(:each) do
+ @secret = "abc123SECRET"
+ @enc_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(@plain_data,
+ @secret)
+ @enc_edited_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(@edited_data,
+ @secret)
+ Chef::DataBagItem.stub!(:load).with('bag_name', 'item_name').and_return(@enc_data)
+
+ @secret_file = Tempfile.new("encrypted_data_bag_secret_file_test")
+ @secret_file.puts(@secret)
+ @secret_file.flush
+ end
+
+ after do
+ @secret_file.close
+ @secret_file.unlink
+ end
+
+ it "decrypts and encrypts via --secret" do
+ @knife.stub!(:config).and_return({:secret => @secret})
+ @knife.should_receive(:edit_data).with(@plain_data).and_return(@edited_data)
+ @rest.should_receive(:put_rest).with("data/bag_name/item_name", @enc_edited_data).ordered
+
+ @knife.run
+ end
+
+ it "decrypts and encrypts via --secret_file" do
+ @knife.stub!(:config).and_return({:secret_file => @secret_file.path})
+ @knife.should_receive(:edit_data).with(@plain_data).and_return(@edited_data)
+ @rest.should_receive(:put_rest).with("data/bag_name/item_name", @enc_edited_data).ordered
+
+ @knife.run
+ end
+ end
+end
diff --git a/spec/unit/knife/data_bag_from_file_spec.rb b/spec/unit/knife/data_bag_from_file_spec.rb
new file mode 100644
index 0000000000..f4ed7ca5de
--- /dev/null
+++ b/spec/unit/knife/data_bag_from_file_spec.rb
@@ -0,0 +1,191 @@
+#
+# Author:: Seth Falcon (<seth@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/data_bag_item'
+require 'chef/encrypted_data_bag_item'
+require 'tempfile'
+require 'json'
+
+Chef::Knife::DataBagFromFile.load_deps
+
+describe Chef::Knife::DataBagFromFile do
+ before :each do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::DataBagFromFile.new
+ @rest = mock("Chef::REST")
+ @knife.stub!(:rest).and_return(@rest)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @tmp_dir = Dir.mktmpdir
+ @db_folder = File.join(@tmp_dir, 'data_bags', 'bag_name')
+ FileUtils.mkdir_p(@db_folder)
+ @db_file = Tempfile.new(["data_bag_from_file_test", ".json"], @db_folder)
+ @db_file2 = Tempfile.new(["data_bag_from_file_test2", ".json"], @db_folder)
+ @db_folder2 = File.join(@tmp_dir, 'data_bags', 'bag_name2')
+ FileUtils.mkdir_p(@db_folder2)
+ @db_file3 = Tempfile.new(["data_bag_from_file_test3", ".json"], @db_folder2)
+ @plain_data = {
+ "id" => "item_name",
+ "greeting" => "hello",
+ "nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true }}
+ }
+ @db_file.write(@plain_data.to_json)
+ @db_file.flush
+ @knife.instance_variable_set(:@name_args, ['bag_name', @db_file.path])
+ end
+
+ # We have to explicitly clean up Tempfile on Windows because it said so.
+ after :each do
+ @db_file.close
+ @db_file2.close
+ @db_file3.close
+ FileUtils.rm_rf(@db_folder)
+ FileUtils.rm_rf(@db_folder2)
+ FileUtils.remove_entry_secure @tmp_dir
+ end
+
+ it "loads from a file and saves" do
+ @knife.loader.should_receive(:load_from).with("data_bags", 'bag_name', @db_file.path).and_return(@plain_data)
+ dbag = Chef::DataBagItem.new
+ Chef::DataBagItem.stub!(:new).and_return(dbag)
+ dbag.should_receive(:save)
+ @knife.run
+
+ dbag.data_bag.should == 'bag_name'
+ dbag.raw_data.should == @plain_data
+ end
+
+ it "loads all from a mutiple files and saves" do
+ @knife.name_args = [ 'bag_name', @db_file.path, @db_file2.path ]
+ @knife.loader.should_receive(:load_from).with("data_bags", 'bag_name', @db_file.path).and_return(@plain_data)
+ @knife.loader.should_receive(:load_from).with("data_bags", 'bag_name', @db_file2.path).and_return(@plain_data)
+ dbag = Chef::DataBagItem.new
+ Chef::DataBagItem.stub!(:new).and_return(dbag)
+ dbag.should_receive(:save).twice
+ @knife.run
+
+ dbag.data_bag.should == 'bag_name'
+ dbag.raw_data.should == @plain_data
+ end
+
+ it "loads all from a folder and saves" do
+ dir = File.dirname(@db_file.path)
+ @knife.name_args = [ 'bag_name', @db_folder ]
+ @knife.loader.should_receive(:load_from).with("data_bags", 'bag_name', @db_file.path).and_return(@plain_data)
+ @knife.loader.should_receive(:load_from).with("data_bags", 'bag_name', @db_file2.path).and_return(@plain_data)
+ dbag = Chef::DataBagItem.new
+ Chef::DataBagItem.stub!(:new).and_return(dbag)
+ dbag.should_receive(:save).twice
+ @knife.run
+ end
+
+ describe "loading all data bags" do
+
+ before do
+ @pwd = Dir.pwd
+ Dir.chdir(@tmp_dir)
+ end
+
+ after do
+ Dir.chdir(@pwd)
+ end
+
+ it "loads all data bags when -a or --all options is provided" do
+ @knife.name_args = []
+ @knife.stub!(:config).and_return({:all => true})
+ @knife.loader.should_receive(:load_from).with("data_bags", "bag_name", File.basename(@db_file.path)).
+ and_return(@plain_data)
+ @knife.loader.should_receive(:load_from).with("data_bags", "bag_name", File.basename(@db_file2.path)).
+ and_return(@plain_data)
+ @knife.loader.should_receive(:load_from).with("data_bags", "bag_name2", File.basename(@db_file3.path)).
+ and_return(@plain_data)
+ dbag = Chef::DataBagItem.new
+ Chef::DataBagItem.stub!(:new).and_return(dbag)
+ dbag.should_receive(:save).exactly(3).times
+ @knife.run
+ end
+
+ it "loads all data bags items when -a or --all options is provided" do
+ @knife.name_args = ["bag_name2"]
+ @knife.stub!(:config).and_return({:all => true})
+ @knife.loader.should_receive(:load_from).with("data_bags", "bag_name2", File.basename(@db_file3.path)).
+ and_return(@plain_data)
+ dbag = Chef::DataBagItem.new
+ Chef::DataBagItem.stub!(:new).and_return(dbag)
+ dbag.should_receive(:save)
+ @knife.run
+ dbag.data_bag.should == 'bag_name2'
+ dbag.raw_data.should == @plain_data
+ end
+
+ end
+
+ describe "encrypted data bag items" do
+ before(:each) do
+ @secret = "abc123SECRET"
+ @enc_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(@plain_data,
+ @secret)
+ @secret_file = Tempfile.new("encrypted_data_bag_secret_file_test")
+ @secret_file.puts(@secret)
+ @secret_file.flush
+ end
+
+ after do
+ @secret_file.close
+ @secret_file.unlink
+ end
+
+ it "encrypts values when given --secret" do
+ @knife.stub!(:config).and_return({:secret => @secret})
+
+ @knife.loader.should_receive(:load_from).with("data_bags", "bag_name", @db_file.path).and_return(@plain_data)
+ dbag = Chef::DataBagItem.new
+ Chef::DataBagItem.stub!(:new).and_return(dbag)
+ dbag.should_receive(:save)
+ @knife.run
+ dbag.data_bag.should == 'bag_name'
+ dbag.raw_data.should == @enc_data
+ end
+
+ it "encrypts values when given --secret_file" do
+ @knife.stub!(:config).and_return({:secret_file => @secret_file.path})
+
+ @knife.loader.stub!(:load_from).with("data_bags", 'bag_name', @db_file.path).and_return(@plain_data)
+ dbag = Chef::DataBagItem.new
+ Chef::DataBagItem.stub!(:new).and_return(dbag)
+ dbag.should_receive(:save)
+ @knife.run
+ dbag.data_bag.should == 'bag_name'
+ dbag.raw_data.should == @enc_data
+ end
+
+ end
+
+ describe "command line parsing" do
+ it "prints help if given no arguments" do
+ @knife.instance_variable_set(:@name_args, [])
+ lambda { @knife.run }.should raise_error(SystemExit)
+ help_text = "knife data bag from file BAG FILE|FOLDER [FILE|FOLDER..] (options)"
+ help_text_regex = Regexp.new("^#{Regexp.escape(help_text)}")
+ @stdout.string.should match(help_text_regex)
+ end
+ end
+
+end
diff --git a/spec/unit/knife/data_bag_show_spec.rb b/spec/unit/knife/data_bag_show_spec.rb
new file mode 100644
index 0000000000..08ecfaa0a7
--- /dev/null
+++ b/spec/unit/knife/data_bag_show_spec.rb
@@ -0,0 +1,112 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Seth Falcon (<seth@opscode.com>)
+# Copyright:: Copyright (c) 2008-2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/data_bag_item'
+require 'chef/encrypted_data_bag_item'
+require 'chef/json_compat'
+require 'tempfile'
+
+describe Chef::Knife::DataBagShow do
+ before do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::DataBagShow.new
+ @knife.config[:format] = 'json'
+ @rest = mock("Chef::REST")
+ @knife.stub!(:rest).and_return(@rest)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+
+ it "prints the ids of the data bag items when given a bag name" do
+ @knife.instance_variable_set(:@name_args, ['bag_o_data'])
+ data_bag_contents = { "baz"=>"http://localhost:4000/data/bag_o_data/baz",
+ "qux"=>"http://localhost:4000/data/bag_o_data/qux"}
+ Chef::DataBag.should_receive(:load).and_return(data_bag_contents)
+ expected = %q|[
+ "baz",
+ "qux"
+]|
+ @knife.run
+ @stdout.string.strip.should == expected
+ end
+
+ it "prints the contents of the data bag item when given a bag and item name" do
+ @knife.instance_variable_set(:@name_args, ['bag_o_data', 'an_item'])
+ data_item = Chef::DataBagItem.new.tap {|item| item.raw_data = {"id" => "an_item", "zsh" => "victory_through_tabbing"}}
+
+ Chef::DataBagItem.should_receive(:load).with('bag_o_data', 'an_item').and_return(data_item)
+
+ @knife.run
+ Chef::JSONCompat.from_json(@stdout.string).should == data_item.raw_data
+
+ end
+
+ describe "encrypted data bag items" do
+ before(:each) do
+ @secret = "abc123SECRET"
+ @plain_data = {
+ "id" => "item_name",
+ "greeting" => "hello",
+ "nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true }}
+ }
+ @enc_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(@plain_data,
+ @secret)
+ @knife.instance_variable_set(:@name_args, ['bag_name', 'item_name'])
+
+ @secret_file = Tempfile.new("encrypted_data_bag_secret_file_test")
+ @secret_file.puts(@secret)
+ @secret_file.flush
+ end
+
+ after do
+ @secret_file.close
+ @secret_file.unlink
+ end
+
+ it "prints the decrypted contents of an item when given --secret" do
+ @knife.stub!(:config).and_return({:secret => @secret})
+ Chef::EncryptedDataBagItem.should_receive(:load).
+ with('bag_name', 'item_name', @secret).
+ and_return(Chef::EncryptedDataBagItem.new(@enc_data, @secret))
+ @knife.run
+ Chef::JSONCompat.from_json(@stdout.string).should == @plain_data
+ end
+
+ it "prints the decrypted contents of an item when given --secret_file" do
+ @knife.stub!(:config).and_return({:secret_file => @secret_file.path})
+ Chef::EncryptedDataBagItem.should_receive(:load).
+ with('bag_name', 'item_name', @secret).
+ and_return(Chef::EncryptedDataBagItem.new(@enc_data, @secret))
+ @knife.run
+ Chef::JSONCompat.from_json(@stdout.string).should == @plain_data
+ end
+ end
+
+ describe "command line parsing" do
+ it "prints help if given no arguments" do
+ @knife.instance_variable_set(:@name_args, [])
+ lambda { @knife.run }.should raise_error(SystemExit)
+ @stdout.string.should match(/^knife data bag show BAG \[ITEM\] \(options\)/)
+ end
+ end
+
+end
diff --git a/spec/unit/knife/environment_create_spec.rb b/spec/unit/knife/environment_create_spec.rb
new file mode 100644
index 0000000000..36f6556682
--- /dev/null
+++ b/spec/unit/knife/environment_create_spec.rb
@@ -0,0 +1,91 @@
+#
+# Author:: Stephen Delano (<stephen@ospcode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::EnvironmentCreate do
+ before(:each) do
+ @knife = Chef::Knife::EnvironmentCreate.new
+ @knife.stub!(:msg).and_return true
+ @knife.stub!(:output).and_return true
+ @knife.stub!(:show_usage).and_return true
+ @knife.name_args = [ "production" ]
+
+ @environment = Chef::Environment.new
+ @environment.stub!(:save)
+
+ Chef::Environment.stub!(:new).and_return @environment
+ @knife.stub!(:edit_data).and_return @environment
+ end
+
+ describe "run" do
+ it "should create a new environment" do
+ Chef::Environment.should_receive(:new)
+ @knife.run
+ end
+
+ it "should set the environment name" do
+ @environment.should_receive(:name).with("production")
+ @knife.run
+ end
+
+ it "should not print the environment" do
+ @knife.should_not_receive(:output)
+ @knife.run
+ end
+
+ it "should prompt you to edit the data" do
+ @knife.should_receive(:edit_data).with(@environment)
+ @knife.run
+ end
+
+ it "should save the environment" do
+ @environment.should_receive(:save)
+ @knife.run
+ end
+
+ it "should show usage and exit when no environment name is provided" do
+ @knife.name_args = [ ]
+ @knife.ui.should_receive(:fatal)
+ @knife.should_receive(:show_usage)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ describe "with --description" do
+ before(:each) do
+ @knife.config[:description] = "This is production"
+ end
+
+ it "should set the description" do
+ @environment.should_receive(:description).with("This is production")
+ @knife.run
+ end
+ end
+
+ describe "with --print-after" do
+ before(:each) do
+ @knife.config[:print_after] = true
+ end
+
+ it "should pretty print the environment, formatted for display" do
+ @knife.should_receive(:output).with(@environment)
+ @knife.run
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/environment_delete_spec.rb b/spec/unit/knife/environment_delete_spec.rb
new file mode 100644
index 0000000000..219ae4a923
--- /dev/null
+++ b/spec/unit/knife/environment_delete_spec.rb
@@ -0,0 +1,71 @@
+#
+# Author:: Stephen Delano (<stephen@ospcode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::EnvironmentDelete do
+ before(:each) do
+ @knife = Chef::Knife::EnvironmentDelete.new
+ @knife.stub!(:msg).and_return true
+ @knife.stub!(:output).and_return true
+ @knife.stub!(:show_usage).and_return true
+ @knife.stub!(:confirm).and_return true
+ @knife.name_args = [ "production" ]
+
+ @environment = Chef::Environment.new
+ @environment.name("production")
+ @environment.description("Please delete me")
+ @environment.stub!(:destroy).and_return true
+ Chef::Environment.stub!(:load).and_return @environment
+ end
+
+ it "should confirm that you want to delete" do
+ @knife.should_receive(:confirm)
+ @knife.run
+ end
+
+ it "should load the environment" do
+ Chef::Environment.should_receive(:load).with("production")
+ @knife.run
+ end
+
+ it "should delete the environment" do
+ @environment.should_receive(:destroy)
+ @knife.run
+ end
+
+ it "should not print the environment" do
+ @knife.should_not_receive(:output)
+ @knife.run
+ end
+
+ it "should show usage and exit when no environment name is provided" do
+ @knife.name_args = []
+ @knife.ui.should_receive(:fatal)
+ @knife.should_receive(:show_usage)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ describe "with --print-after" do
+ it "should pretty print the environment, formatted for display" do
+ @knife.config[:print_after] = true
+ @knife.should_receive(:output).with(@environment)
+ @knife.run
+ end
+ end
+end
diff --git a/spec/unit/knife/environment_edit_spec.rb b/spec/unit/knife/environment_edit_spec.rb
new file mode 100644
index 0000000000..91f9f5d0f0
--- /dev/null
+++ b/spec/unit/knife/environment_edit_spec.rb
@@ -0,0 +1,79 @@
+#
+# Author:: Stephen Delano (<stephen@ospcode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::EnvironmentEdit do
+ before(:each) do
+ @knife = Chef::Knife::EnvironmentEdit.new
+ @knife.ui.stub!(:msg).and_return true
+ @knife.ui.stub!(:output).and_return true
+ @knife.ui.stub!(:show_usage).and_return true
+ @knife.name_args = [ "production" ]
+
+ @environment = Chef::Environment.new
+ @environment.name("production")
+ @environment.description("Please edit me")
+ @environment.stub!(:save).and_return true
+ Chef::Environment.stub!(:load).and_return @environment
+ @knife.ui.stub(:edit_data).and_return @environment
+ end
+
+ it "should load the environment" do
+ Chef::Environment.should_receive(:load).with("production")
+ @knife.run
+ end
+
+ it "should let you edit the environment" do
+ @knife.ui.should_receive(:edit_data).with(@environment)
+ @knife.run
+ end
+
+ it "should save the edited environment data" do
+ pansy = Chef::Environment.new
+
+ @environment.name("new_environment_name")
+ @knife.ui.should_receive(:edit_data).with(@environment).and_return(pansy)
+ pansy.should_receive(:save)
+ @knife.run
+ end
+
+ it "should not save the unedited environment data" do
+ @environment.should_not_receive(:save)
+ @knife.run
+ end
+
+ it "should not print the environment" do
+ @knife.should_not_receive(:output)
+ @knife.run
+ end
+
+ it "shoud show usage and exit when no environment name is provided" do
+ @knife.name_args = []
+ @knife.should_receive(:show_usage)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ describe "with --print-after" do
+ it "should pretty print the environment, formatted for display" do
+ @knife.config[:print_after] = true
+ @knife.ui.should_receive(:output).with(@environment)
+ @knife.run
+ end
+ end
+end
diff --git a/spec/unit/knife/environment_from_file_spec.rb b/spec/unit/knife/environment_from_file_spec.rb
new file mode 100644
index 0000000000..d2234d9be1
--- /dev/null
+++ b/spec/unit/knife/environment_from_file_spec.rb
@@ -0,0 +1,89 @@
+#
+# Author:: Stephen Delano (<stephen@ospcode.com>)
+# Author:: Seth Falcon (<seth@ospcode.com>)
+# Copyright:: Copyright 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+Chef::Knife::EnvironmentFromFile.load_deps
+
+describe Chef::Knife::EnvironmentFromFile do
+ before(:each) do
+ @knife = Chef::Knife::EnvironmentFromFile.new
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @knife.name_args = [ "spec.rb" ]
+
+ @environment = Chef::Environment.new
+ @environment.name("spec")
+ @environment.description("runs the unit tests")
+ @environment.cookbook_versions({"apt" => "= 1.2.3"})
+ @environment.stub!(:save).and_return true
+ @knife.loader.stub!(:load_from).and_return @environment
+ end
+
+ describe "run" do
+ it "loads the environment data from a file and saves it" do
+ @knife.loader.should_receive(:load_from).with('environments', 'spec.rb').and_return(@environment)
+ @environment.should_receive(:save)
+ @knife.run
+ end
+
+ context "when handling multiple environments" do
+ before(:each) do
+ @env_apple = @environment.dup
+ @env_apple.name("apple")
+ @knife.loader.stub!(:load_from).with("apple.rb").and_return @env_apple
+ end
+
+ it "loads multiple environments if given" do
+ @knife.name_args = [ "spec.rb", "apple.rb" ]
+ @environment.should_receive(:save).twice
+ @knife.run
+ end
+
+ it "loads all environments with -a" do
+ File.stub!(:expand_path).with("./environments/*.{json,rb}").and_return("/tmp/environments")
+ Dir.stub!(:glob).with("/tmp/environments").and_return(["spec.rb", "apple.rb"])
+ @knife.name_args = []
+ @knife.stub!(:config).and_return({:all => true})
+ @environment.should_receive(:save).twice
+ @knife.run
+ end
+ end
+
+ it "should not print the environment" do
+ @knife.should_not_receive(:output)
+ @knife.run
+ end
+
+ it "should show usage and exit if not filename is provided" do
+ @knife.name_args = []
+ @knife.ui.should_receive(:fatal)
+ @knife.should_receive(:show_usage)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ describe "with --print-after" do
+ it "should pretty print the environment, formatted for display" do
+ @knife.config[:print_after] = true
+ @knife.should_receive(:output)
+ @knife.run
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/environment_list_spec.rb b/spec/unit/knife/environment_list_spec.rb
new file mode 100644
index 0000000000..05a3ae748a
--- /dev/null
+++ b/spec/unit/knife/environment_list_spec.rb
@@ -0,0 +1,54 @@
+#
+# Author:: Stephen Delano (<stephen@ospcode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::EnvironmentList do
+ before(:each) do
+ @knife = Chef::Knife::EnvironmentList.new
+ @knife.stub!(:msg).and_return true
+ @knife.stub!(:output).and_return true
+ @knife.stub!(:show_usage).and_return true
+
+ @environments = {
+ "production" => "http://localhost:4000/environments/production",
+ "development" => "http://localhost:4000/environments/development",
+ "testing" => "http://localhost:4000/environments/testing"
+ }
+ Chef::Environment.stub!(:list).and_return @environments
+ end
+
+ it "should make an api call to list the environments" do
+ Chef::Environment.should_receive(:list)
+ @knife.run
+ end
+
+ it "should print the environment names in a sorted list" do
+ names = @environments.keys.sort { |a,b| a <=> b }
+ @knife.should_receive(:output).with(names)
+ @knife.run
+ end
+
+ describe "with --with-uri" do
+ it "should print and unsorted list of the environments and their URIs" do
+ @knife.config[:with_uri] = true
+ @knife.should_receive(:output).with(@environments)
+ @knife.run
+ end
+ end
+end
diff --git a/spec/unit/knife/environment_show_spec.rb b/spec/unit/knife/environment_show_spec.rb
new file mode 100644
index 0000000000..1e1556f4c3
--- /dev/null
+++ b/spec/unit/knife/environment_show_spec.rb
@@ -0,0 +1,52 @@
+#
+# Author:: Stephen Delano (<stephen@ospcode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::EnvironmentShow do
+ before(:each) do
+ @knife = Chef::Knife::EnvironmentShow.new
+ @knife.stub!(:msg).and_return true
+ @knife.stub!(:output).and_return true
+ @knife.stub!(:show_usage).and_return true
+ @knife.name_args = [ "production" ]
+
+ @environment = Chef::Environment.new
+ @environment.name("production")
+ @environment.description("Look at me!")
+ Chef::Environment.stub!(:load).and_return @environment
+ end
+
+ it "should load the environment" do
+ Chef::Environment.should_receive(:load).with("production")
+ @knife.run
+ end
+
+ it "should pretty print the environment, formatted for display" do
+ @knife.should_receive(:format_for_display).with(@environment)
+ @knife.should_receive(:output)
+ @knife.run
+ end
+
+ it "should show usage and exit when no environment name is provided" do
+ @knife.name_args = []
+ @knife.ui.should_receive(:fatal)
+ @knife.should_receive(:show_usage)
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+end
diff --git a/spec/unit/knife/index_rebuild_spec.rb b/spec/unit/knife/index_rebuild_spec.rb
new file mode 100644
index 0000000000..3b22a3b88c
--- /dev/null
+++ b/spec/unit/knife/index_rebuild_spec.rb
@@ -0,0 +1,65 @@
+#
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# Copyright:: Copyright (c) 2009 Daniel DeLeo
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::IndexRebuild do
+ before do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::IndexRebuild.new
+ @rest_client = mock("Chef::REST (mock)", :post_rest => { :result => :true })
+ @knife.ui.stub!(:output)
+ @knife.stub!(:rest).and_return(@rest_client)
+
+ @out = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@out)
+ end
+
+ it "asks a yes/no confirmation and aborts on 'no'" do
+ @knife.ui.stub!(:stdin).and_return(StringIO.new("NO\n"))
+ @knife.should_receive(:puts)
+ @knife.should_receive(:exit).with(7)
+ @knife.run
+ @out.string.should match(/yes\/no/)
+ end
+
+ it "asks a confirmation and continues on 'yes'" do
+ @knife.ui.stub!(:stdin).and_return(StringIO.new("yes\n"))
+ @knife.should_not_receive(:exit)
+ @knife.run
+ @out.string.should match(/yes\/no/)
+ end
+
+ describe "after confirming the operation" do
+ before do
+ @knife.ui.stub!(:print)
+ @knife.ui.stub!(:puts)
+ @knife.stub!(:nag)
+ @knife.ui.stub!(:output)
+ end
+
+ it "POSTs to /search/reindex and displays the result" do
+ @rest_client = mock("Chef::REST")
+ @knife.stub!(:rest).and_return(@rest_client)
+ @rest_client.should_receive(:post_rest).with("/search/reindex", {}).and_return("monkey")
+ @knife.should_receive(:output).with("monkey")
+ @knife.run
+ end
+ end
+
+end
diff --git a/spec/unit/knife/knife_help.rb b/spec/unit/knife/knife_help.rb
new file mode 100644
index 0000000000..f5753e3d62
--- /dev/null
+++ b/spec/unit/knife/knife_help.rb
@@ -0,0 +1,92 @@
+#
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# Copyright:: Copyright (c) 2011 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::Help do
+ before(:each) do
+ # Perilously use the build in list even though it is dynamic so we don't get warnings about the constant
+ # HELP_TOPICS = [ "foo", "bar", "knife-kittens", "ceiling-cat", "shell" ]
+ @knife = Chef::Knife::Help.new
+ end
+
+ it "should return a list of help topics" do
+ @knife.help_topics.should include("knife-status")
+ end
+
+ it "should run man for you" do
+ @knife.name_args = [ "shell" ]
+ @knife.should_receive(:exec).with(/^man \/.*\/shell.1$/)
+ @knife.run
+ end
+
+ it "should suggest topics" do
+ @knife.name_args = [ "list" ]
+ @knife.ui.stub!(:msg)
+ @knife.ui.should_receive(:info).with("Available help topics are: ")
+ @knife.ui.should_receive(:msg).with(/knife/)
+ @knife.stub!(:exec)
+ @knife.should_receive(:exit).with(1)
+ @knife.run
+ end
+
+ describe "find_manpage_path" do
+ it "should find the man page in the gem" do
+ @knife.find_manpage_path("shell").should =~ /distro\/common\/man\/man1\/chef-shell.1$/
+ end
+
+ it "should provide the man page name if not in the gem" do
+ @knife.find_manpage_path("foo").should == "foo"
+ end
+ end
+
+ describe "find_manpages_for_query" do
+ it "should error if it does not find a match" do
+ @knife.ui.stub!(:error)
+ @knife.ui.stub!(:info)
+ @knife.ui.stub!(:msg)
+ @knife.should_receive(:exit).with(1)
+ @knife.ui.should_receive(:error).with("No help found for 'chickens'")
+ @knife.ui.should_receive(:msg).with(/knife/)
+ @knife.find_manpages_for_query("chickens")
+ end
+ end
+
+ describe "print_help_topics" do
+ it "should print the known help topics" do
+ @knife.ui.stub!(:msg)
+ @knife.ui.stub!(:info)
+ @knife.ui.should_receive(:msg).with(/knife/)
+ @knife.print_help_topics
+ end
+
+ it "should shorten topics prefixed by knife-" do
+ @knife.ui.stub!(:msg)
+ @knife.ui.stub!(:info)
+ @knife.ui.should_receive(:msg).with(/node/)
+ @knife.print_help_topics
+ end
+
+ it "should not leave topics prefixed by knife-" do
+ @knife.ui.stub!(:msg)
+ @knife.ui.stub!(:info)
+ @knife.ui.should_not_receive(:msg).with(/knife-node/)
+ @knife.print_help_topics
+ end
+ end
+end
diff --git a/spec/unit/knife/node_bulk_delete_spec.rb b/spec/unit/knife/node_bulk_delete_spec.rb
new file mode 100644
index 0000000000..51f707dfcf
--- /dev/null
+++ b/spec/unit/knife/node_bulk_delete_spec.rb
@@ -0,0 +1,97 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::NodeBulkDelete do
+ before(:each) do
+ Chef::Log.logger = Logger.new(StringIO.new)
+
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::NodeBulkDelete.new
+ @knife.name_args = ["."]
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @knife.ui.stub!(:confirm).and_return(true)
+ @nodes = Hash.new
+ %w{adam brent jacob}.each do |node_name|
+ @nodes[node_name] = "http://localhost:4000/nodes/#{node_name}"
+ end
+ end
+
+ describe "when creating the list of nodes" do
+ it "fetches the node list" do
+ expected = @nodes.inject({}) do |inflatedish, (name, uri)|
+ inflatedish[name] = Chef::Node.new.tap {|n| n.name(name)}
+ inflatedish
+ end
+ Chef::Node.should_receive(:list).and_return(@nodes)
+ # I hate not having == defined for anything :(
+ actual = @knife.all_nodes
+ actual.keys.should =~ expected.keys
+ actual.values.map {|n| n.name }.should =~ %w[adam brent jacob]
+ end
+ end
+
+ describe "run" do
+ before do
+ @inflatedish_list = @nodes.keys.inject({}) do |nodes_by_name, name|
+ node = Chef::Node.new()
+ node.name(name)
+ node.stub!(:destroy).and_return(true)
+ nodes_by_name[name] = node
+ nodes_by_name
+ end
+ @knife.stub!(:all_nodes).and_return(@inflatedish_list)
+ end
+
+ it "should print the nodes you are about to delete" do
+ @knife.run
+ @stdout.string.should match(/#{@knife.ui.list(@nodes.keys.sort, :columns_down)}/)
+ end
+
+ it "should confirm you really want to delete them" do
+ @knife.ui.should_receive(:confirm)
+ @knife.run
+ end
+
+ it "should delete each node" do
+ @inflatedish_list.each_value do |n|
+ n.should_receive(:destroy)
+ end
+ @knife.run
+ end
+
+ it "should only delete nodes that match the regex" do
+ @knife.name_args = ['adam']
+ @inflatedish_list['adam'].should_receive(:destroy)
+ @inflatedish_list['brent'].should_not_receive(:destroy)
+ @inflatedish_list['jacob'].should_not_receive(:destroy)
+ @knife.run
+ end
+
+ it "should exit if the regex is not provided" do
+ @knife.name_args = []
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ end
+end
+
+
+
diff --git a/spec/unit/knife/node_delete_spec.rb b/spec/unit/knife/node_delete_spec.rb
new file mode 100644
index 0000000000..b1b3db1aa4
--- /dev/null
+++ b/spec/unit/knife/node_delete_spec.rb
@@ -0,0 +1,68 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::NodeDelete do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::NodeDelete.new
+ @knife.config = {
+ :print_after => nil
+ }
+ @knife.name_args = [ "adam" ]
+ @knife.stub!(:output).and_return(true)
+ @knife.stub!(:confirm).and_return(true)
+ @node = Chef::Node.new()
+ @node.stub!(:destroy).and_return(true)
+ Chef::Node.stub!(:load).and_return(@node)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+ it "should confirm that you want to delete" do
+ @knife.should_receive(:confirm)
+ @knife.run
+ end
+
+ it "should load the node" do
+ Chef::Node.should_receive(:load).with("adam").and_return(@node)
+ @knife.run
+ end
+
+ it "should delete the node" do
+ @node.should_receive(:destroy).and_return(@node)
+ @knife.run
+ end
+
+ it "should not print the node" do
+ @knife.should_not_receive(:output).with("poop")
+ @knife.run
+ end
+
+ describe "with -p or --print-after" do
+ it "should pretty print the node, formatted for display" do
+ @knife.config[:print_after] = true
+ @knife.should_receive(:format_for_display).with(@node).and_return("poop")
+ @knife.should_receive(:output).with("poop")
+ @knife.run
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/node_edit_spec.rb b/spec/unit/knife/node_edit_spec.rb
new file mode 100644
index 0000000000..0ba2e90cfe
--- /dev/null
+++ b/spec/unit/knife/node_edit_spec.rb
@@ -0,0 +1,88 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+Chef::Knife::NodeEdit.load_deps
+
+describe Chef::Knife::NodeEdit do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::NodeEdit.new
+ @knife.config = {
+ :editor => 'cat',
+ :attribute => nil,
+ :print_after => nil
+ }
+ @knife.name_args = [ "adam" ]
+ @node = Chef::Node.new()
+ end
+
+ it "should load the node" do
+ Chef::Node.should_receive(:load).with("adam").and_return(@node)
+ @knife.node
+ end
+
+ describe "after loading the node" do
+ before do
+ @knife.stub!(:node).and_return(@node)
+ @node.automatic_attrs = {:go => :away}
+ @node.default_attrs = {:hide => :me}
+ @node.override_attrs = {:dont => :show}
+ @node.normal_attrs = {:do_show => :these}
+ @node.chef_environment("prod")
+ @node.run_list("recipe[foo]")
+ end
+
+ it "creates a view of the node without attributes from roles or ohai" do
+ actual = Chef::JSONCompat.from_json(@knife.node_editor.view)
+ actual.should_not have_key("automatic")
+ actual.should_not have_key("override")
+ actual.should_not have_key("default")
+ actual["normal"].should == {"do_show" => "these"}
+ actual["run_list"].should == ["recipe[foo]"]
+ actual["chef_environment"].should == "prod"
+ end
+
+ it "shows the extra attributes when given the --all option" do
+ @knife.config[:all_attributes] = true
+
+ actual = Chef::JSONCompat.from_json(@knife.node_editor.view)
+ actual["automatic"].should == {"go" => "away"}
+ actual["override"].should == {"dont" => "show"}
+ actual["default"].should == {"hide" => "me"}
+ actual["normal"].should == {"do_show" => "these"}
+ actual["run_list"].should == ["recipe[foo]"]
+ actual["chef_environment"].should == "prod"
+ end
+
+ it "does not consider unedited data updated" do
+ view = Chef::JSONCompat.from_json( @knife.node_editor.view )
+ @knife.node_editor.apply_updates(view)
+ @knife.node_editor.should_not be_updated
+ end
+
+ it "considers edited data updated" do
+ view = Chef::JSONCompat.from_json( @knife.node_editor.view )
+ view["run_list"] << "role[fuuu]"
+ @knife.node_editor.apply_updates(view)
+ @knife.node_editor.should be_updated
+ end
+
+ end
+end
+
diff --git a/spec/unit/knife/node_from_file_spec.rb b/spec/unit/knife/node_from_file_spec.rb
new file mode 100644
index 0000000000..c6b9610d9e
--- /dev/null
+++ b/spec/unit/knife/node_from_file_spec.rb
@@ -0,0 +1,59 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+Chef::Knife::NodeFromFile.load_deps
+
+describe Chef::Knife::NodeFromFile do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::NodeFromFile.new
+ @knife.config = {
+ :print_after => nil
+ }
+ @knife.name_args = [ "adam.rb" ]
+ @knife.stub!(:output).and_return(true)
+ @knife.stub!(:confirm).and_return(true)
+ @node = Chef::Node.new()
+ @node.stub!(:save)
+ @knife.loader.stub!(:load_from).and_return(@node)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+ it "should load from a file" do
+ @knife.loader.should_receive(:load_from).with('nodes', 'adam.rb').and_return(@node)
+ @knife.run
+ end
+
+ it "should not print the Node" do
+ @knife.should_not_receive(:output)
+ @knife.run
+ end
+
+ describe "with -p or --print-after" do
+ it "should print the Node" do
+ @knife.config[:print_after] = true
+ @knife.should_receive(:output)
+ @knife.run
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/node_list_spec.rb b/spec/unit/knife/node_list_spec.rb
new file mode 100644
index 0000000000..5637d679c8
--- /dev/null
+++ b/spec/unit/knife/node_list_spec.rb
@@ -0,0 +1,63 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::NodeList do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ Chef::Config[:environment] = nil # reset this value each time, as it is not reloaded
+ @knife = Chef::Knife::NodeList.new
+ @knife.stub!(:output).and_return(true)
+ @list = {
+ "foo" => "http://example.com/foo",
+ "bar" => "http://example.com/foo"
+ }
+ Chef::Node.stub!(:list).and_return(@list)
+ Chef::Node.stub!(:list_by_environment).and_return(@list)
+ end
+
+ describe "run" do
+ it "should list all of the nodes if -E is not specified" do
+ Chef::Node.should_receive(:list).and_return(@list)
+ @knife.run
+ end
+
+ it "should pretty print the list" do
+ Chef::Node.should_receive(:list).and_return(@list)
+ @knife.should_receive(:output).with([ "bar", "foo" ])
+ @knife.run
+ end
+
+ it "should list nodes in the specific environment if -E ENVIRONMENT is specified" do
+ Chef::Config[:environment] = "prod"
+ Chef::Node.should_receive(:list_by_environment).with("prod").and_return(@list)
+ @knife.run
+ end
+
+ describe "with -w or --with-uri" do
+ it "should pretty print the hash" do
+ @knife.config[:with_uri] = true
+ Chef::Node.should_receive(:list).and_return(@list)
+ @knife.should_receive(:output).with(@list)
+ @knife.run
+ end
+ end
+ end
+end
+
diff --git a/spec/unit/knife/node_run_list_add_spec.rb b/spec/unit/knife/node_run_list_add_spec.rb
new file mode 100644
index 0000000000..ee0cfc9038
--- /dev/null
+++ b/spec/unit/knife/node_run_list_add_spec.rb
@@ -0,0 +1,125 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::NodeRunListAdd do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::NodeRunListAdd.new
+ @knife.config = {
+ :after => nil
+ }
+ @knife.name_args = [ "adam", "role[monkey]" ]
+ @knife.stub!(:output).and_return(true)
+ @node = Chef::Node.new()
+ @node.stub!(:save).and_return(true)
+ Chef::Node.stub!(:load).and_return(@node)
+ end
+
+ describe "run" do
+ it "should load the node" do
+ Chef::Node.should_receive(:load).with("adam")
+ @knife.run
+ end
+
+ it "should add to the run list" do
+ @knife.run
+ @node.run_list[0].should == 'role[monkey]'
+ end
+
+ it "should save the node" do
+ @node.should_receive(:save)
+ @knife.run
+ end
+
+ it "should print the run list" do
+ @knife.should_receive(:output).and_return(true)
+ @knife.run
+ end
+
+ describe "with -a or --after specified" do
+ it "should add to the run list after the specified entry" do
+ @node.run_list << "role[acorns]"
+ @node.run_list << "role[barn]"
+ @knife.config[:after] = "role[acorns]"
+ @knife.run
+ @node.run_list[0].should == "role[acorns]"
+ @node.run_list[1].should == "role[monkey]"
+ @node.run_list[2].should == "role[barn]"
+ end
+ end
+
+ describe "with more than one role or recipe" do
+ it "should add to the run list all the entries" do
+ @knife.name_args = [ "adam", "role[monkey],role[duck]" ]
+ @node.run_list << "role[acorns]"
+ @knife.run
+ @node.run_list[0].should == "role[acorns]"
+ @node.run_list[1].should == "role[monkey]"
+ @node.run_list[2].should == "role[duck]"
+ end
+ end
+
+ describe "with more than one role or recipe with space between items" do
+ it "should add to the run list all the entries" do
+ @knife.name_args = [ "adam", "role[monkey], role[duck]" ]
+ @node.run_list << "role[acorns]"
+ @knife.run
+ @node.run_list[0].should == "role[acorns]"
+ @node.run_list[1].should == "role[monkey]"
+ @node.run_list[2].should == "role[duck]"
+ end
+ end
+
+ describe "with more than one role or recipe as different arguments" do
+ it "should add to the run list all the entries" do
+ @knife.name_args = [ "adam", "role[monkey]", "role[duck]" ]
+ @node.run_list << "role[acorns]"
+ @knife.run
+ @node.run_list[0].should == "role[acorns]"
+ @node.run_list[1].should == "role[monkey]"
+ @node.run_list[2].should == "role[duck]"
+ end
+ end
+
+ describe "with more than one role or recipe as different arguments and list separated by comas" do
+ it "should add to the run list all the entries" do
+ @knife.name_args = [ "adam", "role[monkey]", "role[duck],recipe[bird::fly]" ]
+ @node.run_list << "role[acorns]"
+ @knife.run
+ @node.run_list[0].should == "role[acorns]"
+ @node.run_list[1].should == "role[monkey]"
+ @node.run_list[2].should == "role[duck]"
+ end
+ end
+
+ describe "with one role or recipe but with an extraneous comma" do
+ it "should add to the run list one item" do
+ @knife.name_args = [ "adam", "role[monkey]," ]
+ @node.run_list << "role[acorns]"
+ @knife.run
+ @node.run_list[0].should == "role[acorns]"
+ @node.run_list[1].should == "role[monkey]"
+ end
+ end
+ end
+end
+
+
+
diff --git a/spec/unit/knife/node_run_list_remove_spec.rb b/spec/unit/knife/node_run_list_remove_spec.rb
new file mode 100644
index 0000000000..90869e8baa
--- /dev/null
+++ b/spec/unit/knife/node_run_list_remove_spec.rb
@@ -0,0 +1,74 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::NodeRunListRemove do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::NodeRunListRemove.new
+ @knife.config[:print_after] = nil
+ @knife.name_args = [ "adam", "role[monkey]" ]
+ @node = Chef::Node.new()
+ @node.name("knifetest-node")
+ @node.run_list << "role[monkey]"
+ @node.stub!(:save).and_return(true)
+
+ @knife.ui.stub!(:output).and_return(true)
+ @knife.ui.stub!(:confirm).and_return(true)
+
+ Chef::Node.stub!(:load).and_return(@node)
+ end
+
+ describe "run" do
+ it "should load the node" do
+ Chef::Node.should_receive(:load).with("adam").and_return(@node)
+ @knife.run
+ end
+
+ it "should remove the item from the run list" do
+ @knife.run
+ @node.run_list[0].should_not == 'role[monkey]'
+ end
+
+ it "should save the node" do
+ @node.should_receive(:save).and_return(true)
+ @knife.run
+ end
+
+ it "should print the run list" do
+ @knife.config[:print_after] = true
+ @knife.ui.should_receive(:output).with({ "knifetest-node" => { 'run_list' => [] } })
+ @knife.run
+ end
+
+ describe "run with a list of roles and recipes" do
+ it "should remove the items from the run list" do
+ @node.run_list << 'role[monkey]'
+ @node.run_list << 'recipe[duck::type]'
+ @knife.name_args = [ 'adam', 'role[monkey],recipe[duck::type]' ]
+ @knife.run
+ @node.run_list.should_not include('role[monkey]')
+ @node.run_list.should_not include('recipe[duck::type]')
+ end
+ end
+ end
+end
+
+
+
diff --git a/spec/unit/knife/node_show_spec.rb b/spec/unit/knife/node_show_spec.rb
new file mode 100644
index 0000000000..6600b2aa96
--- /dev/null
+++ b/spec/unit/knife/node_show_spec.rb
@@ -0,0 +1,48 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::NodeShow do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::NodeShow.new
+ @knife.config = {
+ :attribute => nil,
+ :run_list => nil,
+ :environment => nil
+ }
+ @knife.name_args = [ "adam" ]
+ @knife.stub!(:output).and_return(true)
+ @node = Chef::Node.new()
+ Chef::Node.stub!(:load).and_return(@node)
+ end
+
+ describe "run" do
+ it "should load the node" do
+ Chef::Node.should_receive(:load).with("adam").and_return(@node)
+ @knife.run
+ end
+
+ it "should pretty print the node, formatted for display" do
+ @knife.should_receive(:format_for_display).with(@node).and_return("poop")
+ @knife.should_receive(:output).with("poop")
+ @knife.run
+ end
+ end
+end
diff --git a/spec/unit/knife/role_bulk_delete_spec.rb b/spec/unit/knife/role_bulk_delete_spec.rb
new file mode 100644
index 0000000000..0ee84f6455
--- /dev/null
+++ b/spec/unit/knife/role_bulk_delete_spec.rb
@@ -0,0 +1,80 @@
+#
+# Author:: Stephen Delano (<stephen@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::RoleBulkDelete do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::RoleBulkDelete.new
+ @knife.config = {
+ :print_after => nil
+ }
+ @knife.name_args = ["."]
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @knife.ui.stub!(:confirm).and_return(true)
+ @roles = Hash.new
+ %w{dev staging production}.each do |role_name|
+ role = Chef::Role.new()
+ role.name(role_name)
+ role.stub!(:destroy).and_return(true)
+ @roles[role_name] = role
+ end
+ Chef::Role.stub!(:list).and_return(@roles)
+ end
+
+ describe "run" do
+
+ it "should get the list of the roles" do
+ Chef::Role.should_receive(:list).and_return(@roles)
+ @knife.run
+ end
+
+ it "should print the roles you are about to delete" do
+ @knife.run
+ @stdout.string.should match(/#{@knife.ui.list(@roles.keys.sort, :columns_down)}/)
+ end
+
+ it "should confirm you really want to delete them" do
+ @knife.ui.should_receive(:confirm)
+ @knife.run
+ end
+
+ it "should delete each role" do
+ @roles.each_value do |r|
+ r.should_receive(:destroy)
+ end
+ @knife.run
+ end
+
+ it "should only delete roles that match the regex" do
+ @knife.name_args = ["dev"]
+ @roles["dev"].should_receive(:destroy)
+ @roles["staging"].should_not_receive(:destroy)
+ @roles["production"].should_not_receive(:destroy)
+ @knife.run
+ end
+
+ it "should exit if the regex is not provided" do
+ @knife.name_args = []
+ lambda { @knife.run }.should raise_error(SystemExit)
+ end
+
+ end
+end
diff --git a/spec/unit/knife/role_create_spec.rb b/spec/unit/knife/role_create_spec.rb
new file mode 100644
index 0000000000..af3a6bf539
--- /dev/null
+++ b/spec/unit/knife/role_create_spec.rb
@@ -0,0 +1,80 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::RoleCreate do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::RoleCreate.new
+ @knife.config = {
+ :description => nil
+ }
+ @knife.name_args = [ "adam" ]
+ @knife.stub!(:output).and_return(true)
+ @role = Chef::Role.new()
+ @role.stub!(:save)
+ Chef::Role.stub!(:new).and_return(@role)
+ @knife.stub!(:edit_data).and_return(@role)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+ it "should create a new role" do
+ Chef::Role.should_receive(:new).and_return(@role)
+ @knife.run
+ end
+
+ it "should set the role name" do
+ @role.should_receive(:name).with("adam")
+ @knife.run
+ end
+
+ it "should not print the role" do
+ @knife.should_not_receive(:output)
+ @knife.run
+ end
+
+ it "should allow you to edit the data" do
+ @knife.should_receive(:edit_data).with(@role)
+ @knife.run
+ end
+
+ it "should save the role" do
+ @role.should_receive(:save)
+ @knife.run
+ end
+
+ describe "with -d or --description" do
+ it "should set the description" do
+ @knife.config[:description] = "All is bob"
+ @role.should_receive(:description).with("All is bob")
+ @knife.run
+ end
+ end
+
+ describe "with -p or --print-after" do
+ it "should pretty print the node, formatted for display" do
+ @knife.config[:print_after] = true
+ @knife.should_receive(:output).with(@role)
+ @knife.run
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/role_delete_spec.rb b/spec/unit/knife/role_delete_spec.rb
new file mode 100644
index 0000000000..d2d8b889b3
--- /dev/null
+++ b/spec/unit/knife/role_delete_spec.rb
@@ -0,0 +1,67 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::RoleDelete do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::RoleDelete.new
+ @knife.config = {
+ :print_after => nil
+ }
+ @knife.name_args = [ "adam" ]
+ @knife.stub!(:output).and_return(true)
+ @knife.stub!(:confirm).and_return(true)
+ @role = Chef::Role.new()
+ @role.stub!(:destroy).and_return(true)
+ Chef::Role.stub!(:load).and_return(@role)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+ it "should confirm that you want to delete" do
+ @knife.should_receive(:confirm)
+ @knife.run
+ end
+
+ it "should load the Role" do
+ Chef::Role.should_receive(:load).with("adam").and_return(@role)
+ @knife.run
+ end
+
+ it "should delete the Role" do
+ @role.should_receive(:destroy).and_return(@role)
+ @knife.run
+ end
+
+ it "should not print the Role" do
+ @knife.should_not_receive(:output)
+ @knife.run
+ end
+
+ describe "with -p or --print-after" do
+ it "should pretty print the Role, formatted for display" do
+ @knife.config[:print_after] = true
+ @knife.should_receive(:output)
+ @knife.run
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/role_edit_spec.rb b/spec/unit/knife/role_edit_spec.rb
new file mode 100644
index 0000000000..3a002f348c
--- /dev/null
+++ b/spec/unit/knife/role_edit_spec.rb
@@ -0,0 +1,79 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::RoleEdit do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::RoleEdit.new
+ @knife.config[:print_after] = nil
+ @knife.name_args = [ "adam" ]
+ @knife.ui.stub!(:output).and_return(true)
+ @role = Chef::Role.new()
+ @role.stub!(:save)
+ Chef::Role.stub!(:load).and_return(@role)
+ @knife.ui.stub!(:edit_data).and_return(@role)
+ @knife.ui.stub!(:msg)
+ end
+
+ describe "run" do
+ it "should load the role" do
+ Chef::Role.should_receive(:load).with("adam").and_return(@role)
+ @knife.run
+ end
+
+ it "should edit the role data" do
+ @knife.ui.should_receive(:edit_data).with(@role)
+ @knife.run
+ end
+
+ it "should save the edited role data" do
+ pansy = Chef::Role.new
+
+ @role.name("new_role_name")
+ @knife.ui.should_receive(:edit_data).with(@role).and_return(pansy)
+ pansy.should_receive(:save)
+ @knife.run
+ end
+
+ it "should not save the unedited role data" do
+ pansy = Chef::Role.new
+
+ @knife.ui.should_receive(:edit_data).with(@role).and_return(pansy)
+ pansy.should_not_receive(:save)
+ @knife.run
+
+ end
+
+ it "should not print the role" do
+ @knife.ui.should_not_receive(:output)
+ @knife.run
+ end
+
+ describe "with -p or --print-after" do
+ it "should pretty print the role, formatted for display" do
+ @knife.config[:print_after] = true
+ @knife.ui.should_receive(:output).with(@role)
+ @knife.run
+ end
+ end
+ end
+end
+
+
diff --git a/spec/unit/knife/role_from_file_spec.rb b/spec/unit/knife/role_from_file_spec.rb
new file mode 100644
index 0000000000..9b81bb14af
--- /dev/null
+++ b/spec/unit/knife/role_from_file_spec.rb
@@ -0,0 +1,69 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+Chef::Knife::RoleFromFile.load_deps
+
+describe Chef::Knife::RoleFromFile do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::RoleFromFile.new
+ @knife.config = {
+ :print_after => nil
+ }
+ @knife.name_args = [ "adam.rb" ]
+ @knife.stub!(:output).and_return(true)
+ @knife.stub!(:confirm).and_return(true)
+ @role = Chef::Role.new()
+ @role.stub!(:save)
+ @knife.loader.stub!(:load_from).and_return(@role)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+ it "should load from a file" do
+ @knife.loader.should_receive(:load_from).with('roles', 'adam.rb').and_return(@role)
+ @knife.run
+ end
+
+ it "should not print the role" do
+ @knife.should_not_receive(:output)
+ @knife.run
+ end
+
+ describe "with -p or --print-after" do
+ it "should print the role" do
+ @knife.config[:print_after] = true
+ @knife.should_receive(:output)
+ @knife.run
+ end
+ end
+ end
+
+ describe "run with multiple arguments" do
+ it "should load each file" do
+ @knife.name_args = [ "adam.rb", "caleb.rb" ]
+ @knife.loader.should_receive(:load_from).with('roles', 'adam.rb').and_return(@role)
+ @knife.loader.should_receive(:load_from).with('roles', 'caleb.rb').and_return(@role)
+ @knife.run
+ end
+ end
+
+end
diff --git a/spec/unit/knife/role_list_spec.rb b/spec/unit/knife/role_list_spec.rb
new file mode 100644
index 0000000000..1a5e8e2a72
--- /dev/null
+++ b/spec/unit/knife/role_list_spec.rb
@@ -0,0 +1,56 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::RoleList do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::RoleList.new
+ @knife.stub!(:output).and_return(true)
+ @list = {
+ "foo" => "http://example.com/foo",
+ "bar" => "http://example.com/foo"
+ }
+ Chef::Role.stub!(:list).and_return(@list)
+ end
+
+ describe "run" do
+ it "should list the roles" do
+ Chef::Role.should_receive(:list).and_return(@list)
+ @knife.run
+ end
+
+ it "should pretty print the list" do
+ Chef::Role.should_receive(:list).and_return(@list)
+ @knife.should_receive(:output).with([ "bar", "foo" ])
+ @knife.run
+ end
+
+ describe "with -w or --with-uri" do
+ it "should pretty print the hash" do
+ @knife.config[:with_uri] = true
+ Chef::Role.should_receive(:list).and_return(@list)
+ @knife.should_receive(:output).with(@list)
+ @knife.run
+ end
+ end
+ end
+end
+
+
diff --git a/spec/unit/knife/ssh_spec.rb b/spec/unit/knife/ssh_spec.rb
new file mode 100644
index 0000000000..6e90a87f01
--- /dev/null
+++ b/spec/unit/knife/ssh_spec.rb
@@ -0,0 +1,182 @@
+#
+# Author:: Bryan McLellan <btm@opscode.com>
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'net/ssh'
+require 'net/ssh/multi'
+
+describe Chef::Knife::Ssh do
+ before(:all) do
+ @original_config = Chef::Config.hash_dup
+ @original_knife_config = Chef::Config[:knife].dup
+ Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
+ end
+
+ after(:all) do
+ Chef::Config.configuration = @original_config
+ Chef::Config[:knife] = @original_knife_config
+ end
+
+ before do
+ @knife = Chef::Knife::Ssh.new
+ @knife.config.clear
+ @knife.config[:attribute] = "fqdn"
+ @node_foo = Chef::Node.new
+ @node_foo.automatic_attrs[:fqdn] = "foo.example.org"
+ @node_foo.automatic_attrs[:ipaddress] = "10.0.0.1"
+ @node_bar = Chef::Node.new
+ @node_bar.automatic_attrs[:fqdn] = "bar.example.org"
+ @node_bar.automatic_attrs[:ipaddress] = "10.0.0.2"
+ end
+
+ describe "#configure_session" do
+ context "manual is set to false (default)" do
+ before do
+ @knife.config[:manual] = false
+ @query = Chef::Search::Query.new
+ end
+
+ def configure_query(node_array)
+ @query.stub!(:search).and_return([node_array])
+ Chef::Search::Query.stub!(:new).and_return(@query)
+ end
+
+ def self.should_return_specified_attributes
+ it "returns an array of the attributes specified on the command line OR config file, if only one is set" 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.configure_session
+ end
+
+ it "returns an array of the attributes specified on the command line even when a config value is set" 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.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.configure_session
+ end
+
+ should_return_specified_attributes
+
+ context "when cloud hostnames are available" do
+ before do
+ @node_foo.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-1.compute-1.amazonaws.com"
+ @node_bar.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-2.compute-1.amazonaws.com"
+ end
+
+ 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.configure_session
+ end
+
+ should_return_specified_attributes
+ end
+
+ it "should raise an error if no host are found" do
+ configure_query([ ])
+ @knife.ui.should_receive(:fatal)
+ @knife.should_receive(:exit).with(10)
+ @knife.configure_session
+ end
+
+ context "when there are some hosts found but they do not have an attribute to connect with" do
+ before do
+ @query.stub!(:search).and_return([[@node_foo, @node_bar]])
+ @node_foo.automatic_attrs[:fqdn] = nil
+ @node_bar.automatic_attrs[:fqdn] = nil
+ Chef::Search::Query.stub!(:new).and_return(@query)
+ end
+
+ it "should raise a specific error (CHEF-3402)" do
+ @knife.ui.should_receive(:fatal).with(/^2 nodes found/)
+ @knife.should_receive(:exit).with(10)
+ @knife.configure_session
+ end
+ end
+ end
+
+ context "manual is set to true" do
+ before do
+ @knife.config[:manual] = true
+ end
+
+ it "returns an array of provided values" do
+ @knife.instance_variable_set(:@name_args, ["foo.example.org bar.example.org"])
+ @knife.should_receive(:session_from_list).with(['foo.example.org', 'bar.example.org'])
+ @knife.configure_session
+ end
+ end
+ end
+
+ describe "#configure_attribute" do
+ before do
+ Chef::Config[:knife][:ssh_attribute] = nil
+ @knife.config[:attribute] = nil
+ end
+
+ it "should return fqdn by default" do
+ @knife.configure_attribute
+ @knife.config[:attribute].should == "fqdn"
+ end
+
+ it "should return the value set in the configuration file" do
+ Chef::Config[:knife][:ssh_attribute] = "config_file"
+ @knife.configure_attribute
+ @knife.config[:attribute].should == "config_file"
+ end
+
+ it "should return the value set on the command line" do
+ @knife.config[:attribute] = "command_line"
+ @knife.configure_attribute
+ @knife.config[:attribute].should == "command_line"
+ end
+
+ it "should set override_attribute to the value of attribute from the command line" do
+ @knife.config[:attribute] = "command_line"
+ @knife.configure_attribute
+ @knife.config[:attribute].should == "command_line"
+ @knife.config[:override_attribute].should == "command_line"
+ end
+
+ it "should set override_attribute to the value of attribute from the config file" do
+ Chef::Config[:knife][:ssh_attribute] = "config_file"
+ @knife.configure_attribute
+ @knife.config[:attribute].should == "config_file"
+ @knife.config[:override_attribute].should == "config_file"
+ end
+
+ it "should prefer the command line over the config file for the value of override_attribute" do
+ Chef::Config[:knife][:ssh_attribute] = "config_file"
+ @knife.config[:attribute] = "command_line"
+ @knife.configure_attribute
+ @knife.config[:override_attribute].should == "command_line"
+ end
+ end
+
+end
+
diff --git a/spec/unit/knife/status_spec.rb b/spec/unit/knife/status_spec.rb
new file mode 100644
index 0000000000..b009997ab1
--- /dev/null
+++ b/spec/unit/knife/status_spec.rb
@@ -0,0 +1,43 @@
+#
+# Author:: Sahil Muthoo (<sahil.muthoo@gmail.com>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'highline'
+
+describe Chef::Knife::Status do
+ before(:each) do
+ node = Chef::Node.new.tap do |n|
+ n.automatic_attrs["fqdn"] = "foobar"
+ n.automatic_attrs["ohai_time"] = 1343845969
+ end
+ query = mock("Chef::Search::Query")
+ query.stub!(:search).and_yield(node)
+ Chef::Search::Query.stub!(:new).and_return(query)
+ @knife = Chef::Knife::Status.new
+ @stdout = StringIO.new
+ @knife.stub!(:highline).and_return(HighLine.new(StringIO.new, @stdout))
+ end
+
+ describe "run" do
+ it "should not colorize output unless it's writing to a tty" do
+ @knife.run
+ @stdout.string.match(/foobar/).should_not be_nil
+ @stdout.string.match(/\e.*ago/).should be_nil
+ end
+ end
+end
diff --git a/spec/unit/knife/tag_create_spec.rb b/spec/unit/knife/tag_create_spec.rb
new file mode 100644
index 0000000000..925d060879
--- /dev/null
+++ b/spec/unit/knife/tag_create_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Chef::Knife::TagCreate do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::TagCreate.new
+ @knife.name_args = [ Chef::Config[:node_name], "happytag" ]
+
+ @node = Chef::Node.new
+ @node.stub! :save
+ Chef::Node.stub!(:load).and_return @node
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+ it "can create tags on a node" do
+ @knife.run
+ @node.tags.should == ["happytag"]
+ @stdout.string.should match /created tags happytag.+node webmonkey.example.com/i
+ end
+ end
+end
diff --git a/spec/unit/knife/tag_delete_spec.rb b/spec/unit/knife/tag_delete_spec.rb
new file mode 100644
index 0000000000..ca279033a4
--- /dev/null
+++ b/spec/unit/knife/tag_delete_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Chef::Knife::TagDelete do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::TagDelete.new
+ @knife.name_args = [ Chef::Config[:node_name], "sadtag" ]
+
+ @node = Chef::Node.new
+ @node.stub! :save
+ @node.tags << "sadtag" << "happytag"
+ Chef::Node.stub!(:load).and_return @node
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+ it "can delete tags on a node" do
+ @node.tags.should == ["sadtag", "happytag"]
+ @knife.run
+ @node.tags.should == ["happytag"]
+ @stdout.string.should match /deleted.+sadtag/i
+ end
+ end
+end
diff --git a/spec/unit/knife/tag_list_spec.rb b/spec/unit/knife/tag_list_spec.rb
new file mode 100644
index 0000000000..0de5d5ebd8
--- /dev/null
+++ b/spec/unit/knife/tag_list_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Chef::Knife::TagList do
+ before(:each) do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ @knife = Chef::Knife::TagList.new
+ @knife.name_args = [ Chef::Config[:node_name], "sadtag" ]
+
+ @node = Chef::Node.new
+ @node.stub! :save
+ @node.tags << "sadtag" << "happytag"
+ Chef::Node.stub!(:load).and_return @node
+ end
+
+ describe "run" do
+ it "can list tags on a node" do
+ expected = %w(sadtag happytag)
+ @node.tags.should == expected
+ @knife.should_receive(:output).with(expected)
+ @knife.run
+ end
+ end
+end