Skip to content

Commit

Permalink
Merge pull request #75 from willaerk/have_resource_count
Browse files Browse the repository at this point in the history
Asserting an exact number of classes or resources
  • Loading branch information
timtim123456 committed Feb 23, 2013
2 parents f2e276c + dd64761 commit 8dc9ecb
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 5 deletions.
35 changes: 35 additions & 0 deletions README.md
Expand Up @@ -140,6 +140,41 @@ it { should contain_service('keystone').without(
)}
```

#### Checking the number of resources

You can test the number of resources in the catalogue with the
`have_resource_count` matcher.

```ruby
it { should have_resource_count(2) }
```

The number of classes in the catalogue can be checked with the
`have_class_count` matcher.

```ruby
it { should have_class_count(2) }
```

You can also test the number of a specific resource type, by using the generic
`have_<resource type>_count` matcher.

```ruby
it { should have_exec_resource_count(1) }
```

This last matcher also works for defined types. If the resource type contains
::, you can replace it with __ (two underscores).

```ruby
it { should have_logrotate__rule_count(3) }
```

*NOTE*: when testing a class, the catalogue generated will always contain at
least one class, the class under test. The same holds for defined types, the
catalogue generated when testing a defined type will have at least one resource
(the defined type itself).

### Writing tests

#### Basic test structure
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec-puppet/matchers.rb
Expand Up @@ -2,3 +2,5 @@
require 'rspec-puppet/matchers/create_resource'
require 'rspec-puppet/matchers/include_class'
require 'rspec-puppet/matchers/run'
require 'rspec-puppet/matchers/count_generic'
require 'rspec-puppet/matchers/dynamic_matchers'
86 changes: 86 additions & 0 deletions lib/rspec-puppet/matchers/count_generic.rb
@@ -0,0 +1,86 @@
module RSpec::Puppet
module ManifestMatchers
class CountGeneric
def initialize(type, count, *method)
if type.nil?
@type = method[0].to_s.gsub(/^have_(.+)_resource_count$/, '\1')
else
@type = type
end
@referenced_type = referenced_type(@type)
@expected_number = count.to_i
end

def matches?(catalogue)
ret = true

case @type
when "class"
actual = catalogue.resources.select do |res|
res.type.eql? "Class"
end

# Puppet automatically adds Class[main] and Class[Settings]
@actual_number = actual.length - 2
when "resource"
actual = catalogue.resources.select do |res|
!res.type.eql? "Class" and !res.type.eql? "Node"
end

# Puppet automatically adds Stage[main]
@actual_number = actual.length - 1
else
actual = catalogue.resources.select do |res|
res.type.eql? "#{@referenced_type}"
end

@actual_number = actual.length
end

unless @actual_number == @expected_number
ret = false
end

ret
end

def description
desc = []

desc << "contain exactly #{@expected_number}"
if @type == "class"
desc << "#{@expected_number == 1 ? "class" : "classes" }"
else
unless @type == "resource"
desc << "#{@referenced_type}"
end
desc << "#{@expected_number == 1 ? "resource" : "resources" }"
end

desc.join(" ")
end

def failure_message_for_should
"expected that the catalogue would " + description + " but it contains #{@actual_number}"
end

def failure_message_for_should_not
"expected that the catalogue would not " + description + " but it does"
end

private

def referenced_type(type)
type.split('__').map { |r| r.capitalize }.join('::')
end
end

def have_class_count(count)
RSpec::Puppet::ManifestMatchers::CountGeneric.new('class', count)
end

def have_resource_count(count)
RSpec::Puppet::ManifestMatchers::CountGeneric.new('resource', count)
end
end
end
5 changes: 0 additions & 5 deletions lib/rspec-puppet/matchers/create_generic.rb
Expand Up @@ -144,10 +144,5 @@ def errors
@errors.nil? ? "" : " with #{@errors.join(', and parameter ')}"
end
end

def method_missing(method, *args, &block)
return RSpec::Puppet::ManifestMatchers::CreateGeneric.new(method, *args, &block) if method.to_s =~ /^(create|contain)_/
super
end
end
end
9 changes: 9 additions & 0 deletions lib/rspec-puppet/matchers/dynamic_matchers.rb
@@ -0,0 +1,9 @@
module RSpec::Puppet
module ManifestMatchers
def method_missing(method, *args, &block)
return RSpec::Puppet::ManifestMatchers::CreateGeneric.new(method, *args, &block) if method.to_s =~ /^(create|contain)_/
return RSpec::Puppet::ManifestMatchers::CountGeneric.new(nil, args[0], method) if method.to_s =~ /^have_.+_count$/
super
end
end
end
2 changes: 2 additions & 0 deletions rspec-puppet.gemspec
Expand Up @@ -18,6 +18,8 @@ Gem::Specification.new do |s|
'lib/rspec-puppet/matchers/create_resource.rb',
'lib/rspec-puppet/matchers/include_class.rb',
'lib/rspec-puppet/matchers/run.rb',
'lib/rspec-puppet/matchers/count_generic.rb',
'lib/rspec-puppet/matchers/dynamic_matchers.rb',
'lib/rspec-puppet/matchers.rb',
'lib/rspec-puppet/setup.rb',
'lib/rspec-puppet/support.rb',
Expand Down
4 changes: 4 additions & 0 deletions spec/classes/sysctl_common_spec.rb
Expand Up @@ -37,6 +37,10 @@

it { should create_class("sysctl::common")\
.with_test_param("yes") }
it { should have_class_count(1) }
it { should have_exec_resource_count(1) }
# one exec resource and one notify resource from the default node (site.pp)
it { should have_resource_count(2) }
end

describe 'sysctl::common' do
Expand Down
1 change: 1 addition & 0 deletions spec/defines/sysctl_spec.rb
Expand Up @@ -11,4 +11,5 @@
.with_onlyif("match vm.swappiness[.='60'] size == 0") \
.with_notify('Exec[sysctl/reload]')\
.without_foo }
it { should have_sysctl_resource_count(1) }
end

0 comments on commit 8dc9ecb

Please sign in to comment.