spec/chef-vault/item_spec.rb in chef-vault-2.9.2 vs spec/chef-vault/item_spec.rb in chef-vault-3.0.0.rc1

- old
+ new

@@ -1,69 +1,14 @@ require "openssl" -require "rspec/core/shared_context" -# it turns out that simulating a node that doesn't have a public -# key is not a simple thing -module BorkedNodeWithoutPublicKey - extend RSpec::Core::SharedContext +RSpec.describe ChefVault::Item do + subject(:item) { ChefVault::Item.new("foo", "bar") } before do - # a node 'foo' with a public key - node_foo = double("chef node foo") - allow(node_foo).to receive(:name).and_return("foo") - client_foo = double("chef apiclient foo") - allow(client_foo).to receive(:name).and_return("foo") - privkey = OpenSSL::PKey::RSA.new(1024) - pubkey = privkey.public_key - allow(client_foo).to receive(:public_key).and_return(pubkey.to_pem) - # a node 'bar' without a public key - node_bar = double("chef node bar") - allow(node_bar).to receive(:name).and_return("bar") - # fake out searches to return both of our nodes - query_result = double("chef search results") - allow(query_result) - .to receive(:search) - .with(Symbol, String) - .and_yield(node_foo).and_yield(node_bar) - allow(Chef::Search::Query) - .to receive(:new) - .and_return(query_result) - # create a new vault item - @vaultitem = ChefVault::Item.new("foo", "bar") - @vaultitem["foo"] = "bar" - # make the vault item return the apiclient for foo but raise for bar - allow(@vaultitem).to receive(:load_client).with("foo") - .and_return(client_foo) - allow(@vaultitem).to receive(:load_client).with("bar") - .and_raise(ChefVault::Exceptions::ClientNotFound) - # make the vault item fall back to 'load-admin-as-client' behaviour - http_response = double("http not found") - allow(http_response).to receive(:code).and_return("404") - http_not_found = Net::HTTPServerException.new("not found", http_response) - allow(ChefVault::ChefPatch::User) - .to receive(:load) - .with("foo") - .and_return(client_foo) - allow(ChefVault::ChefPatch::User) - .to receive(:load) - .with("bar") - .and_raise(http_not_found) + item["foo"] = "bar" end -end -RSpec.describe ChefVault::Item do - before do - @orig_stdout = $stdout - $stdout = File.open(File::NULL, "w") - end - - after do - $stdout = @orig_stdout - end - - subject(:item) { ChefVault::Item.new("foo", "bar") } - describe "vault probe predicates" do before do # a normal data bag item @db = { "foo" => "..." } @dbi = Chef::DataBagItem.new @@ -212,12 +157,11 @@ describe "#save" do context 'when item["id"] is bar.bar' do let(:item) { ChefVault::Item.new("foo", "bar.bar") } it "raises an error on save with an invalid item['id']" do - expect { item.save }.to raise_error - + expect { item.save }.to raise_error(RuntimeError) end end it "validates that the id of the vault matches the id of the keys data bag" do item = ChefVault::Item.new("foo", "bar") @@ -243,53 +187,163 @@ node = double("node", name: "testnode") query = double("query") allow(Chef::Search::Query).to receive(:new).and_return(query) allow(query).to receive(:search).and_yield(node) - client = double("client", - name: "testclient", - public_key: OpenSSL::PKey::RSA.new(1024).public_key) - allow(ChefVault::ChefPatch::ApiClient).to receive(:load).and_return(client) + client_key = double("client_key", + name: "testnode", + public_key: OpenSSL::PKey::RSA.new(1024).public_key) + allow(item).to receive(:load_actor).with("testnode", "clients").and_return(client_key) expect(item).not_to receive(:save) expect(keys).to receive(:save) item.refresh end end describe "#clients" do - include BorkedNodeWithoutPublicKey + context "when search returns a node with a valid client backing it and one without a valid client" do + let(:node_with_valid_client) { double("chef node valid") } + let(:node_without_valid_client) { double("chef node no valid client") } + let(:query_result) { double("chef search results") } + let(:client_key) { double("chef key") } - it "should not blow up when search returns a node without a public key" do - # try to set clients when we know a node is missing a public key - # this should not die as of v2.4.1 - expect { @vaultitem.clients("*:*") }.not_to raise_error + before do + # node with valid client proper loads client key + allow(node_with_valid_client).to receive(:name).and_return("foo") + allow(item).to receive(:load_actor).with("foo", "clients").and_return(client_key) + privkey = OpenSSL::PKey::RSA.new(1024) + pubkey = privkey.public_key + allow(client_key).to receive(:key).and_return(pubkey.to_pem) + allow(client_key).to receive(:name).and_return("foo") + allow(client_key).to receive(:type).and_return("clients") + + # node without client throws relevant error on key load + allow(node_without_valid_client).to receive(:name).and_return("bar") + allow(item).to receive(:load_actor).with("bar", "clients").and_raise(ChefVault::Exceptions::ClientNotFound) + + allow(query_result) + .to receive(:search) + .with(Symbol, String) + .and_yield(node_with_valid_client).and_yield(node_without_valid_client) + allow(Chef::Search::Query) + .to receive(:new) + .and_return(query_result) + end + + it "should not blow up when search returns a node without a public key" do + # try to set clients when we know a node is missing a public key + # this should not die as of v2.4.1 + expect { item.clients("*:*") }.not_to raise_error + end + + it "should emit a warning if search returns a node without a public key" do + # it should however emit a warning that you have a borked node + expect(ChefVault::Log).to receive(:warn).with(/node 'bar' has no private key; skipping/) + item.clients("*:*") + end end - it "should emit a warning if search returns a node without a public key" do - # it should however emit a warning that you have a borked node - expect { @vaultitem.clients("*:*") } - .to output(/node 'bar' has no private key; skipping/).to_stdout + context "when a Chef::ApiClient is passed" do + let(:client) { Chef::ApiClient.new } + let(:client_name) { "foo" } + let(:client_key) { double("chef key") } + + before do + client.name client_name + privkey = OpenSSL::PKey::RSA.new(1024) + pubkey = privkey.public_key + allow(item).to receive(:load_actor).with(client_name, "clients").and_return(client_key) + allow(client_key).to receive(:key).and_return(pubkey.to_pem) + allow(client_key).to receive(:name).and_return(client_name) + allow(client_key).to receive(:type).and_return("clients") + end + + context "when no action is passed" do + it "default to add and properly add the client" do + item.clients(client) + expect(item.get_clients).to include(client_name) + end + + it "does not perform a query" do + expect(Chef::Search::Query).not_to receive(:new) + item.clients(client) + end + end + + context "when the delete action is passed on an existing client" do + before do + # add the client + item.clients(client) + end + + it "properly deletes the client" do + item.clients(client, :delete) + expect(item.get_clients).to_not include(client_name) + end + + it "does not perform a query" do + expect(Chef::Search::Query).not_to receive(:new) + item.clients(client, :delete) + end + end end - it "should accept a client object and not perform a search" do - client = Chef::ApiClient.new - client.name "foo" - privkey = OpenSSL::PKey::RSA.new(1024) - pubkey = privkey.public_key - client.public_key(pubkey.to_pem) - expect(Chef::Search::Query).not_to receive(:new) - expect(ChefVault::ChefPatch::User).not_to receive(:load) - @vaultitem.clients(client) - expect(@vaultitem.clients).to include("foo") + context "when an Array with named clients is passed" do + let(:client) { Chef::ApiClient.new } + let(:clients) { Array.new } + let(:client_name) { "foo" } + let(:client_key) { double("chef key") } + + before do + clients << client_name + client.name client_name + privkey = OpenSSL::PKey::RSA.new(1024) + pubkey = privkey.public_key + allow(item).to receive(:load_actor).with(client_name, "clients").and_return(client_key) + allow(client_key).to receive(:key).and_return(pubkey.to_pem) + allow(client_key).to receive(:name).and_return(client_name) + allow(client_key).to receive(:type).and_return("clients") + end + + context "when no action is passed" do + it "default to add and properly add the client" do + item.clients(clients) + expect(item.get_clients).to include(client_name) + end + + it "does not perform a query" do + expect(Chef::Search::Query).not_to receive(:new) + item.clients(clients) + end + end + + context "when the delete action is passed on an existing client" do + before do + # add the client + item.clients(clients) + end + + it "properly deletes the client" do + item.clients(clients, :delete) + expect(item.get_clients).to_not include(client_name) + end + + it "does not perform a query" do + expect(Chef::Search::Query).not_to receive(:new) + item.clients(clients, :delete) + end + end end end describe "#admins" do - include BorkedNodeWithoutPublicKey + before do + allow(item).to receive(:load_actor).with("foo", "admins").and_raise(ChefVault::Exceptions::AdminNotFound) + end it "should blow up if you try to use a node without a public key as an admin" do - expect { @vaultitem.admins("foo,bar") } + expect { item.admins("foo,bar") } .to raise_error(ChefVault::Exceptions::AdminNotFound) end end describe "#raw_keys" do