1

I have a bunch of repeated code that manipulates the Puppet catalogue object (notice the repeated content = lines):

require 'nokogiri'

describe 'role::jenkins' do
  before(:each) do
    @jobs = catalogue.resource_keys.select{|k,v| k == 'Jenkins::Jobs'}.map{|k,v| v}
  end

  it 'Jenkins jobs should be valid XML' do
    @jobs.each do |j|
      content = catalogue.resource('file', "/tmp/#{j}.xml").send(:parameters)[:content]
      result = Nokogiri::XML(content).errors.empty?
      if ! result
        puts "    Job #{j} does NOT have valid XML"
      end
      expect(result).to be true
    end
  end 

  it "XML should contain a variables.json snippet that is valid JSON" do
    @jobs.each do |j|
      content = catalogue.resource('file', "/tmp/#{j}.xml").send(:parameters)[:content]
      if content.match(/cat << EOF > #{json_file}.*?EOF/m)
        json_snippet = content.match(/#{json_file}(.*?)EOF/m)[1]
        expect { JSON.parse(json_snippet) }.to_not raise_error
      end
    end
  end
end

As can be seen, I have moved a long query into a before(:each) block and saved it in an instance variable. That makes it available in the it blocks.

What I do not understand is how I could define a method for the content = lines, like:

def content(file_name)
  catalogue.resource('file', file_name).send(:parameters)[:content]
end

If I knew how to do that I could clean up this code considerably. What I cannot figure out is where I could place this def block, if it is even possible to do this.

Alex Harvey
  • 14,494
  • 5
  • 61
  • 97
  • This actually seems like a pure RSpec question since there are not extended matchers. Is that method of mocking a resource actually working? I thought that was only an experimental feature of that one rspec-puppet helper gem. – Matthew Schuchard May 07 '17 at 22:02
  • 1
    It definitely works. It is useful to know this trick, and undocumented I think. I could be wrong but my understanding is that the 'catalogue' object (note the UK spelling, whereas US spelling 'catalog' is used elsewhere) is made available by rspec-puppet. – Alex Harvey May 08 '17 at 00:48
  • Fair enough. Puppet does actually use `catalog` in its source code for that object just like you said. I probably should dig into this some. Thanks for the info! – Matthew Schuchard May 08 '17 at 02:04

1 Answers1

0

I have made a silly mistake (thought I had already tried something when actually I had not).

The answer is just put the def inside the before block as well:

require 'nokogiri'

describe 'role::jenkins' do
  before(:each) do
    @jobs = catalogue.resource_keys.select{|k,v| k == 'Jenkins::Jobs'}.map{|k,v| v}

    def content(file_name)
      catalogue.resource('file', file_name).send(:parameters)[:content]
    end
  end

  it 'Jenkins jobs should be valid XML' do
    @jobs.each do |j|
      result = Nokogiri::XML(content("/tmp/#{j}.xml").errors.empty?
      if ! result
        puts "    Job #{j} does NOT have valid XML"
      end
      expect(result).to be true
    end
  end 

  it 'XML should contain a variables.json snippet that is valid JSON' do
    @jobs.each do |j|
      content = content("/tmp/#{j}.xml")
      if content.match(/cat << EOF > #{json_file}.*?EOF/m)
        json_snippet = content.match(/#{json_file}(.*?)EOF/m)[1]
        expect { JSON.parse(json_snippet) }.to_not raise_error
      end
    end
  end
end

Please let me know though if anyone can see any further improvements that could be made!

Alex Harvey
  • 14,494
  • 5
  • 61
  • 97