Tom Wilson
Tom Wilson

Reputation: 837

Chef cookbook_file and template resource: how to test that source exists?

If I use a template resource in a recipe

template '/home/user1/foo' do
  source 'bar'
end

And I test using chefspec/rspec

...
expect(chef_run).to render_file('/home/user1/foo')
expect(chef_run).to create_template('/home/user1/foo')
expect(chef_run).to create_template('/home/user1/foo').with_source 'bar
...

All tests pass...
even if the source template "bar" is missing from the cookbook.

This leaves me with a cookbook that passes all the tests I can think of, but will fail when I try to run it. Same for a cookbook_file resource.

I can see how it would be a can of worms to test all the places where the resource could look for the source file (per the "File Specificity" section in the resource docs) but it still seems like a big hole in the test tools.

Am I overlooking a way to test for the existence of the source file, or doing something wrong at a more basic level? Or is this just how it is, and everyone lives with it?

Upvotes: 1

Views: 2474

Answers (1)

Tom Wilson
Tom Wilson

Reputation: 837

OK, this is a little convoluted, but it works. For this recipe fragment:

template '/home/user1/foo' do
  source 'bar'
end

When no template source file "bar" exists, this Chefspec expectation passes, despite the missing source file.

expect(chef_run).to render_file('/home/user1/foo')

But this fails with Chef::Exceptions::FileNotFound, providing a way to test if the source exists:

expect(chef_run).to render_file('/home/user1/foo').with_content(/.*/)

Apparently, the with_content() method forces the source reference to be resolved so the matcher can render the file for comparison purposes. If you don't really care about matching specific content, /.*/ matches anything so you can still test that the source exists.

more, in response to @ user3670408 's issue...

Chefspec has to render the file content to a string to compare with the .with_content() argument The issue seems to be that the actual cookbook file you're using is a binary file. What's going on is probably something like this:

# Create a bad UTF-8 file
File.open('bad_utf8.bin', mode: 'wb') { |file| file.putc(0xfe) }
# Read contents and try to use the resulting string
File.read('bad_utf8.bin').match(/.*/)
# Result is:  ArgumentError: invalid byte sequence in UTF-8

So in general, the .with_content(/.*/) trick won't work with binary cookbook files unless you do something hack-ey like catch the ArgumentError.

Upvotes: 1

Related Questions