[DRE-commits] [ruby-rspec-puppet] 01/04: Imported Upstream version 1.0.1
Thomas Bechtold
toabctl at moszumanska.debian.org
Fri Dec 27 07:10:37 UTC 2013
This is an automated email from the git hooks/post-receive script.
toabctl pushed a commit to branch master
in repository ruby-rspec-puppet.
commit 8925cacbdc9117bd78cf0f0be2b925a689eaf2db
Author: Thomas Bechtold <toabctl at debian.org>
Date: Fri Dec 27 07:40:31 2013 +0100
Imported Upstream version 1.0.1
---
LICENSE | 20 --
README.md | 94 ++++++++--
Rakefile | 7 -
checksums.yaml.gz | Bin 0 -> 270 bytes
lib/rspec-puppet.rb | 45 +++++
lib/rspec-puppet/errors.rb | 83 +++++++++
lib/rspec-puppet/example/class_example_group.rb | 56 +-----
lib/rspec-puppet/example/define_example_group.rb | 58 +-----
lib/rspec-puppet/example/function_example_group.rb | 56 +++---
lib/rspec-puppet/example/host_example_group.rb | 27 +--
lib/rspec-puppet/matchers.rb | 4 +-
lib/rspec-puppet/matchers/compile.rb | 133 +++++++++++++
lib/rspec-puppet/matchers/count_generic.rb | 73 ++++++++
lib/rspec-puppet/matchers/create_generic.rb | 207 +++++++++++++++------
lib/rspec-puppet/matchers/create_resource.rb | 53 ------
lib/rspec-puppet/matchers/dynamic_matchers.rb | 17 ++
lib/rspec-puppet/matchers/include_class.rb | 3 +-
lib/rspec-puppet/matchers/parameter_matcher.rb | 117 ++++++++++++
lib/rspec-puppet/matchers/run.rb | 80 +++++---
lib/rspec-puppet/setup.rb | 91 +++++----
lib/rspec-puppet/support.rb | 161 +++++++++++++++-
metadata.yml | 50 ++---
rspec-puppet.gemspec | 47 -----
spec/classes/boolean_regexp_spec.rb | 10 -
spec/classes/boolean_spec.rb | 11 --
spec/classes/sysctl_common_spec.rb | 40 ----
spec/defines/sysctl_before_spec.rb | 26 ---
spec/defines/sysctl_spec.rb | 14 --
spec/fixtures/manifests/site.pp | 7 -
spec/fixtures/modules/boolean/manifests/init.pp | 12 --
spec/fixtures/modules/sysctl/manifests/init.pp | 39 ----
spec/functions/split_spec.rb | 17 --
spec/hosts/foo_spec.rb | 6 -
spec/hosts/testhost_spec.rb | 5 -
spec/spec_helper.rb | 6 -
35 files changed, 1008 insertions(+), 667 deletions(-)
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 08b22f7..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2011 Tim Sharpe
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index 7c64d61..37ea54a 100644
--- a/README.md
+++ b/README.md
@@ -63,16 +63,6 @@ end
### Matchers
-#### Checking if a class has been included
-
-You can test if a class has been included in the catalogue with the
-`include_class` matcher. It takes the class name as a string as its only
-argument
-
-```ruby
-it { should include_class('foo') }
-```
-
#### Checking if a resource exists
You can test if a resource exists in the catalogue with the generic
@@ -82,6 +72,13 @@ You can test if a resource exists in the catalogue with the generic
it { should contain_augeas('bleh') }
```
+You can also test if a class has been included in the catalogue with the
+same matcher.
+
+```ruby
+it { should contain_class('foo') }
+```
+
If your resource type includes :: (e.g.
`foo::bar` simply replace the :: with __ (two underscores).
@@ -96,6 +93,13 @@ the generic `with_<parameter>` chains.
it { should contain_package('mysql-server').with_ensure('present') }
```
+If you want to specify that the given parameters should be the only ones passed
+to the resource, use the `only_with_<parameter>` chains.
+
+```ruby
+it { should contain_package('httpd').only_with_ensure('latest') }
+```
+
You can use the `with` method to verify the value of multiple parameters.
```ruby
@@ -107,6 +111,16 @@ it do should contain_service('keystone').with(
) end
```
+The same holds for the `only_with` method, which in addition verifies the exact
+set of parameters and values for the resource in the catalogue.
+
+```ruby
+it do should contain_user('luke').only_with(
+ 'ensure' => 'present',
+ 'uid' => '501'
+) end
+```
+
You can also test that specific parameters have been left undefined with the
generic `without_<parameter>` chains.
@@ -123,6 +137,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>_resource_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_resource_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
@@ -180,6 +229,19 @@ You can set them with a hash
let(:facts) { {:operatingsystem => 'Debian', :kernel => 'Linux', ...} }
```
+You can also create a set of default facts provided to all specs in your spec_helper:
+
+``` ruby
+RSpec.configure do |c|
+ c.default_facts = {
+ :operatingsystem => 'Ubuntu'
+ }
+end
+```
+
+Any facts you provide with `let(:facts)` in a spec will automatically be merged on top
+of the default facts.
+
#### Specifying the path to find your modules
I recommend setting a default module path by adding the following code to your
@@ -206,7 +268,7 @@ Puppet functions.
```ruby
it 'should be able to do something' do
- subject.call('foo') == 'bar'
+ subject.call(['foo']) == 'bar'
end
```
@@ -250,7 +312,7 @@ Or by using the `call` method on the subject directly
```ruby
it 'something' do
- subject.call('foo', 'bar', ['baz'])
+ subject.call(['foo', 'bar', ['baz']])
end
```
@@ -267,8 +329,8 @@ Or by using any of the existing RSpec matchers on the subject directly
```ruby
it 'something' do
- subject.call('foo') == 'bar'
- subject.call('baz').should be_an Array
+ subject.call(['foo']) == 'bar'
+ subject.call(['baz']).should be_an Array
end
```
@@ -286,7 +348,7 @@ Or by using the existing `raises_error` RSpec matcher
```ruby
it 'something' do
- expect { subject.call('a', 'b') }.should raise_error(Puppet::ParseError)
- expect { subject.call('a') }.should_not raise_error(Puppet::ParseError)
+ expect { subject.call(['a', 'b']) }.should raise_error(Puppet::ParseError)
+ expect { subject.call(['a']) }.should_not raise_error(Puppet::ParseError)
end
```
diff --git a/Rakefile b/Rakefile
deleted file mode 100644
index 19aad28..0000000
--- a/Rakefile
+++ /dev/null
@@ -1,7 +0,0 @@
-require 'rake'
-require 'rspec/core/rake_task'
-
-task :default => :test
-task :spec => :test
-
-RSpec::Core::RakeTask.new(:test)
diff --git a/checksums.yaml.gz b/checksums.yaml.gz
new file mode 100644
index 0000000..a2858ba
Binary files /dev/null and b/checksums.yaml.gz differ
diff --git a/lib/rspec-puppet.rb b/lib/rspec-puppet.rb
index b0d64c3..32709d2 100644
--- a/lib/rspec-puppet.rb
+++ b/lib/rspec-puppet.rb
@@ -2,14 +2,59 @@ require 'puppet'
require 'rspec'
require 'fileutils'
require 'tmpdir'
+require 'rspec-puppet/errors'
require 'rspec-puppet/matchers'
require 'rspec-puppet/example'
require 'rspec-puppet/setup'
+begin
+ require 'puppet/test/test_helper'
+rescue LoadError
+end
+
RSpec.configure do |c|
c.add_setting :module_path, :default => '/etc/puppet/modules'
c.add_setting :manifest_dir, :default => nil
c.add_setting :manifest, :default => nil
c.add_setting :template_dir, :default => nil
c.add_setting :config, :default => nil
+ c.add_setting :confdir, :default => '/etc/puppet'
+ c.add_setting :default_facts, :default => {}
+ c.add_setting :hiera_config, :default => '/dev/null'
+
+ if defined?(Puppet::Test::TestHelper)
+ begin
+ Puppet::Test::TestHelper.initialize
+ rescue NoMethodError
+ Puppet::Test::TestHelper.before_each_test
+ end
+
+ c.before :all do
+ begin
+ Puppet::Test::TestHelper.before_all_tests
+ rescue
+ end
+ end
+
+ c.after :all do
+ begin
+ Puppet::Test::TestHelper.after_all_tests
+ rescue
+ end
+ end
+
+ c.before :each do
+ begin
+ Puppet::Test::TestHelper.before_each_test
+ rescue
+ end
+ end
+
+ c.after :each do
+ begin
+ Puppet::Test::TestHelper.after_each_test
+ rescue
+ end
+ end
+ end
end
diff --git a/lib/rspec-puppet/errors.rb b/lib/rspec-puppet/errors.rb
new file mode 100644
index 0000000..96dd0b3
--- /dev/null
+++ b/lib/rspec-puppet/errors.rb
@@ -0,0 +1,83 @@
+module RSpec::Puppet
+ module Errors
+ class MatchError < StandardError
+ attr_reader :param, :expected, :actual, :negative
+
+ def initialize(param, expected, actual, negative)
+ @param = param
+ @expected = expected.inspect
+ @actual = actual.inspect
+ @negative = negative
+ end
+
+ def message
+ if negative == true
+ "#{param} not set to #{expected} but it is set to #{actual}"
+ else
+ "#{param} set to #{expected} but it is set to #{actual}"
+ end
+ end
+
+ def to_s
+ message
+ end
+ end
+
+ class RegexpMatchError < MatchError
+ def message
+ if negative == true
+ "#{param} not matching #{expected} but its value of #{actual} does"
+ else
+ "#{param} matching #{expected} but its value of #{actual} does not"
+ end
+ end
+ end
+
+ class ProcMatchError < MatchError
+ def message
+ if negative == true
+ "#{param} passed to the block would not return `#{expected}` but it did"
+ else
+ "#{param} passed to the block would return `#{expected}` but it is `#{actual}`"
+ end
+ end
+ end
+
+ class RelationshipError < StandardError
+ attr_reader :from, :to
+
+ def initialize(from, to)
+ @from = from
+ @to = to
+ end
+
+ def to_s
+ message
+ end
+ end
+
+ class BeforeRelationshipError < RelationshipError
+ def message
+ "#{from} to come before #{to} in the graph"
+ end
+ end
+
+ class RequireRelationshipError < RelationshipError
+ def message
+ "#{from} to require #{to} in the graph"
+ end
+ end
+
+ class NotifyRelationshipError < RelationshipError
+ def message
+ "#{from} to notify #{to}"
+ end
+ end
+
+ class SubscribeRelationshipError < RelationshipError
+ def message
+ "#{from} to be subscribed to #{to}"
+ end
+ end
+ end
+end
diff --git a/lib/rspec-puppet/example/class_example_group.rb b/lib/rspec-puppet/example/class_example_group.rb
index bc137b8..994bf88 100644
--- a/lib/rspec-puppet/example/class_example_group.rb
+++ b/lib/rspec-puppet/example/class_example_group.rb
@@ -4,61 +4,7 @@ module RSpec::Puppet
include RSpec::Puppet::Support
def subject
- @catalogue ||= catalogue
- end
-
- def catalogue
- vardir = Dir.mktmpdir
- Puppet[:vardir] = vardir
- Puppet[:hiera_config] = File.join(vardir, "hiera.yaml") if Puppet[:hiera_config] == File.expand_path("/dev/null")
- Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
- Puppet[:manifestdir] = self.respond_to?(:manifest_dir) ? manifest_dir : RSpec.configuration.manifest_dir
- Puppet[:manifest] = self.respond_to?(:manifest) ? manifest : RSpec.configuration.manifest
- Puppet[:templatedir] = self.respond_to?(:template_dir) ? template_dir : RSpec.configuration.template_dir
- Puppet[:config] = self.respond_to?(:config) ? config : RSpec.configuration.config
-
- klass_name = self.class.top_level_description.downcase
-
- # If we're testing a standalone module (i.e. one that's outside of a
- # puppet tree), the autoloader won't work, so we need to fudge it a bit.
- if File.exists?(File.join(Puppet[:modulepath], 'manifests', 'init.pp'))
- path_to_manifest = File.join([Puppet[:modulepath], 'manifests', klass_name.split('::')[1..-1]].flatten)
- import_str = "import '#{Puppet[:modulepath]}/manifests/init.pp'\nimport '#{path_to_manifest}.pp'\n"
- elsif File.exists?(Puppet[:modulepath])
- import_str = "import '#{Puppet[:manifest]}'\n"
- else
- import_str = ""
- end
-
- if self.respond_to? :pre_condition
- if pre_condition.kind_of?(Array)
- pre_cond = pre_condition.join("\n")
- else
- pre_cond = pre_condition
- end
- else
- pre_cond = ''
- end
-
- if !self.respond_to?(:params) || params == {}
- code = import_str + "include #{klass_name}"
- else
- code = import_str + 'class' + " { \"" + klass_name + "\": " + params.keys.map { |r| "#{r.to_s} => #{params[r].inspect}"
- }.join(',' ) + " }"
- end
- code = pre_cond + "\n" + code
-
- nodename = self.respond_to?(:node) ? node : Puppet[:certname]
- facts_val = {
- 'hostname' => nodename.split('.').first,
- 'fqdn' => nodename,
- 'domain' => nodename.split('.').last,
- }
- facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
-
- catalogue = build_catalog(nodename, facts_val, code)
- FileUtils.rm_rf(vardir) if File.directory?(vardir)
- catalogue
+ @catalogue ||= catalogue(:class)
end
end
end
diff --git a/lib/rspec-puppet/example/define_example_group.rb b/lib/rspec-puppet/example/define_example_group.rb
index 901be1d..3da87bb 100644
--- a/lib/rspec-puppet/example/define_example_group.rb
+++ b/lib/rspec-puppet/example/define_example_group.rb
@@ -4,63 +4,7 @@ module RSpec::Puppet
include RSpec::Puppet::Support
def subject
- @catalogue ||= catalogue
- end
-
- def catalogue
- define_name = self.class.top_level_description.downcase
-
- vardir = Dir.mktmpdir
- Puppet[:vardir] = vardir
- Puppet[:hiera_config] = File.join(vardir, "hiera.yaml") if Puppet[:hiera_config] == File.expand_path("/dev/null")
- Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
- Puppet[:manifestdir] = self.respond_to?(:manifest_dir) ? manifest_dir : RSpec.configuration.manifest_dir
- Puppet[:manifest] = self.respond_to?(:manifest) ? manifest : RSpec.configuration.manifest
- Puppet[:templatedir] = self.respond_to?(:template_dir) ? template_dir : RSpec.configuration.template_dir
- Puppet[:config] = self.respond_to?(:config) ? config : RSpec.configuration.config
-
- # If we're testing a standalone module (i.e. one that's outside of a
- # puppet tree), the autoloader won't work, so we need to fudge it a bit.
- if File.exists?(File.join(Puppet[:modulepath], 'manifests', 'init.pp'))
- path_to_manifest = File.join([Puppet[:modulepath], 'manifests', define_name.split('::')[1..-1]].flatten)
- import_str = "import '#{Puppet[:modulepath]}/manifests/init.pp'\nimport '#{path_to_manifest}.pp'\n"
- elsif File.exists?(Puppet[:modulepath])
- import_str = "import '#{Puppet[:manifest]}'\n"
- else
- import_str = ""
- end
-
- if self.respond_to? :params
- param_str = params.keys.map { |r|
- "#{r.to_s} => #{params[r].inspect}"
- }.join(', ')
- else
- param_str = ""
- end
-
- if self.respond_to? :pre_condition
- if pre_condition.kind_of?(Array)
- pre_cond = pre_condition.join("\n")
- else
- pre_cond = pre_condition
- end
- else
- pre_cond = ""
- end
-
- code = pre_cond + "\n" + import_str + define_name + " { \"" + title + "\": " + param_str + " }"
-
- nodename = self.respond_to?(:node) ? node : Puppet[:certname]
- facts_val = {
- 'hostname' => nodename.split('.').first,
- 'fqdn' => nodename,
- 'domain' => nodename.split('.', 2).last,
- }
- facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
-
- catalogue = build_catalog(nodename, facts_val, code)
- FileUtils.rm_rf(vardir) if File.directory?(vardir)
- catalogue
+ @catalogue ||= catalogue(:define)
end
end
end
diff --git a/lib/rspec-puppet/example/function_example_group.rb b/lib/rspec-puppet/example/function_example_group.rb
index a3a0800..8373750 100644
--- a/lib/rspec-puppet/example/function_example_group.rb
+++ b/lib/rspec-puppet/example/function_example_group.rb
@@ -1,58 +1,44 @@
-require 'puppetlabs_spec_helper/puppetlabs_spec/puppet_internals'
-
module RSpec::Puppet
module FunctionExampleGroup
include RSpec::Puppet::FunctionMatchers
- PuppetInternals = PuppetlabsSpec::PuppetInternals
+ include RSpec::Puppet::ManifestMatchers
+ include RSpec::Puppet::Support
def subject
function_name = self.class.top_level_description.downcase
- Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
- Puppet[:libdir] = Dir["#{Puppet[:modulepath]}/*/lib"].entries.join(File::PATH_SEPARATOR)
+ vardir = setup_puppet
- nodename = self.respond_to?(:node) ? node : Puppet[:certname]
- facts_val = {
- 'hostname' => nodename.split('.').first,
- 'fqdn' => nodename,
- 'domain' => nodename.split('.').last,
- }
- facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
- facts_val.each { |k, v| Facter.add(k) { setcode { v } } }
+ node_name = nodename(:function)
+
+ facts_val = facts_hash(node_name)
# if we specify a pre_condition, we should ensure that we compile that code
# into a catalog that is accessible from the scope where the function is called
- if self.respond_to? :pre_condition
- if pre_condition.kind_of?(Array)
- Puppet[:code] = pre_condition.join("\n")
- else
- Puppet[:code] = pre_condition
- end
- # we need to get a compiler, b/c we can attach that to a scope
- @compiler = build_compiler(nodename, facts_val)
- else
- @compiler = PuppetInternals.compiler
- end
-
- scope = PuppetInternals.scope(:compiler => @compiler)
+ Puppet[:code] = pre_cond
+
+ compiler = build_compiler(node_name, facts_val)
+
+ function_scope = scope(compiler, node_name)
# Return the method instance for the function. This can be used with
# method.call
- method = PuppetInternals.function_method(function_name, :scope => scope)
- end
-
- def compiler
- @compiler
+ return nil unless Puppet::Parser::Functions.function(function_name)
+ FileUtils.rm_rf(vardir) if File.directory?(vardir)
+ function_scope.method("function_#{function_name}".intern)
end
# get a compiler with an attached compiled catalog
def build_compiler(node_name, fact_values)
node_options = {
- :name => node_name,
- :options => { :parameters => fact_values },
+ :parameters => fact_values,
}
- node = PuppetInternals.node(node_options)
- compiler = PuppetInternals.compiler(:node => node)
+
+ stub_facts! fact_values
+
+ node = build_node(node_name, node_options)
+
+ compiler = Puppet::Parser::Compiler.new(node)
compiler.compile
compiler
end
diff --git a/lib/rspec-puppet/example/host_example_group.rb b/lib/rspec-puppet/example/host_example_group.rb
index ec0d392..9e26434 100644
--- a/lib/rspec-puppet/example/host_example_group.rb
+++ b/lib/rspec-puppet/example/host_example_group.rb
@@ -4,32 +4,7 @@ module RSpec::Puppet
include RSpec::Puppet::Support
def subject
- @catalogue ||= catalogue
- end
-
- def catalogue
- vardir = Dir.mktmpdir
- Puppet[:vardir] = vardir
- Puppet[:hiera_config] = File.join(vardir, "hiera.yaml") if Puppet[:hiera_config] == File.expand_path("/dev/null")
- Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
- Puppet[:manifestdir] = self.respond_to?(:manifest_dir) ? manifest_dir : RSpec.configuration.manifest_dir
- Puppet[:manifest] = self.respond_to?(:manifest) ? manifest : RSpec.configuration.manifest
- Puppet[:templatedir] = self.respond_to?(:template_dir) ? template_dir : RSpec.configuration.template_dir
- Puppet[:config] = self.respond_to?(:config) ? config : RSpec.configuration.config
- code = ""
-
- nodename = self.class.top_level_description.downcase
-
- facts_val = {
- 'hostname' => nodename.split('.').first,
- 'fqdn' => nodename,
- 'domain' => nodename.split('.').last,
- }
- facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
-
- catalogue = build_catalog(nodename, facts_val, code)
- FileUtils.rm_rf(vardir) if File.directory?(vardir)
- catalogue
+ @catalogue ||= catalogue(:host)
end
end
end
diff --git a/lib/rspec-puppet/matchers.rb b/lib/rspec-puppet/matchers.rb
index d92a0f7..2611504 100644
--- a/lib/rspec-puppet/matchers.rb
+++ b/lib/rspec-puppet/matchers.rb
@@ -1,4 +1,6 @@
require 'rspec-puppet/matchers/create_generic'
-require 'rspec-puppet/matchers/create_resource'
require 'rspec-puppet/matchers/include_class'
+require 'rspec-puppet/matchers/compile'
require 'rspec-puppet/matchers/run'
+require 'rspec-puppet/matchers/count_generic'
+require 'rspec-puppet/matchers/dynamic_matchers'
diff --git a/lib/rspec-puppet/matchers/compile.rb b/lib/rspec-puppet/matchers/compile.rb
new file mode 100644
index 0000000..c4d8ef8
--- /dev/null
+++ b/lib/rspec-puppet/matchers/compile.rb
@@ -0,0 +1,133 @@
+module RSpec::Puppet
+ module ManifestMatchers
+ class Compile
+ def initialize
+ @failed_resource = ""
+ @check_deps = false
+ @cycles = []
+ end
+
+ def with_all_deps
+ @check_deps = true
+ self
+ end
+
+ def matches?(catalogue)
+ @catalogue = catalogue
+ if cycles_found?
+ false
+ elsif @check_deps == true && missing_dependencies?
+ false
+ else
+ true
+ end
+ end
+
+ def description
+ "compile the catalogue without cycles"
+ end
+
+ def failure_message_for_should
+ unless @cycles.empty?
+ "dependency cycles found: #{@cycles.join('; ')}"
+ else
+ "expected that the catalogue would include #{@failed_resource}"
+ end
+ end
+
+ def failure_message_for_should_not
+ "expected that the catalogue would not compile but it does"
+ end
+
+ private
+ def missing_dependencies?
+ retval = false
+
+ resource_vertices = @catalogue.vertices.select { |v| v.is_a? Puppet::Resource }
+ resource_vertices.each do |vertex|
+ vertex.each do |param,value|
+ if [:require, :subscribe, :notify, :before].include? param
+ value = Array[value] unless value.is_a? Array
+ value.each do |val|
+ if val.is_a? Puppet::Resource
+ retval = true unless resource_exists?(val, vertex)
+ end
+ end
+ end
+ end
+ end
+
+ retval
+ end
+
+ def resource_hash
+ @resource_hash ||= Proc.new do
+ res_hash = {}
+ @catalogue.vertices.each do |vertex|
+ if vertex.is_a? Puppet::Resource
+ res_hash[vertex.ref] = 1
+ if vertex[:alias]
+ res_hash["#{vertex.type.to_s}[#{vertex[:alias]}]"] = 1
+ end
+ end
+ end
+ res_hash
+ end.call
+ end
+
+ def check_resource(res)
+ if resource_hash[res.ref]
+ true
+ elsif res[:alias] && resource_hash["#{res.type.to_s}[#{res[:alias]}]"]
+ true
+ else
+ false
+ end
+ end
+
+ def resource_exists?(res, vertex)
+ unless check_resource(res)
+ @failed_resource = "#{res.ref} used at #{vertex.file}:#{vertex.line} in #{vertex.ref}"
+ false
+ else
+ true
+ end
+ end
+
+ def cycles_found?
+ retval = false
+ begin
+ cat = @catalogue.to_ral.relationship_graph
+ cat.write_graph(:resources)
+ if cat.respond_to? :find_cycles_in_graph
+ find_cycles(cat)
+ else
+ find_cycles_legacy(cat)
+ end
+ retval = true unless @cycles.empty?
+ rescue Puppet::Error
+ retval = true
+ end
+ retval
+ end
+
+ def find_cycles(catalogue)
+ cycles = catalogue.find_cycles_in_graph
+ if cycles.length > 0
+ cycles.each do |cycle|
+ paths = catalogue.paths_in_cycle(cycle)
+ @cycles << (paths.map{ |path| '(' + path.join(" => ") + ')'}.join("\n") + "\n")
+ end
+ end
+ end
+
+ def find_cycles_legacy(catalogue)
+ begin
+ catalogue.topsort
+ rescue Puppet::Error => e
+ @cycles = [e.message.rpartition(';').first.partition(':').last]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rspec-puppet/matchers/count_generic.rb b/lib/rspec-puppet/matchers/count_generic.rb
new file mode 100644
index 0000000..cd7af4d
--- /dev/null
+++ b/lib/rspec-puppet/matchers/count_generic.rb
@@ -0,0 +1,73 @@
+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)
+ if @type == "resource"
+ @actual_number = catalogue.resources.count do |res|
+ !(['Class', 'Node'].include? res.type)
+ end
+
+ # Puppet automatically adds Stage[main]
+ @actual_number = @actual_number - 1
+ else
+ @actual_number = catalogue.resources.count do |res|
+ res.type == @referenced_type
+ end
+
+ # Puppet automatically adds Class[main] and Class[Settings]
+ @actual_number = @actual_number - 2 if @type == "class"
+ end
+
+ @actual_number == @expected_number
+ 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
diff --git a/lib/rspec-puppet/matchers/create_generic.rb b/lib/rspec-puppet/matchers/create_generic.rb
index 593c1f9..98cd116 100644
--- a/lib/rspec-puppet/matchers/create_generic.rb
+++ b/lib/rspec-puppet/matchers/create_generic.rb
@@ -1,34 +1,76 @@
+require 'rspec-puppet/matchers/parameter_matcher'
module RSpec::Puppet
module ManifestMatchers
class CreateGeneric
+ include RSpec::Puppet::Errors
+
def initialize(*args, &block)
@exp_resource_type = args.shift.to_s.gsub(/^(create|contain)_/, '')
@args = args
@block = block
@referenced_type = referenced_type(@exp_resource_type)
@title = args[0]
+
+ @errors = []
+ @expected_params = []
+ @expected_undef_params = []
+ @notifies = []
+ @subscribes = []
+ @requires = []
+ @befores = []
end
def with(*args, &block)
params = args.shift
- @expected_params = (@expected_params || []) | params.to_a
+ @expected_params = @expected_params | params.to_a
self
end
+ def only_with(*args, &block)
+ params = args.shift
+ @expected_params_count = (@expected_params_count || 0) + params.size
+ self.with(params, &block)
+ end
+
def without(*args, &block)
params = args.shift
- @expected_undef_params = (@expected_undef_params || []) | Array(params)
+ @expected_undef_params = @expected_undef_params | Array(params)
+ self
+ end
+
+ def that_notifies(resource)
+ @notifies << resource
+ self
+ end
+
+ def that_subscribes_to(resource)
+ @subscribes << resource
+ self
+ end
+
+ def that_requires(resource)
+ @requires << resource
+ self
+ end
+
+ def that_comes_before(resource)
+ @befores << resource
self
end
def method_missing(method, *args, &block)
if method.to_s =~ /^with_/
param = method.to_s.gsub(/^with_/, '')
- (@expected_params ||= []) << [param, args[0]]
+ @expected_params << [param, args[0]]
+ self
+ elsif method.to_s =~ /^only_with_/
+ param = method.to_s.gsub(/^only_with_/, '')
+ @expected_params_count = (@expected_params_count || 0) + 1
+ @expected_params << [param, args[0]]
self
elsif method.to_s =~ /^without_/
param = method.to_s.gsub(/^without_/, '')
- (@expected_undef_params ||= []) << param
+ @expected_undef_params << [param, args[0]]
self
else
super
@@ -40,41 +82,25 @@ module RSpec::Puppet
resource = catalogue.resource(@referenced_type, @title)
if resource.nil?
- ret = false
+ false
else
rsrc_hsh = resource.to_hash
- if @expected_params
- @expected_params.each do |name, value|
- if value.kind_of?(Regexp) then
- unless rsrc_hsh[name.to_sym].to_s =~ value
- ret = false
- (@errors ||= []) << "#{name.to_s} matching `#{value.inspect}` but its value of `#{rsrc_hsh[name.to_sym].inspect}` does not"
- end
- elsif value.kind_of?(Array) then
- unless Array(rsrc_hsh[name.to_sym]).flatten.join == value.flatten.join
- ret = false
- (@errors ||= []) << "#{name.to_s} set to `#{value.inspect}` but it is set to `#{rsrc_hsh[name.to_sym].inspect}` in the catalogue"
- end
- else
- unless rsrc_hsh[name.to_sym].to_s == value.to_s
- ret = false
- (@errors ||= []) << "#{name.to_s} set to `#{value.inspect}` but it is set to `#{rsrc_hsh[name.to_sym].inspect}` in the catalogue"
- end
- end
+ if @expected_params_count
+ unless rsrc_hsh.size == @expected_params_count
+ ret = false
+ (@errors ||= []) << "exactly #{@expected_params_count} parameters but the catalogue contains #{rsrc_hsh.size}"
end
end
- if @expected_undef_params
- @expected_undef_params.each do |name|
- unless resource.send(:parameters)[name.to_sym].nil?
- ret = false
- (@errors ||= []) << "#{name.to_s} undefined"
- end
- end
- end
- end
+ check_params(rsrc_hsh, @expected_params, :should) if @expected_params.any?
+ check_params(rsrc_hsh, @expected_undef_params, :not) if @expected_undef_params.any?
+ check_befores(catalogue, resource) if @befores.any?
+ check_requires(catalogue, resource) if @requires.any?
+ check_notifies(catalogue, resource) if @notifies.any?
+ check_subscribes(catalogue, resource) if @subscribes.any?
- ret
+ @errors.empty?
+ end
end
def failure_message_for_should
@@ -87,20 +113,17 @@ module RSpec::Puppet
def description
values = []
- if @expected_params
- @expected_params.each do |name, value|
- if value.kind_of?(Regexp)
- values << "#{name.to_s} matching #{value.inspect}"
- else
- values << "#{name.to_s} => #{value.inspect}"
- end
- end
+
+ if @expected_params_count
+ values << "exactly #{@expected_params_count} parameters"
end
- if @expected_undef_params
- @expected_undef_params.each do |name, value|
- values << "#{name.to_s} undefined"
- end
+ if @expected_params.any?
+ values.concat(generate_param_list(@expected_params, :should))
+ end
+
+ if @expected_undef_params.any?
+ values.concat(generate_param_list(@expected_undef_params, :not))
end
unless values.empty?
@@ -114,20 +137,100 @@ module RSpec::Puppet
"contain #{@referenced_type}[#{@title}]#{value_str}"
end
- private
-
+ private
def referenced_type(type)
type.split('__').map { |r| r.capitalize }.join('::')
end
def errors
- @errors.nil? ? "" : " with #{@errors.join(', and parameter ')}"
+ @errors.empty? ? "" : " 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
+ def generate_param_list(list, type)
+ output = []
+ list.each do |param, value|
+ if value.nil?
+ output << "#{param.to_s} #{type == :not ? 'un' : ''}defined"
+ else
+ a = type == :not ? '!' : '='
+ b = value.is_a?(Regexp) ? '~' : '>'
+ output << "#{param.to_s} #{a}#{b} #{value.inspect}"
+ end
+ end
+ output
+ end
+
+ def check_befores(catalogue, resource)
+ @befores.each do |ref|
+ unless precedes?(resource, catalogue.resource(ref))
+ @errors << BeforeRelationshipError.new(resource.to_ref, ref)
+ end
+ end
+ end
+
+ def check_requires(catalogue, resource)
+ @requires.each do |ref|
+ unless precedes?(catalogue.resource(ref), resource)
+ @errors << RequireRelationshipError.new(resource.to_ref, ref)
+ end
+ end
+ end
+
+ def check_notifies(catalogue, resource)
+ @notifies.each do |ref|
+ unless notifies?(resource, catalogue.resource(ref))
+ @errors << NotifyRelationshipError.new(resource.to_ref, ref)
+ end
+ end
+ end
+
+ def check_subscribes(catalogue, resource)
+ @subscribes.each do |ref|
+ unless notifies?(catalogue.resource(ref), resource)
+ @errors << SubscribeRelationshipError.new(resource.to_ref, ref)
+ end
+ end
+ end
+
+ def relationship_refs(array)
+ Array[array].flatten.map do |resource|
+ resource.respond_to?(:to_ref) ? resource.to_ref : resource
+ end
+ end
+
+ def precedes?(first, second)
+ before_refs = relationship_refs(first[:before])
+ require_refs = relationship_refs(second[:require])
+
+ before_refs.include?(second.to_ref) || require_refs.include?(first.to_ref)
+ end
+
+ def notifies?(first, second)
+ notify_refs = relationship_refs(first[:notify])
+ subscribe_refs = relationship_refs(second[:subscribe])
+
+ notify_refs.include?(second.to_ref) || subscribe_refs.include?(first.to_ref)
+ end
+
+ # @param resource [Hash<Symbol, Object>] The resource in the catalog
+ # @param list [Array<String, Object>] The expected values of the resource
+ # @param type [:should, :not] Whether the given parameters should/not match
+ def check_params(resource, list, type)
+ list.each do |param, value|
+ param = param.to_sym
+
+ if value.nil? then
+ unless resource[param].nil?
+ @errors << "#{param} undefined"
+ end
+ else
+ m = ParameterMatcher.new(param, value, type)
+ unless m.matches?(resource)
+ @errors.concat m.errors
+ end
+ end
+ end
+ end
end
end
end
diff --git a/lib/rspec-puppet/matchers/create_resource.rb b/lib/rspec-puppet/matchers/create_resource.rb
deleted file mode 100644
index 5949fe2..0000000
--- a/lib/rspec-puppet/matchers/create_resource.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-module RSpec::Puppet
- module ManifestMatchers
- extend RSpec::Matchers::DSL
-
- matcher :create_resource do |expected_type, expected_title|
- match do |catalogue|
- ret = true
- resources = catalogue.resources.select { |r|
- r.type == referenced_type(expected_type)
- }.select { |r|
- r.title == expected_title if r.respond_to? :title
- }
-
- unless resources.length == 1
- ret = false
- end
-
- if @expected_params and resources.length != 0
- @expected_params.each do |name, value|
- unless resources.first.send(:parameters)[name.to_sym] == value
- ret = false
- (@errors ||= []) << "the parameter #{name.to_s} set to `#{value}`"
- end
- end
- end
-
- ret
- end
-
- def errors
- @errors.nil? ? "" : " with #{@errors.join(', ')}"
- end
-
- def referenced_type(type)
- type.split('::').map { |r| r.capitalize }.join('::')
- end
-
- chain :with_param do |param_name,param_value|
- (@expected_params ||= []) << [param_name, param_value]
- end
-
- description do
- type = referenced_type(expected_type)
- "create #{type}['#{expected_title}']"
- end
-
- failure_message_for_should do |actual|
- type = referenced_type(expected_type)
- "expected that the catalogue would contain #{type}['#{expected_title}']#{errors}"
- end
- end
- end
-end
diff --git a/lib/rspec-puppet/matchers/dynamic_matchers.rb b/lib/rspec-puppet/matchers/dynamic_matchers.rb
new file mode 100644
index 0000000..446d085
--- /dev/null
+++ b/lib/rspec-puppet/matchers/dynamic_matchers.rb
@@ -0,0 +1,17 @@
+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$/
+ return RSpec::Puppet::ManifestMatchers::Compile.new if method == :compile
+ super
+ end
+ end
+
+ module FunctionMatchers
+ def method_missing(method, *args, &block)
+ return RSpec::Puppet::FunctionMatchers::Run.new if method == :run
+ super
+ end
+ end
+end
diff --git a/lib/rspec-puppet/matchers/include_class.rb b/lib/rspec-puppet/matchers/include_class.rb
index 81c64e6..254d9a1 100644
--- a/lib/rspec-puppet/matchers/include_class.rb
+++ b/lib/rspec-puppet/matchers/include_class.rb
@@ -4,7 +4,8 @@ module RSpec::Puppet
matcher :include_class do |expected_class|
match do |catalogue|
- catalogue.classes.include? expected_class
+ RSpec.deprecate(:include_class, :contain_class)
+ catalogue.classes.include?(expected_class)
end
description do
diff --git a/lib/rspec-puppet/matchers/parameter_matcher.rb b/lib/rspec-puppet/matchers/parameter_matcher.rb
new file mode 100644
index 0000000..9ced6e2
--- /dev/null
+++ b/lib/rspec-puppet/matchers/parameter_matcher.rb
@@ -0,0 +1,117 @@
+module RSpec::Puppet
+ module ManifestMatchers
+ class ParameterMatcher
+ include RSpec::Puppet::Errors
+
+ # @param parameter [Symbol] The specific parameter to check
+ # @param value [Object] The expected data to match the parameter against
+ # @param type [:should, :not] Whether the given parameter should match
+ def initialize(parameter, value, type)
+ @parameter, @value, @type = parameter, value, type
+
+ @should_match = (type == :should)
+
+ @errors = []
+ end
+
+ # Ensure that the actual parameter matches the expected parameter.
+ #
+ # @param resource [Hash<Symbol, Object>] A hash representing a Puppet
+ # resource in the catalog
+ #
+ # @return [true, false]
+ def matches?(resource)
+
+ @resource = resource
+
+ actual = @resource[@parameter]
+ expected = @value
+
+ # Puppet flattens an array with a single value into just the value and
+ # this can cause confusion when testing as people expect when you put
+ # an array in, you'll get an array out.
+ if expected.is_a?(Array) && expected.length == 1
+ actual = Array[actual] unless actual.is_a?(Array)
+ end
+
+ retval = check(expected, actual)
+
+ unless retval
+ @errors << MatchError.new(@parameter, expected, actual, !@should_match)
+ end
+
+ retval
+ end
+
+ # @!attribute [r] errors
+ # @return [Array<Object < StandardError>] All expectation errors
+ # generated on this parameter.
+ attr_reader :errors
+
+ private
+
+ # Recursively check that the `expected` and `actual` data structures match
+ #
+ # @param expected [Object] The expected value of the given resource param
+ # @param actual [Object] The value of the resource as found in the catalogue
+ #
+ # @return [true, false] If the resource matched
+ def check(expected, actual)
+ case expected
+ when Proc
+ check_proc(expected, actual)
+ when Regexp
+ check_regexp(expected, actual)
+ when Hash
+ check_hash(expected, actual)
+ when Array
+ check_array(expected, actual)
+ else
+ check_string(expected, actual)
+ end
+ end
+
+ def check_proc(expected, actual)
+ expected_return = @should_match
+ actual_return = expected.call(actual)
+
+ actual_return == expected_return
+ end
+
+ def check_regexp(expected, actual)
+ !!(actual.to_s.match expected) == @should_match
+ end
+
+ # Ensure that two hashes have the same number of keys, and that for each
+ # key in the expected hash, there's a stringified key in the actual hash
+ # with a matching value.
+ def check_hash(expected, actual)
+ op = @should_match ? :"==" : :"!="
+
+ unless expected.keys.size.send(op, actual.keys.size)
+ return false
+ end
+
+ expected.keys.all? do |key|
+ check(expected[key], actual[key.to_s])
+ end
+ end
+
+ def check_array(expected, actual)
+ op = @should_match ? :"==" : :"!="
+
+ unless expected.size.send(op, actual.size)
+ return false
+ end
+
+ (0...expected.size).all? do |index|
+ check(expected[index], actual[index])
+ end
+ end
+
+ def check_string(expected, actual)
+ (expected.to_s == actual.to_s) == @should_match
+ end
+ end
+ end
+end
diff --git a/lib/rspec-puppet/matchers/run.rb b/lib/rspec-puppet/matchers/run.rb
index d56efea..2ffdc34 100644
--- a/lib/rspec-puppet/matchers/run.rb
+++ b/lib/rspec-puppet/matchers/run.rb
@@ -1,27 +1,35 @@
module RSpec::Puppet
module FunctionMatchers
- extend RSpec::Matchers::DSL
-
- matcher :run do
- match do |func_obj|
+ class Run
+ def matches?(func_obj)
if @params
@func = lambda { func_obj.call(@params) }
else
@func = lambda { func_obj.call }
end
- if @expected_error
+ unless @expected_error.nil?
+ result = false
begin
@func.call
- rescue @expected_error
- #XXX check error string here
- true
- rescue
- false
+ rescue Exception => e
+ @actual_error = e.class
+ if e.is_a?(@expected_error)
+ case @expected_error_message
+ when nil
+ result = true
+ when Regexp
+ result = @expected_error_message =~ e.message
+ else
+ result = @expected_error_message == e.message
+ end
+ end
end
+ result
else
- if @expected_return
- @func.call == @expected_return
+ unless @expected_return.nil?
+ @actual_return = @func.call
+ @actual_return == @expected_return
else
begin
@func.call
@@ -33,43 +41,53 @@ module RSpec::Puppet
end
end
- chain :with_params do |*params|
+ def with_params(*params)
@params = params
+ self
end
- chain :and_return do |value|
+ def and_return(value)
@expected_return = value
+ self
end
- # XXX support error string and regexp
- chain :and_raise_error do |value|
- @expected_error = value
+ def and_raise_error(error_or_message, message=nil)
+ case error_or_message
+ when String, Regexp
+ @expected_error, @expected_error_message = Exception, error_or_message
+ else
+ @expected_error, @expected_error_message = error_or_message, message
+ end
+ self
end
- failure_message_for_should do |func_obj|
- func_name = func_obj.name.to_s.gsub(/^function_/, '')
- func_params = @params.inspect[1..-2]
+ def failure_message_for_should(func_obj)
+ failure_message_generic(:should, func_obj)
+ end
- if @expected_return
- "expected #{func_name}(#{func_params}) to have returned #{@expected_return.inspect} instead of #{@func.call.inspect}"
- elsif @expected_error
- "expected #{func_name}(#{func_params}) to have raised #{@expected_error.inspect}"
- else
- "expected #{func_name}(#{func_params}) to have run successfully"
- end
+ def failure_message_for_should_not(func_obj)
+ failure_message_generic(:should_not, func_obj)
end
- failure_message_for_should_not do |func_obj|
+ private
+ def failure_message_generic(type, func_obj)
func_name = func_obj.name.gsub(/^function_/, '')
func_params = @params.inspect[1..-2]
+ message = "expected #{func_name}(#{func_params}) to "
+ message << "not " if type == :should_not
+
if @expected_return
- "expected #{func_name}(#{func_params}) to not have returned #{@expected_return.inspect}"
+ message << "have returned #{@expected_return.inspect}"
+ if type == :should
+ message << " instead of #{@actual_return.inspect}"
+ end
elsif @expected_error
- "expected #{func_name}(#{func_params}) to not have raised #{@expected_error.inspect}"
+ message << "have raised #{@expected_error.inspect}"
else
- "expected #{func_name}(#{func_params}) to not have run successfully"
+ message << "have run successfully"
end
+ message
end
end
end
diff --git a/lib/rspec-puppet/setup.rb b/lib/rspec-puppet/setup.rb
index 217fc8c..8afbd80 100644
--- a/lib/rspec-puppet/setup.rb
+++ b/lib/rspec-puppet/setup.rb
@@ -43,17 +43,25 @@ module RSpec::Puppet
protected
def self.get_module_name
- p = Puppet::Parser::Lexer.new
module_name = nil
Dir["manifests/*.pp"].entries.each do |manifest|
- p.string = File.read(manifest)
- tokens = p.fullscan
- i = tokens.index { |token| [:CLASS, :DEFINE].include? token.first }
- unless i.nil?
- module_name = tokens[i + 1].last[:value].split('::').first
- break
- end
+ module_name = get_module_name_from_file(manifest)
+ break unless module_name.nil?
+ end
+ module_name
+ end
+
+ def self.get_module_name_from_file(file)
+ p = Puppet::Parser::Lexer.new
+ module_name = nil
+ p.string = File.read(file)
+ tokens = p.fullscan
+
+ i = tokens.index { |token| [:CLASS, :DEFINE].include? token.first }
+ unless i.nil?
+ module_name = tokens[i + 1].last[:value].split('::').first
end
+
module_name
end
@@ -83,6 +91,20 @@ module RSpec::Puppet
end
end
+ def self.safe_create_file(filename, content)
+ if File.exists? filename
+ old_content = File.read(filename)
+ if old_content != content
+ $stderr.puts "!! #{filename} already exists and differs from template"
+ end
+ else
+ File.open(filename, 'w') do |f|
+ f.puts content
+ end
+ puts " + #{filename}"
+ end
+ end
+
def self.safe_create_spec_helper
content = <<-EOF
require 'rspec-puppet'
@@ -94,17 +116,7 @@ RSpec.configure do |c|
c.manifest_dir = File.join(fixture_path, 'manifests')
end
EOF
- if File.exists? 'spec/spec_helper.rb'
- old_content = File.read('spec/spec_helper.rb')
- if old_content != content
- $stderr.puts "!! spec/spec_helper.rb already exists and differs from template"
- end
- else
- File.open('spec/spec_helper.rb', 'w') do |f|
- f.puts content
- end
- puts ' + spec/spec_helper.rb'
- end
+ safe_create_file('spec/spec_helper.rb', content)
end
def self.safe_make_symlink(source, target)
@@ -119,26 +131,37 @@ EOF
end
def self.safe_create_rakefile
- content = <<-EOF
+ content = <<-'EOF'
require 'rake'
-
require 'rspec/core/rake_task'
-RSpec::Core::RakeTask.new(:spec) do |t|
- t.pattern = 'spec/*/*_spec.rb'
+desc "Run all RSpec code examples"
+RSpec::Core::RakeTask.new(:rspec) do |t|
+ t.rspec_opts = File.read("spec/spec.opts").chomp || ""
+end
+
+SPEC_SUITES = (Dir.entries('spec') - ['.', '..','fixtures']).select {|e| File.directory? "spec/#{e}" }
+namespace :rspec do
+ SPEC_SUITES.each do |suite|
+ desc "Run #{suite} RSpec code examples"
+ RSpec::Core::RakeTask.new(suite) do |t|
+ t.pattern = "spec/#{suite}/**/*_spec.rb"
+ t.rspec_opts = File.read("spec/spec.opts").chomp || ""
+ end
+ end
+end
+task :default => :rspec
+
+begin
+ if Gem::Specification::find_by_name('puppet-lint')
+ require 'puppet-lint/tasks/puppet-lint'
+ PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "vendor/**/*.pp"]
+ task :default => [:rspec, :lint]
+ end
+rescue Gem::LoadError
end
EOF
- if File.exists? 'Rakefile'
- old_content = File.read('Rakefile')
- if old_content != content
- $stderr.puts "!! Rakefile already exists and differs from template"
- end
- else
- File.open('Rakefile', 'w') do |f|
- f.puts content
- end
- puts ' + Rakefile'
- end
+ safe_create_file('Rakefile', content)
end
end
end
diff --git a/lib/rspec-puppet/support.rb b/lib/rspec-puppet/support.rb
index 7de1ccd..59ae67d 100644
--- a/lib/rspec-puppet/support.rb
+++ b/lib/rspec-puppet/support.rb
@@ -3,15 +3,132 @@ module RSpec::Puppet
@@cache = {}
- protected
- def build_catalog_without_cache(nodename, facts_val, code)
- if Integer(Puppet.version.split('.').first) >= 3
- Puppet.initialize_settings
+ def catalogue(type)
+ vardir = setup_puppet
+
+ code = [import_str, pre_cond, test_manifest(type)].join("\n")
+ node_name = nodename(type)
+
+ catalogue = build_catalog(node_name, facts_hash(node_name), code)
+ FileUtils.rm_rf(vardir) if File.directory?(vardir)
+ catalogue
+ end
+
+ def import_str
+ klass_name = self.class.top_level_description.downcase
+
+ if File.exists?(File.join(Puppet[:modulepath], 'manifests', 'init.pp'))
+ path_to_manifest = File.join([
+ Puppet[:modulepath],
+ 'manifests',
+ klass_name.split('::')[1..-1]
+ ].flatten)
+ import_str = [
+ "import '#{Puppet[:modulepath]}/manifests/init.pp'",
+ "import '#{path_to_manifest}.pp'",
+ '',
+ ].join("\n")
+ elsif File.exists?(Puppet[:modulepath])
+ import_str = "import '#{Puppet[:manifest]}'\n"
+ else
+ import_str = ""
+ end
+
+ import_str
+ end
+
+ def test_manifest(type)
+ klass_name = self.class.top_level_description.downcase
+
+ if type == :class
+ if !self.respond_to?(:params) || params == {}
+ "include #{klass_name}"
+ else
+ "class { '#{klass_name}': #{param_str} }"
+ end
+ elsif type == :define
+ if self.respond_to? :params
+ "#{klass_name} { '#{title}': #{param_str} }"
+ else
+ "#{klass_name} { '#{title}': }"
+ end
+ elsif type == :host
+ ""
+ end
+ end
+
+ def nodename(type)
+ if [:class, :define, :function].include? type
+ self.respond_to?(:node) ? node : Puppet[:certname]
+ else
+ self.class.top_level_description.downcase
+ end
+ end
+
+
+ def pre_cond
+ if self.respond_to?(:pre_condition) && !pre_condition.nil?
+ if pre_condition.is_a? Array
+ pre_condition.join("\n")
+ else
+ pre_condition
+ end
+ else
+ ''
+ end
+ end
+
+ def facts_hash(node)
+ facts_val = {
+ 'hostname' => node.split('.').first,
+ 'fqdn' => node,
+ 'domain' => node.split('.', 2).last,
+ }
+
+ if RSpec.configuration.default_facts.any?
+ facts_val.merge!(munge_facts(RSpec.configuration.default_facts))
+ end
+
+ facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
+ facts_val
+ end
+
+ def param_str
+ params.keys.map do |r|
+ param_val = escape_special_chars(params[r].inspect)
+ "#{r.to_s} => #{param_val}"
+ end.join(', ')
+ end
+
+ def setup_puppet
+ vardir = Dir.mktmpdir
+ Puppet[:vardir] = vardir
+
+ [
+ [:modulepath, :module_path],
+ [:manifestdir, :manifest_dir],
+ [:manifest, :manifest],
+ [:templatedir, :template_dir],
+ [:config, :config],
+ [:confdir, :confdir],
+ [:hiera_config, :hiera_config],
+ ].each do |a, b|
+ value = self.respond_to?(b) ? self.send(b) : RSpec.configuration.send(b)
+ begin
+ Puppet[a] = value
+ rescue ArgumentError
+ Puppet.settings.setdefaults(:main, {a => {:default => value, :desc => a.to_s}})
+ end
end
+ Puppet[:libdir] = Dir["#{Puppet[:modulepath]}/*/lib"].entries.join(File::PATH_SEPARATOR)
+ vardir
+ end
+
+ def build_catalog_without_cache(nodename, facts_val, code)
Puppet[:code] = code
- facts_val.each { |k, v| Facter.add(k) { setcode { v } } }
+ stub_facts! facts_val
node_obj = Puppet::Node.new(nodename)
@@ -25,8 +142,11 @@ module RSpec::Puppet
end
end
- public
- def build_catalog *args
+ def stub_facts!(facts)
+ facts.each { |k, v| Facter.add(k) { setcode { v } } }
+ end
+
+ def build_catalog(*args)
@@cache[args] ||= self.build_catalog_without_cache(*args)
end
@@ -35,5 +155,32 @@ module RSpec::Puppet
facts.keys.each { |key| output[key.to_s] = facts[key] }
output
end
+
+ def escape_special_chars(string)
+ string.gsub!(/\$/, "\\$")
+ string
+ end
+
+ def scope(compiler, node_name)
+ if Puppet.version =~ /^2\.[67]/
+ # loadall should only be necessary prior to 3.x
+ # Please note, loadall needs to happen first when creating a scope, otherwise
+ # you might receive undefined method `function_*' errors
+ Puppet::Parser::Functions.autoloader.loadall
+ scope = Puppet::Parser::Scope.new(:compiler => compiler)
+ else
+ scope = Puppet::Parser::Scope.new(compiler)
+ end
+
+ scope.source = Puppet::Resource::Type.new(:node, node_name)
+ scope.parent = compiler.topscope
+ scope
+ end
+
+ def build_node(name, opts = {})
+ node_environment = Puppet::Node::Environment.new('test')
+ opts.merge!({:environment => node_environment})
+ Puppet::Node.new(name, opts)
+ end
end
end
diff --git a/metadata.yml b/metadata.yml
index 2616aa7..f436c1d 100644
--- a/metadata.yml
+++ b/metadata.yml
@@ -1,30 +1,27 @@
--- !ruby/object:Gem::Specification
name: rspec-puppet
version: !ruby/object:Gem::Version
- version: 0.1.6
- prerelease:
+ version: 1.0.1
platform: ruby
authors:
- Tim Sharpe
autorequire:
bindir: bin
cert_chain: []
-date: 2013-01-24 00:00:00.000000000 Z
+date: 2013-12-06 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: rspec
requirement: !ruby/object:Gem::Requirement
- none: false
requirements:
- - - ! '>='
+ - - '>='
- !ruby/object:Gem::Version
version: '0'
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
- none: false
requirements:
- - - ! '>='
+ - - '>='
- !ruby/object:Gem::Version
version: '0'
description: RSpec tests for your Puppet manifests
@@ -34,58 +31,47 @@ executables:
extensions: []
extra_rdoc_files: []
files:
-- bin/rspec-puppet-init
+- README.md
+- lib/rspec-puppet/errors.rb
- lib/rspec-puppet/example/class_example_group.rb
- lib/rspec-puppet/example/define_example_group.rb
- lib/rspec-puppet/example/function_example_group.rb
- lib/rspec-puppet/example/host_example_group.rb
- lib/rspec-puppet/example.rb
+- lib/rspec-puppet/matchers/compile.rb
+- lib/rspec-puppet/matchers/count_generic.rb
- lib/rspec-puppet/matchers/create_generic.rb
-- lib/rspec-puppet/matchers/create_resource.rb
+- lib/rspec-puppet/matchers/dynamic_matchers.rb
- lib/rspec-puppet/matchers/include_class.rb
+- lib/rspec-puppet/matchers/parameter_matcher.rb
- lib/rspec-puppet/matchers/run.rb
- lib/rspec-puppet/matchers.rb
- lib/rspec-puppet/setup.rb
- lib/rspec-puppet/support.rb
- lib/rspec-puppet.rb
-- LICENSE
-- Rakefile
-- README.md
-- rspec-puppet.gemspec
-- spec/classes/boolean_regexp_spec.rb
-- spec/classes/boolean_spec.rb
-- spec/classes/sysctl_common_spec.rb
-- spec/defines/sysctl_before_spec.rb
-- spec/defines/sysctl_spec.rb
-- spec/hosts/foo_spec.rb
-- spec/hosts/testhost_spec.rb
-- spec/fixtures/manifests/site.pp
-- spec/fixtures/modules/boolean/manifests/init.pp
-- spec/fixtures/modules/sysctl/manifests/init.pp
-- spec/functions/split_spec.rb
-- spec/spec_helper.rb
+- bin/rspec-puppet-init
homepage: https://github.com/rodjek/rspec-puppet/
-licenses: []
+licenses:
+- MIT
+metadata: {}
post_install_message:
rdoc_options: []
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
- none: false
requirements:
- - - ! '>='
+ - - '>='
- !ruby/object:Gem::Version
version: '0'
required_rubygems_version: !ruby/object:Gem::Requirement
- none: false
requirements:
- - - ! '>='
+ - - '>='
- !ruby/object:Gem::Version
version: '0'
requirements: []
rubyforge_project:
-rubygems_version: 1.8.23
+rubygems_version: 2.0.3
signing_key:
-specification_version: 3
+specification_version: 4
summary: RSpec tests for your Puppet manifests
test_files: []
diff --git a/rspec-puppet.gemspec b/rspec-puppet.gemspec
deleted file mode 100644
index aae0b78..0000000
--- a/rspec-puppet.gemspec
+++ /dev/null
@@ -1,47 +0,0 @@
-Gem::Specification.new do |s|
- s.name = 'rspec-puppet'
- s.version = '0.1.6'
- s.homepage = 'https://github.com/rodjek/rspec-puppet/'
- s.summary = 'RSpec tests for your Puppet manifests'
- s.description = 'RSpec tests for your Puppet manifests'
-
- s.executables = ['rspec-puppet-init']
-
- s.files = [
- 'bin/rspec-puppet-init',
- 'lib/rspec-puppet/example/class_example_group.rb',
- 'lib/rspec-puppet/example/define_example_group.rb',
- 'lib/rspec-puppet/example/function_example_group.rb',
- 'lib/rspec-puppet/example/host_example_group.rb',
- 'lib/rspec-puppet/example.rb',
- 'lib/rspec-puppet/matchers/create_generic.rb',
- 'lib/rspec-puppet/matchers/create_resource.rb',
- 'lib/rspec-puppet/matchers/include_class.rb',
- 'lib/rspec-puppet/matchers/run.rb',
- 'lib/rspec-puppet/matchers.rb',
- 'lib/rspec-puppet/setup.rb',
- 'lib/rspec-puppet/support.rb',
- 'lib/rspec-puppet.rb',
- 'LICENSE',
- 'Rakefile',
- 'README.md',
- 'rspec-puppet.gemspec',
- 'spec/classes/boolean_regexp_spec.rb',
- 'spec/classes/boolean_spec.rb',
- 'spec/classes/sysctl_common_spec.rb',
- 'spec/defines/sysctl_before_spec.rb',
- 'spec/defines/sysctl_spec.rb',
- 'spec/hosts/foo_spec.rb',
- 'spec/hosts/testhost_spec.rb',
- 'spec/fixtures/manifests/site.pp',
- 'spec/fixtures/modules/boolean/manifests/init.pp',
- 'spec/fixtures/modules/sysctl/manifests/init.pp',
- 'spec/functions/split_spec.rb',
- 'spec/spec_helper.rb',
- ]
-
- s.add_dependency 'rspec'
-
- s.authors = ['Tim Sharpe']
- s.email = 'tim at sharpe.id.au'
-end
diff --git a/spec/classes/boolean_regexp_spec.rb b/spec/classes/boolean_regexp_spec.rb
deleted file mode 100644
index b4120a9..0000000
--- a/spec/classes/boolean_regexp_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'spec_helper'
-
-describe 'boolean' do
- let(:title) { 'bool.testing' }
- let(:params) { { :bool => false } }
- let(:message_re) { /bool is false/ }
-
- it { should create_notify("bool testing").with_message(message_re) }
- it { should_not create_notify("bool testing").with_message(/true/) }
-end
diff --git a/spec/classes/boolean_spec.rb b/spec/classes/boolean_spec.rb
deleted file mode 100644
index 62ce239..0000000
--- a/spec/classes/boolean_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require 'spec_helper'
-
-if Puppet::PUPPETVERSION !~ /0\.2/
- describe 'boolean' do
- let(:title) { 'bool.testing' }
- let(:params) { { :bool => false } }
-
- it { should create_notify("bool testing")\
- .with_message("This will print when \$bool is false.") }
- end
-end
diff --git a/spec/classes/sysctl_common_spec.rb b/spec/classes/sysctl_common_spec.rb
deleted file mode 100644
index 88bfaf8..0000000
--- a/spec/classes/sysctl_common_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require 'spec_helper'
-
-describe 'sysctl::common' do
- it { should contain_exec('sysctl/reload') \
- .with_command('/sbin/sysctl -p /etc/sysctl.conf').with_returns([0, 2]) }
- it { should_not create_augeas('foo') }
- describe 'when using with to specify a hash of parameters' do
- it 'should fail if the parameter is not contained in the resource' do
- expect do
- subject.should contain_exec('sysctl/reload').with('foo' => 'bar')
- end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
- end
- it 'should pass if the parameters are contained in the resource' do
- subject.should contain_exec('sysctl/reload').with(
- 'refreshonly' => 'true',
- 'returns' => [0, 2]
- )
- end
- end
- describe 'when using without to specify parameter name(s)' do
- it 'should pass if the parameter name is not contained in the resource' do
- subject.should contain_exec('sysctl/reload').without('foo')
- end
- it 'should pass if the parameter names are not contained in the resource' do
- subject.should contain_exec('sysctl/reload').without(['foo', 'bar'])
- end
- it 'should fail if any of the parameter names are contained in the resource' do
- expect do
- subject.should contain_exec('sysctl/reload').without(['foo', 'returns'])
- end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
- end
- end
-end
-
-describe 'sysctl::common' do
- let(:params) { { :test_param => "yes" } }
-
- it { should create_class("sysctl::common")\
- .with_test_param("yes") }
-end
diff --git a/spec/defines/sysctl_before_spec.rb b/spec/defines/sysctl_before_spec.rb
deleted file mode 100644
index 9e9d953..0000000
--- a/spec/defines/sysctl_before_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require 'spec_helper'
-
-describe 'sysctl::before' do
- let(:title) { 'sysctl::before' }
- let(:params) { { :value => "title" } }
-
- it "Should raise an error about needing the sysctl::common class" do
- expect { should create_notify("message-title")\
- .with_message("This should print if the class is here first.") }\
- .to raise_error(Puppet::Error, /Could not find resource 'Class\[Sysctl::Common\]/)
- end
-end
-
-describe 'sysctl::before' do
- let(:title) { 'test define' }
- let(:pre_condition) {
- [ '# we need sysctl common',
- 'class {"sysctl::common":}' ] }
- let(:params) { { :value => "title" } }
-
- it { should create_resource("sysctl::before", 'test define')\
- .with_param(:value, "title") }
-
- it { should include_class("sysctl::common") }
-
-end
diff --git a/spec/defines/sysctl_spec.rb b/spec/defines/sysctl_spec.rb
deleted file mode 100644
index 47a6a4c..0000000
--- a/spec/defines/sysctl_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require 'spec_helper'
-
-describe 'sysctl' do
- let(:title) { 'vm.swappiness' }
- let(:params) { {:value => '60'} }
-
- it { should include_class('sysctl::common') }
- it { should create_augeas('sysctl/vm.swappiness') \
- .with_context('/files/etc/sysctl.conf') \
- .with_changes("set vm.swappiness '60'") \
- .with_onlyif("match vm.swappiness[.='60'] size == 0") \
- .with_notify('Exec[sysctl/reload]')\
- .without_foo }
-end
diff --git a/spec/fixtures/manifests/site.pp b/spec/fixtures/manifests/site.pp
deleted file mode 100644
index 0f0ec89..0000000
--- a/spec/fixtures/manifests/site.pp
+++ /dev/null
@@ -1,7 +0,0 @@
-node default {
- notify { 'test': }
-}
-
-node /testhost/ {
- include sysctl::common
-}
diff --git a/spec/fixtures/modules/boolean/manifests/init.pp b/spec/fixtures/modules/boolean/manifests/init.pp
deleted file mode 100644
index 94bb2c7..0000000
--- a/spec/fixtures/modules/boolean/manifests/init.pp
+++ /dev/null
@@ -1,12 +0,0 @@
-class boolean($bool) {
- $real_bool = $bool ? {
- true => false,
- false => true,
- }
-
- if ($real_bool) {
- notify {"bool testing":
- message => "This will print when \$bool is false."
- }
- }
-}
diff --git a/spec/fixtures/modules/sysctl/manifests/init.pp b/spec/fixtures/modules/sysctl/manifests/init.pp
deleted file mode 100644
index 3f98404..0000000
--- a/spec/fixtures/modules/sysctl/manifests/init.pp
+++ /dev/null
@@ -1,39 +0,0 @@
-class sysctl::common ($test_param = 'yes') {
- exec { 'sysctl/reload':
- command => '/sbin/sysctl -p /etc/sysctl.conf',
- refreshonly => true,
- returns => [0, 2],
- }
-}
-
-define sysctl($value) {
- include sysctl::common
-
- augeas { "sysctl/${name}":
- context => '/files/etc/sysctl.conf',
- changes => "set ${name} '${value}'",
- onlyif => "match ${name}[.='${value}'] size == 0",
- notify => Exec['sysctl/reload'],
- }
-}
-
-class boolean($bool) {
- $real_bool = $bool ? {
- true => false,
- false => true,
- }
-
- if ($real_bool) {
- notify {"bool testing":
- message => "This will print when \$bool is false."
- }
- }
-}
-
-define sysctl::before($value) {
- Class['sysctl::common'] -> Sysctl::Before[$name]
-
- notify {"message-${name}":
- message => "This should print if the class is here first."
- }
-}
diff --git a/spec/functions/split_spec.rb b/spec/functions/split_spec.rb
deleted file mode 100644
index 17a3db9..0000000
--- a/spec/functions/split_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'spec_helper'
-
-describe 'split' do
- it { should run.with_params('aoeu', 'o').and_return(['a', 'eu']) }
- it { should run.with_params('foo').and_raise_error(Puppet::ParseError) }
- it { should_not run.with_params('foo').and_raise_error(Puppet::DevError) }
-
- it 'something' do
- if Integer(Puppet.version.split('.').first) >= 3
- expected_error = ArgumentError
- else
- expected_error = Puppet::ParseError
- end
-
- expect { subject.call('foo') }.to raise_error(expected_error)
- end
-end
diff --git a/spec/hosts/foo_spec.rb b/spec/hosts/foo_spec.rb
deleted file mode 100644
index 9c1496d..0000000
--- a/spec/hosts/foo_spec.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require 'spec_helper'
-
-describe 'foo.example.com' do
- it { should_not include_class('sysctl::common') }
- it { should contain_notify('test') }
-end
diff --git a/spec/hosts/testhost_spec.rb b/spec/hosts/testhost_spec.rb
deleted file mode 100644
index 42c2ca2..0000000
--- a/spec/hosts/testhost_spec.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'spec_helper'
-
-describe 'testhost' do
- it { should include_class('sysctl::common') }
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
deleted file mode 100644
index 66145c2..0000000
--- a/spec/spec_helper.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require 'rspec-puppet'
-
-RSpec.configure do |c|
- c.module_path = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'modules')
- c.manifest_dir = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'manifests')
-end
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-rspec-puppet.git
More information about the Pkg-ruby-extras-commits
mailing list