I am still a bit confused about what would be the best way to write ChefSpec for library, especially those only process and return value.
So this is my library:
# Chef/Cookbooks/MyCookbook/Libraries/get_service_account_password.rb
module MyOrg
module GetServiceAccountPassword
def get_service_account_password(base_username)
service_account_name = "#{node['service_account_prefixes'][node['server_type']]}_#{base_username}"
key_vault = Chef::EncryptedDataBagItem.load("key_vault", node['key_vault_data_bag'])
key_vault_service_principal = {
'tenant_id' => key_vault["tenant_id"],
'client_id' => key_vault["client_id"],
'secret' => key_vault["key_vault_secret"]
}
service_account_password = akv_get_secret(key_vault["key_vault_name"],
service_account_name.gsub("_","-").downcase,
key_vault_service_principal)
end
end
end
Chef::Recipe.send(:include, MyOrg::GetServiceAccountPassword)
This is my ChefSpec for the recipe - which under development
# Chef/Cookbooks/MyCookbook/Spec/get_service_account_password_spec.rb
require 'spec_helper'
service_account_prefixes = {
Server: "SV"
}
key_vault = {'key_vault_name' => 'test'}
key_vault_service_principal = {
'tenant_id' => '123456',
'client_id' => '123456',
'secret' => '123456'
}
describe MyOrg::GetServiceAccountPassword do
let(:subject) { Class.new { extend MyOrg::GetServiceAccountPassword } }
# Not sure if this is required or not, I think they would be required if it tests the recipe
# but if the test calls the function in the library, then these node attributes wont be
# passed to the library's function and throw error as I mention below.
let(:chef_run) do
ChefSpec::SoloRunner.new do |node|
node.default['server_type'] = "Server"
node.default['service_account_prefixes'] = service_account_prefixes
node.default['domain_prefix'] = "MyOrg"
node.default['key_vault_data_bag'] = "test"
end.converge(described_recipe)
end
before do
service_account_name = "MyOrg\\SV_jenkins"
allow(Chef::EncryptedDataBagItem).to receive(:load).with('key_vault', 'test').and_return({
'client_id' => '123456',
'key_vault_name' => 'test',
'key_vault_secret' => '123456',
'tenant_id' => '123456'
})
end
it 'returns the right value' do
#ATTEMPT 1: expect{subject}.to_not raise_error
# this passed, but when I removed stuff in the recipe, it still passed
#instead of failed and raised error
# And I am not sure if checking for error is the right way to test this or not
#ATTEMPT 2: expect(get_full_service_account_name("jenkins")).to be("MyOrg\\SV_jenkins")
# error returned: undefined local variable or method `node'
# Passing all node attributes to the function via parameters is not feasible since I have
# quite a few different node attributes in the library
end
end
So my question here is what's the best way to test and evaluate this library. Should I place an assertion to make sure it returns the right value? Or should I place an expect statement to make sure it wont throw an error ?
The former seems like I need to call the function in that library to return the value which I reckon it gonna throw errors because I use node attributes in the library and the node attributes I defined earlier in the ChefSpec just couldnt get sync to the library it called. So I dont really know how to do this properly.