Search code examples
rubychef-infracookbooklwrp

How to write regression tests for custom Chef resources?


Given the minimal example

# resources/novowel.rb
resource_name :novowel
property :name, String, name_property: true, regex: /\A[^aeiou]\z/

I would like to write the unit tests in spec/unit/resources/novowel_spec.rb

  • Resource 'novowel' for name should accept 'k'
  • Resource 'novowel' for name should accept '&'
  • Resource 'novowel' for name should NOT accept 'a'
  • Resource 'novowel' for name should NOT accept 'mm'

to ensure that the name property still works correctly even if the regex is changed for some reason.

I browsed several top notch Chef cookbooks but could not find references for such testing.

How can it be done? Feel free to provide more complex examples with explicitly subclassing Chef::Resource if that helps achieve the task.

Update 1: Could it be that Chef does NOT FAIL when a property does not fit the regex? Clearly this should not work:

link '/none' do
  owner  'r<oo=t'
  to     '/usr'
end

but chef-apply (12.13.37) does not complain about r<oo=t not matching owner_valid_regex. It simply converges as if owner would not have been provided.


Solution

  • We wrap the DSL inside a little Ruby in order to know the name of the resource's Ruby class:

    # libraries/no_vowel_resource.rb
    require 'chef/resource'
    
    class Chef
      class Resource
        class NoVowel < Chef::Resource
          resource_name :novowel
          property :letter, String, name_property: true, regex: /\A[^aeiou]\z/
          property :author, String, regex: /\A[^aeiou]+\z/
        end
      end
    end
    

    and now we can use RSpec with

    # spec/unit/libraries/no_vowel_resource_spec.rb
    require 'spec_helper'
    
    require_relative '../../../libraries/no_vowel_resource.rb'
    
    describe Chef::Resource::NoVowel do
      before(:each) do
        @resource = described_class.new('k')
      end
    
      describe "property 'letter'" do
        it "should accept the letter 'k'" do
          @resource.letter = 'k'
          expect(@resource.letter).to eq('k')
        end
    
        it "should accept the character '&'" do
          @resource.letter = '&'
          expect(@resource.letter).to eq('&')
        end
    
        it "should NOT accept the vowel 'a'" do
          expect { @resource.letter = 'a' }.to raise_error(Chef::Exceptions::ValidationFailed)
        end
    
        it "should NOT accept the word 'mm'" do
          expect { @resource.letter = 'mm' }.to raise_error(Chef::Exceptions::ValidationFailed)
        end
      end
    
      describe "property 'author'" do
        it "should accept a String without vowels" do
          @resource.author = 'cdrngr'
          expect(@resource.author).to eq('cdrngr')
        end
    
        it "should NOT accept a String with vowels" do
          expect { @resource.author = 'coderanger' }.to raise_error(Chef::Exceptions::ValidationFailed)
        end
      end
    end