pax_global_header00006660000000000000000000000064125316361260014517gustar00rootroot0000000000000052 comment=3094e2dd607ee7a78a780301bd395a4b4d7808c9 rspec-puppet-2.2.0/000077500000000000000000000000001253163612600141475ustar00rootroot00000000000000rspec-puppet-2.2.0/.gitignore000066400000000000000000000000651253163612600161400ustar00rootroot00000000000000*.gem /Gemfile.lock /.bundle/ /vendor/ .ruby-version rspec-puppet-2.2.0/.travis.yml000066400000000000000000000012641253163612600162630ustar00rootroot00000000000000language: ruby cache: bundler rvm: - 1.8.7 - 1.9.3 - 2.0.0 - 2.1 sudo: false env: - PUPPET_VERSION=2.7 - PUPPET_VERSION=3.2 - PUPPET_VERSION=3.4 - PUPPET_VERSION=3.6 - PUPPET_VERSION=3.7 - PUPPET_VERSION=3.8 - PUPPET_VERSION=4.0 - PUPPET_VERSION=4.1 - RSPEC_VERSION=2.14 matrix: exclude: - rvm: 1.8.7 env: RSPEC_VERSION=2.14 - rvm: 1.8.7 env: PUPPET_VERSION=4.0 - rvm: 1.8.7 env: PUPPET_VERSION=4.1 - rvm: 1.9.3 env: PUPPET_VERSION=2.7 - rvm: 2.0.0 env: PUPPET_VERSION=2.7 - rvm: 2.1 env: PUPPET_VERSION=2.7 - rvm: 2.1 env: PUPPET_VERSION=3.2 notifications: email: - tim@github.com rspec-puppet-2.2.0/CHANGELOG.md000066400000000000000000000032171253163612600157630ustar00rootroot00000000000000# Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## [2.2.0] ### Added - Settings for ordering, strict_variables, stringify_facts, and trusted_node_data - Expose the scope in function example groups ### Fixed - rspec-puppet-init now works with Puppet 4 - Several fixes and enhancements for the `run` matcher - Recompile the catalog when the hiera config changes ## [2.1.0] - 2015-04-21 ### Added - Puppet 4 support - Ability to set `environment` in a let block - Better function failure messages ### Fixed - Filter fixtures from coverage reports - Fix functions accidentally modifying rspec function arguments - Restructured TravisCI matrix (NB: Puppet 2.6 is no longer tested) ## [2.0.1] - 2015-03-12 ### Fixed - Allow RSpec 2 to still be used ## [2.0.0] - 2014-12-02 ### Changed - `subject` is now a lambda to enable catching compilation failures. ### Added - Ability to use RSpec 3 - Hiera integration - Coverage reports - Ability to test on the future parser - Function tests now have a catalogue - Add array of references support to Relationship matchers `that_requires`, `that_comes_before`, `that_notifies`, and `that_subscribes_to` ### Fixed - Better error messaging and handling for parameters (`nil` and friends) and dependency cycles ## 1.0.1 and earlier For changelog of versions 1.0.1 and earlier, see http://rspec-puppet.com/changelog/ [2.1.0]: https://github.com/rodjek/rspec-puppet/compare/v2.0.1...v2.1.0 [2.0.1]: https://github.com/rodjek/rspec-puppet/compare/v2.0.0...v2.0.1 [2.0.0]: https://github.com/rodjek/rspec-puppet/compare/v1.0.1...v2.0.0 rspec-puppet-2.2.0/Gemfile000066400000000000000000000005301253163612600154400ustar00rootroot00000000000000source 'https://rubygems.org' puppetversion = ENV.key?('PUPPET_VERSION') ? "~> #{ENV['PUPPET_VERSION']}.0" : ['~> 4.0'] rspecversion = ENV.key?('RSPEC_VERSION') ? "= #{ENV['RSPEC_VERSION']}" : ['~> 2.0'] gemspec gem 'rake' gem 'rspec', rspecversion gem 'rspec-core', rspecversion gem 'puppet', puppetversion gem 'pry', :group => :developmentrspec-puppet-2.2.0/LICENSE000066400000000000000000000020361253163612600151550ustar00rootroot00000000000000Copyright (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. rspec-puppet-2.2.0/README.md000066400000000000000000000256031253163612600154340ustar00rootroot00000000000000# RSpec tests for your Puppet manifests & modules [![Build Status](https://travis-ci.org/rodjek/rspec-puppet.png)](https://travis-ci.org/rodjek/rspec-puppet) ## Installation gem install rspec-puppet ## Naming conventions For clarity and consistency, I recommend that you use the following directory structure and naming convention. module | +-- manifests | +-- lib | +-- spec | +-- spec_helper.rb | +-- classes | | | +-- _spec.rb | +-- defines | | | +-- _spec.rb | +-- functions | | | +-- _spec.rb | +-- hosts | +-- _spec.rb ## Example groups If you use the above directory structure, your examples will automatically be placed in the correct groups and have access to the custom matchers. *If you choose not to*, you can force the examples into the required groups as follows. ```ruby describe 'myclass', :type => :class do ... end describe 'mydefine', :type => :define do ... end describe 'myfunction', :type => :puppet_function do ... end describe 'myhost.example.com', :type => :host do ... end ``` ## Defined Types & Classes ### Matchers #### Checking if a resource exists You can test if a resource exists in the catalogue with the generic `contain_` matcher. ```ruby 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). ```ruby it { should contain_foo__bar('baz') } ``` You can further test the parameters that have been passed to the resources with the generic `with_` chains. ```ruby 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_` 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 it do should contain_service('keystone').with( 'ensure' => 'running', 'enable' => 'true', 'hasstatus' => 'true', 'hasrestart' => 'true' ) 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_` chains. ```ruby it { should contain_file('/foo/bar').without_mode } ``` You can use the without method to verify that a list of parameters have not been defined ```ruby it { should contain_service('keystone').without( ['restart', 'status'] )} ``` #### 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_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). #### Relationship matchers The following methods will allow you to test the relationships between the resources in your catalogue, regardless of how the relationship is defined. This means that it doesn’t matter if you prefer to define your relationships with the metaparameters (**require**, **before**, **notify** and **subscribe**) or the chaining arrows (**->**, **~>**, **<-** and **<~**), they’re all tested the same. ```ruby it { should contain_file('foo').that_requires('File[bar]') } it { should contain_file('foo').that_comes_before('File[bar]') } it { should contain_file('foo').that_notifies('File[bar]') } it { should contain_file('foo').that_subscribes_to('File[bar]') } ``` An array can be used to test a resource for multiple relationships ```ruby it { should contain_file('foo').that_requires(['File[bar]', 'File[baz]']) } it { should contain_file('foo').that_comes_before(['File[bar]','File[baz]']) } it { should contain_file('foo').that_notifies(['File[bar]', 'File[baz]']) } it { should contain_file('foo').that_subscribes_to(['File[bar]', 'File[baz]']) } ``` You can also test the reverse direction of the relationship, so if you have the following bit of Puppet code ```ruby notify { 'foo': } notify { 'bar': before => Notify['foo'], } ``` You can test that **Notify[bar]** comes before **Notify[foo]** ```ruby it { should contain_notify('bar').that_comes_before('Notify[foo]') } ``` Or, you can test that **Notify[foo]** requires **Notify[bar]** ```ruby it { should contain_notify('foo').that_requires('Notify[bar]') } ``` ### Writing tests #### Basic test structure To test that sysctl { 'baz' value => 'foo', } Will cause the following resource to be in included in catalogue for a host exec { 'sysctl/reload': command => '/sbin/sysctl -p /etc/sysctl.conf', } We can write the following testcase (in `spec/defines/sysctl_spec.rb`) ```ruby describe 'sysctl' do let(:title) { 'baz' } let(:params) { { :value => 'foo' } } it { should contain_exec('sysctl/reload').with_command("/sbin/sysctl -p /etc/sysctl.conf") } end ``` #### Specifying the title of a resource ```ruby let(:title) { 'foo' } ``` #### Specifying the parameters to pass to a resources or parametised class ```ruby let(:params) { {:ensure => 'present', ...} } ``` #### Specifying the FQDN of the test node If the manifest you're testing expects to run on host with a particular name, you can specify this as follows ```ruby let(:node) { 'testhost.example.com' } ``` #### Specifying the environment name If the manifest you're testing expects to evaluate the environment name, you can specify this as follows ```ruby let(:environment) { 'production' } ``` #### Specifying the facts that should be available to your manifest By default, the test environment contains no facts for your manifest to use. You can set them with a hash ```ruby 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 `spec_helper.rb` ```ruby RSpec.configure do |c| c.module_path = '/path/to/your/module/dir' end ``` However, if you want to specify it in each example, you can do so ```ruby let(:module_path) { '/path/to/your/module/dir' } ``` ## Functions ### Matchers All of the standard RSpec matchers are available for you to use when testing Puppet functions. ```ruby it 'should be able to do something' do subject.call(['foo']) == 'bar' end ``` For your convenience though, a `run` matcher exists to provide easier to understand test cases. ```ruby it { should run.with_params('foo').and_return('bar') } ``` ### Writing tests #### Basic test structure ```ruby require 'spec_helper' describe '' do ... end ``` #### Specifying the name of the function to test The name of the function must be provided in the top level description, e.g. ```ruby describe 'split' do ``` #### Specifying the arguments to pass to the function You can specify the arguments to pass to your function during the test(s) using either the `with_params` chain method in the `run` matcher ```ruby it { should run.with_params('foo', 'bar', ['baz']) } ``` Or by using the `call` method on the subject directly ```ruby it 'something' do subject.call(['foo', 'bar', ['baz']]) end ``` #### Testing the results of the function You can test the result of a function (if it produces one) using either the `and_returns` chain method in the `run` matcher ```ruby it { should run.with_params('foo').and_return('bar') } ``` 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 end ``` #### Testing the errors thrown by the function You can test whether the function throws an exception using either the `and_raises_error` chain method in the `run` matcher ```ruby it { should run.with_params('a', 'b').and_raise_error(Puppet::ParseError) } it { should_not run.with_params('a').and_raise_error(Puppet::ParseError) } ``` 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) end ``` ## Hiera integration ### Configuration Set the hiera config symbol properly in your spec files: ```ruby let(:hiera_config) { 'spec/fixtures/hiera/hiera.yaml' } hiera = Hiera.new(:config => 'spec/fixtures/hiera/hiera.yaml') ``` Create your spec hiera files spec/fixtures/hiera/hiera.yaml ```ruby --- :backends: - yaml :hierarchy: - test :yaml: :datadir: 'spec/fixtures/hiera' ``` spec/fixtures/hiera/test.yaml ```ruby --- ntpserver: ['ntp1.domain.com','ntpXX.domain.com'] user: oneuser: shell: '/bin/bash' twouser: shell: '/sbin/nologin' ``` ### Use hiera in your tests ```ruby ntpserver = hiera.lookup('ntpserver', nil, nil) let(:params) { :ntpserver => ntpserver } ``` ### Enabling hiera lookups If you just want to fetch values from hiera (e.g. because you're testing code that uses explicit hiera lookups) just specify the path to the hiera config in your `spec_helper.rb` ```ruby RSpec.configure do |c| c.hiera_config = 'spec/fixtures/hiera/hiera.yaml' end ``` ## Producing coverage reports You can output a basic resource coverage report with the following in you spec file. ```ruby at_exit { RSpec::Puppet::Coverage.report! } ``` This checks which Puppet resources have been explicitly checked as part of the current test run and outputs both a coverage percentage and a list of untouched resources. rspec-puppet-2.2.0/Rakefile000066400000000000000000000002301253163612600156070ustar00rootroot00000000000000require 'rake' require 'rspec/core/rake_task' require 'bundler/gem_tasks' task :default => :test task :spec => :test RSpec::Core::RakeTask.new(:test) rspec-puppet-2.2.0/bin/000077500000000000000000000000001253163612600147175ustar00rootroot00000000000000rspec-puppet-2.2.0/bin/rspec-puppet-init000077500000000000000000000006531253163612600202410ustar00rootroot00000000000000#!/usr/bin/env ruby $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib]) require 'rspec-puppet' require 'optparse' options = { :module_name => nil, } OptionParser.new do |opts| opts.banner = "Usage: rspec-puppet-init [options]" opts.on('-n', '--name NAME', 'The name of the module (override autodetection)') do |v| options[:module_name] = v end end.parse! RSpec::Puppet::Setup.run(options[:module_name]) rspec-puppet-2.2.0/lib/000077500000000000000000000000001253163612600147155ustar00rootroot00000000000000rspec-puppet-2.2.0/lib/rspec-puppet.rb000066400000000000000000000032311253163612600176700ustar00rootroot00000000000000require 'puppet' require 'rspec' require 'fileutils' require 'tmpdir' require 'rspec-puppet/errors' require 'rspec-puppet/matchers' require 'rspec-puppet/example' require 'rspec-puppet/setup' require 'rspec-puppet/coverage' begin require 'puppet/test/test_helper' rescue LoadError end RSpec.configure do |c| c.add_setting :environmentpath, :default => '/etc/puppetlabs/code/environments' 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' c.add_setting :parser, :default => 'current' c.add_setting :trusted_node_data, :default => false c.add_setting :ordering, :default => 'title-hash' c.add_setting :stringify_facts, :default => true c.add_setting :strict_variables, :default => false 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 rspec-puppet-2.2.0/lib/rspec-puppet/000077500000000000000000000000001253163612600173445ustar00rootroot00000000000000rspec-puppet-2.2.0/lib/rspec-puppet/coverage.rb000066400000000000000000000073021253163612600214660ustar00rootroot00000000000000module RSpec::Puppet class Coverage attr_accessor :filters class << self extend Forwardable def_delegators(:instance, :add, :cover!, :report!, :filters, :add_filter, :add_from_catalog) end include Singleton def initialize @collection = {} @filters = ['Stage[main]', 'Class[Settings]', 'Class[main]'] end def add(resource) if !exists?(resource) && !filtered?(resource) @collection[resource.to_s] = ResourceWrapper.new(resource) end end def add_filter(type, title) @filters << "#{type.capitalize}[#{title.capitalize}]" end # add all resources from catalog declared in module test_module def add_from_catalog(catalog, test_module) catalog.to_a.each do |resource| # check filters next if @filters.include?(resource.to_s) if resource.type == 'Class' # if the resource is a class, make sure the class belongs to # module test_module module_name = resource.title.split('::').first.downcase next if module_name != test_module elsif resource.file # otherwise, the source file should be available, so make # sure the manifest declaring the resource is in # test_module's directory tree or the site manifest(s) if Puppet.version.to_f >= 4.0 paths = [ (Pathname.new(Puppet[:environmentpath]) + 'fixtures' + test_module + 'manifests').to_s, (Pathname.new(Puppet[:environmentpath]) + 'fixtures' + 'manifests' + 'site.pp').to_s ] else paths = Puppet[:modulepath].split(File::PATH_SEPARATOR).map do |dir| (Pathname.new(dir) + test_module + 'manifests').to_s end paths << Puppet[:manifest] end next unless paths.any? { |path| resource.file.include?(path) } end add(resource) end end def filtered?(resource) filters.include?(resource.to_s) end def cover!(resource) if !filtered?(resource) && (wrapper = find(resource)) wrapper.touch! end end def report! report = {} report[:total] = @collection.size report[:touched] = @collection.count { |_, resource| resource.touched? } report[:untouched] = report[:total] - report[:touched] report[:coverage] = sprintf("%5.2f", ((report[:touched].to_f/report[:total].to_f)*100)) report[:detailed] = Hash[*@collection.map do |name, wrapper| [name, wrapper.to_hash] end.flatten] puts <<-EOH.gsub(/^ {8}/, '') Total resources: #{report[:total]} Touched resources: #{report[:touched]} Resource coverage: #{report[:coverage]}% EOH if report[:coverage] != "100.00" puts <<-EOH.gsub(/^ {10}/, '') Untouched resources: #{ untouched_resources = report[:detailed].reject do |_,rsrc| rsrc["touched"] end untouched_resources.inject([]) do |memo, (name,_)| memo << " #{name}" end.sort.join("\n") } EOH end end private def find(resource) @collection[resource.to_s] end def exists?(resource) !find(resource).nil? end class ResourceWrapper attr_reader :resource def initialize(resource = nil) @resource = resource end def to_s @resource.to_s end def to_hash { 'touched' => touched?, } end def touch! @touched = true end def touched? !!@touched end end end end rspec-puppet-2.2.0/lib/rspec-puppet/errors.rb000066400000000000000000000036521253163612600212130ustar00rootroot00000000000000module RSpec::Puppet module Errors class MatchError < StandardError attr_reader :param, :expected, :actual, :negative def initialize(param, expected, actual, negative) @param = param @expected = expected @actual = actual @negative = negative end def message if negative == true "#{param} not set to #{expected.inspect} but it is set to #{actual.inspect}" else "#{param} set to #{expected.inspect} but it is set to #{actual.inspect}" end end def to_s message end end class RegexpMatchError < MatchError def message if negative == true "#{param} not matching #{expected.inspect} but its value of #{actual.inspect} does" else "#{param} matching #{expected.inspect} but its value of #{actual.inspect} does not" end end end class ProcMatchError < MatchError def message if negative == true "#{param} passed to the block would not return `#{expected.inspect}` but it did" else "#{param} passed to the block would return `#{expected.inspect}` but it is `#{actual.inspect}`" 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 "that comes before #{to}" end end class RequireRelationshipError < RelationshipError def message "that requires #{to}" end end class NotifyRelationshipError < RelationshipError def message "that notifies #{to}" end end class SubscribeRelationshipError < RelationshipError def message "that is subscribed to #{to}" end end end end rspec-puppet-2.2.0/lib/rspec-puppet/example.rb000066400000000000000000000026401253163612600213260ustar00rootroot00000000000000require 'rspec-puppet/support' require 'rspec-puppet/example/define_example_group' require 'rspec-puppet/example/class_example_group' require 'rspec-puppet/example/function_example_group' require 'rspec-puppet/example/host_example_group' RSpec::configure do |c| def c.escaped_path(*parts) Regexp.compile(parts.join('[\\\/]')) end if RSpec::Version::STRING < '3' c.include RSpec::Puppet::DefineExampleGroup, :type => :define, :example_group => { :file_path => c.escaped_path(%w[spec defines]) } c.include RSpec::Puppet::ClassExampleGroup, :type => :class, :example_group => { :file_path => c.escaped_path(%w[spec classes]) } c.include RSpec::Puppet::FunctionExampleGroup, :type => :puppet_function, :example_group => { :file_path => c.escaped_path(%w[spec functions]) } c.include RSpec::Puppet::HostExampleGroup, :type => :host, :example_group => { :file_path => c.escaped_path(%w[spec hosts]) } else c.include RSpec::Puppet::DefineExampleGroup, :type => :define, :file_path => c.escaped_path(%w[spec defines]) c.include RSpec::Puppet::ClassExampleGroup, :type => :class, :file_path => c.escaped_path(%w[spec classes]) c.include RSpec::Puppet::FunctionExampleGroup, :type => :puppet_function, :file_path => c.escaped_path(%w[spec functions]) c.include RSpec::Puppet::HostExampleGroup, :type => :host, :file_path => c.escaped_path(%w[spec hosts]) end end rspec-puppet-2.2.0/lib/rspec-puppet/example/000077500000000000000000000000001253163612600207775ustar00rootroot00000000000000rspec-puppet-2.2.0/lib/rspec-puppet/example/class_example_group.rb000066400000000000000000000003201253163612600253530ustar00rootroot00000000000000module RSpec::Puppet module ClassExampleGroup include RSpec::Puppet::ManifestMatchers include RSpec::Puppet::Support def catalogue @catalogue ||= load_catalogue(:class) end end end rspec-puppet-2.2.0/lib/rspec-puppet/example/define_example_group.rb000066400000000000000000000003221253163612600255020ustar00rootroot00000000000000module RSpec::Puppet module DefineExampleGroup include RSpec::Puppet::ManifestMatchers include RSpec::Puppet::Support def catalogue @catalogue ||= load_catalogue(:define) end end end rspec-puppet-2.2.0/lib/rspec-puppet/example/function_example_group.rb000066400000000000000000000036021253163612600261010ustar00rootroot00000000000000module RSpec::Puppet module FunctionExampleGroup include RSpec::Puppet::FunctionMatchers include RSpec::Puppet::ManifestMatchers include RSpec::Puppet::Support def subject function_name = self.class.top_level_description.downcase vardir = setup_puppet if Puppet.version.to_f >= 4.0 env = Puppet::Node::Environment.create(environment, [File.join(Puppet[:environmentpath],'fixtures','modules')], File.join(Puppet[:environmentpath],'fixtures','manifests')) loader = Puppet::Pops::Loaders.new(env) func = loader.private_environment_loader.load(:function,function_name) return func if func end # Return the method instance for the function. This can be used with # method.call if env return nil unless Puppet::Parser::Functions.function(function_name,env) else return nil unless Puppet::Parser::Functions.function(function_name) end FileUtils.rm_rf(vardir) if File.directory?(vardir) scope.method("function_#{function_name}".intern) end def scope @scope ||= build_scope(compiler, nodename(:function)) end def catalogue @catalogue ||= compiler.catalog end private def compiler @compiler ||= build_compiler end # get a compiler with an attached compiled catalog def build_compiler node_name = nodename(:function) fact_values = 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 Puppet[:code] = pre_cond node_options = { :parameters => fact_values, } stub_facts! fact_values node = build_node(node_name, node_options) compiler = Puppet::Parser::Compiler.new(node) compiler.compile compiler end end end rspec-puppet-2.2.0/lib/rspec-puppet/example/host_example_group.rb000066400000000000000000000003161253163612600252300ustar00rootroot00000000000000module RSpec::Puppet module HostExampleGroup include RSpec::Puppet::ManifestMatchers include RSpec::Puppet::Support def catalogue @catalogue ||= load_catalogue(:host) end end end rspec-puppet-2.2.0/lib/rspec-puppet/matchers.rb000066400000000000000000000004101253163612600214720ustar00rootroot00000000000000require 'rspec-puppet/matchers/create_generic' 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' rspec-puppet-2.2.0/lib/rspec-puppet/matchers/000077500000000000000000000000001253163612600211525ustar00rootroot00000000000000rspec-puppet-2.2.0/lib/rspec-puppet/matchers/compile.rb000066400000000000000000000077251253163612600231420ustar00rootroot00000000000000module RSpec::Puppet module ManifestMatchers class Compile def initialize @failed_resource = "" @check_deps = false @cycles = [] @error_msg = "" end def with_all_deps @check_deps = true self end def and_raise_error(error) @expected_error = error self end def matches?(catalogue) begin @catalogue = catalogue.call if cycles_found? false elsif @check_deps == true && missing_dependencies? false else true end rescue Puppet::Error => e @error_msg = e.message if @expected_error.nil? false else method = @expected_error.is_a?(Regexp) ? :=~ : :== e.message.send(method, @expected_error) end end end def description case @expected_error when nil "compile into a catalogue without dependency cycles" when Regexp "fail to compile and raise an error matching #{@expected_error.inspect}" else "fail to compile and raise the error #{@expected_error.inspect}" end end def failure_message unless @cycles.empty? "dependency cycles found: #{@cycles.join('; ')}" else unless @error_msg.empty? "error during compilation: #{@error_msg}" else "expected that the catalogue would include #{@failed_resource}" end end end def failure_message_when_negated "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? 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 !@cycles.empty? 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 rspec-puppet-2.2.0/lib/rspec-puppet/matchers/count_generic.rb000066400000000000000000000040151253163612600243230ustar00rootroot00000000000000module 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) @catalogue = catalogue.call 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 "expected that the catalogue would " + description + " but it contains #{@actual_number}" end def failure_message_when_negated "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 rspec-puppet-2.2.0/lib/rspec-puppet/matchers/create_generic.rb000066400000000000000000000177111253163612600244450ustar00rootroot00000000000000require '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 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) self end def that_notifies(resource) @notifies.concat(Array(resource)) self end def that_subscribes_to(resource) @subscribes.concat(Array(resource)) self end def that_requires(resource) @requires.concat(Array(resource)) self end def that_comes_before(resource) @befores.concat(Array(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]] 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, args[0]] self else super end end def matches?(catalogue) ret = true @catalogue = catalogue.call resource = @catalogue.resource(@referenced_type, @title) if resource.nil? false else RSpec::Puppet::Coverage.cover!(resource) rsrc_hsh = resource.to_hash 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 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? @errors.empty? end end def failure_message "expected that the catalogue would contain #{@referenced_type}[#{@title}]#{errors}" end def failure_message_when_negated "expected that the catalogue would not contain #{@referenced_type}[#{@title}]#{errors}" end def description values = [] value_str_prefix = "with" if @expected_params_count values << "exactly #{@expected_params_count} parameters" 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 if @notifies.any? value_str_prefix = "that notifies" values = @notifies end if @subscribes.any? value_str_prefix = "that subscribes to" values = @subscribes end if @requires.any? value_str_prefix = "that requires" values = @requires end if @befores.any? value_str_prefix = "that comes before" values = @befores end unless values.empty? if values.length == 1 value_str = " #{value_str_prefix} #{values.first}" else value_str = " #{value_str_prefix} #{values[0..-2].join(", ")} and #{values[-1]}" end end "contain #{@referenced_type}[#{@title}]#{value_str}" end def diffable? true end def expected @errors.map {|e| e.expected if e.respond_to?(:expected)}.compact.join("\n\n") end def actual @errors.map {|e| e.actual if e.respond_to?(:actual)}.compact.join("\n\n") end private def referenced_type(type) type.split('__').map { |r| r.capitalize }.join('::') end def errors @errors.empty? ? "" : " with #{@errors.join(', and parameter ')}" end 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) if first.nil? || second.nil? false else 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 end def notifies?(first, second) if first.nil? || second.nil? false else 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 end # @param resource [Hash] The resource in the catalog # @param list [Array] 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 rspec-puppet-2.2.0/lib/rspec-puppet/matchers/dynamic_matchers.rb000066400000000000000000000011551253163612600250130ustar00rootroot00000000000000module 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 rspec-puppet-2.2.0/lib/rspec-puppet/matchers/include_class.rb000066400000000000000000000012701253163612600243070ustar00rootroot00000000000000module RSpec::Puppet module ManifestMatchers extend RSpec::Matchers::DSL matcher :include_class do |expected_class| match do |catalogue| RSpec.deprecate(:include_class, :contain_class) catalogue.call.classes.include?(expected_class) end description do "include Class[#{expected_class}]" end if RSpec::Version::STRING < '3' failure_message_for_should do |actual| "expected that the catalogue would include Class[#{expected_class}]" end else failure_message do |actual| "expected that the catalogue would include Class[#{expected_class}]" end end end end end rspec-puppet-2.2.0/lib/rspec-puppet/matchers/parameter_matcher.rb000066400000000000000000000065161253163612600251720ustar00rootroot00000000000000module 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] 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. actual = [*actual] if expected.is_a?(Array) retval = check(expected, actual) unless retval @errors << MatchError.new(@parameter, expected, actual, !@should_match) end retval end # @!attribute [r] errors # @return [Array] 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) return false if actual.nil? && !expected.nil? 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]) 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 rspec-puppet-2.2.0/lib/rspec-puppet/matchers/run.rb000066400000000000000000000113011253163612600222770ustar00rootroot00000000000000module RSpec::Puppet module FunctionMatchers class Run def matches?(func_obj) @func_obj = func_obj if @params if Puppet.version.to_f >= 4.0 and ! @func_obj.respond_to?(:receiver) @func = lambda { func_obj.call({}, *@params) } else @func = lambda { func_obj.call(@params) } end else if Puppet.version.to_f >= 4.0 and ! @func_obj.respond_to?(:receiver) @func = lambda { func_obj.call({}) } else @func = lambda { func_obj.call } end end begin @actual_return = @func.call @has_returned = true rescue Exception => e @actual_error = e end if @has_expected_error if @has_returned return false elsif @actual_error.is_a?(@expected_error) case @expected_error_message when nil return true when Regexp return @actual_error.message =~ @expected_error_message else return @actual_error.message == @expected_error_message end else # error did not match return false end elsif @has_expected_return if !@has_returned return false else case @expected_return when Regexp return @actual_return =~ @expected_return else return @actual_return == @expected_return end end else return @has_returned end end def with_params(*params) @params = params # stringify immediately to protect us from the params being changed by # the subject, e.g. with params.shift @func_args = @params.inspect[1..-2] self end def and_return(value) @has_expected_return = true @expected_return = value if value.is_a? Regexp @desc = "match #{value.inspect}" else @desc = "return #{value.inspect}" end self end def and_raise_error(error_or_message, message=nil) @has_expected_error = true 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 if @expected_error_message.is_a? Regexp @desc = "raise an #{@expected_error} with the message matching #{@expected_error_message.inspect}" else @desc = "raise an #{@expected_error}" unless @expected_error_message.nil? @desc += "with the message #{@expected_error_message.inspect}" end end self end def failure_message failure_message_generic(:should, @func_obj) end def failure_message_when_negated failure_message_generic(:should_not, @func_obj) end def description if @desc "run #{func_name}(#{func_params}) and #{@desc}" else "run #{func_name}(#{func_params}) without error" end end private def func_name if Puppet.version.to_f >= 4.0 and ! @func_ob and ! @func_obj.respond_to?(:receiver) @func_name ||= @func_obj.class.name else @func_name ||= @func_obj.name.to_s.gsub(/^function_/, '') end end def func_params @func_args end def failure_message_actual(type) if type != :should '' elsif @actual_error if @has_expected_return " instead of raising #{@actual_error.class.inspect}(#{@actual_error})" else " instead of #{@actual_error.class.inspect}(#{@actual_error})" end else # function has returned if @has_expected_error " instead of returning #{@actual_return.inspect}" else " instead of #{@actual_return.inspect}" end end end def failure_message_generic(type, func_obj) message = "expected #{func_name}(#{func_params}) to " message << "not " if type == :should_not if @has_expected_return message << "have returned #{@expected_return.inspect}" else if @has_expected_error message << "have raised #{@expected_error.inspect}" if @expected_error_message message << " matching #{@expected_error_message.inspect}" end else message << "have run successfully" end end message << failure_message_actual(type) end end end end rspec-puppet-2.2.0/lib/rspec-puppet/setup.rb000066400000000000000000000107671253163612600210440ustar00rootroot00000000000000require 'puppet' if Puppet.version.to_f >= 4.0 require 'puppet/pops' end require 'fileutils' module RSpec::Puppet class Setup def self.run(module_name=nil) unless is_module_dir? $stderr.puts "Does not appear to be a Puppet module. Aborting" return false end if module_name.nil? module_name = get_module_name if module_name.nil? $stderr.puts "Unable to determine module name. Aborting" return false end end [ 'spec', 'spec/classes', 'spec/defines', 'spec/functions', 'spec/hosts', 'spec/fixtures', 'spec/fixtures/manifests', 'spec/fixtures/modules', "spec/fixtures/modules/#{module_name}", ].each { |dir| safe_mkdir(dir) } safe_touch('spec/fixtures/manifests/site.pp') %w(data manifests lib files templates).each do |dir| if File.exist? dir safe_make_symlink("../../../../#{dir}", "spec/fixtures/modules/#{module_name}/#{dir}") end end safe_create_spec_helper safe_create_rakefile end protected def self.get_module_name module_name = nil Dir["manifests/*.pp"].entries.each do |manifest| 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) if Puppet.version.to_f >= 4.0 p = Puppet::Pops::Parser::Lexer2.new else p = Puppet::Parser::Lexer.new end 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 def self.is_module_dir? Dir["*"].entries.include? "manifests" end def self.safe_mkdir(dir) if File.exists? dir unless File.directory? dir $stderr.puts "!! #{dir} already exists and is not a directory" end else FileUtils.mkdir dir puts " + #{dir}/" end end def self.safe_touch(file) if File.exists? file unless File.file? file $stderr.puts "!! #{file} already exists and is not a regular file" end else FileUtils.touch file puts " + #{file}" 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' fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures')) RSpec.configure do |c| c.module_path = File.join(fixture_path, 'modules') c.manifest_dir = File.join(fixture_path, 'manifests') c.environmentpath = File.join(Dir.pwd, 'spec') end EOF safe_create_file('spec/spec_helper.rb', content) end def self.safe_make_symlink(source, target) if File.exists? target unless File.symlink? target $stderr.puts "!! #{target} already exists and is not a symlink" end else FileUtils.ln_s(source, target) puts " + #{target}" end end def self.safe_create_rakefile content = <<-'EOF' require 'rake' require 'rspec/core/rake_task' desc "Run all RSpec code examples" RSpec::Core::RakeTask.new(:rspec) do |t| File.exist?('spec/spec.opts') ? opts = File.read("spec/spec.opts").chomp : opts = "" t.rspec_opts = opts 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" File.exist?('spec/spec.opts') ? opts = File.read("spec/spec.opts").chomp : opts = "" t.rspec_opts = opts 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 safe_create_file('Rakefile', content) end end end rspec-puppet-2.2.0/lib/rspec-puppet/support.rb000066400000000000000000000200771253163612600214130ustar00rootroot00000000000000module RSpec::Puppet module Support @@cache = {} def subject lambda { catalogue } end def environment 'rp_env' end def load_catalogue(type) vardir = setup_puppet if Puppet.version.to_f >= 4.0 or Puppet[:parser] == 'future' code = [pre_cond, test_manifest(type)].compact.join("\n") else code = [import_str, pre_cond, test_manifest(type)].compact.join("\n") end node_name = nodename(type) hiera_config_value = self.respond_to?(:hiera_config) ? hiera_config : nil catalogue = build_catalog(node_name, facts_hash(node_name), hiera_config_value, code) test_module = class_name.split('::').first RSpec::Puppet::Coverage.add_filter(type.to_s, self.class.description) RSpec::Puppet::Coverage.add_from_catalog(catalogue, test_module) FileUtils.rm_rf(vardir) if File.directory?(vardir) catalogue end def import_str if File.exists?(File.join(Puppet[:modulepath], 'manifests', 'init.pp')) path_to_manifest = File.join([ Puppet[:modulepath], 'manifests', class_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) if type == :class if !self.respond_to?(:params) || params == {} "include #{class_name}" else "class { '#{class_name}': #{param_str} }" end elsif type == :define if self.respond_to? :params "#{class_name} { '#{title}': #{param_str} }" else "#{class_name} { '#{title}': }" end elsif type == :host nil end end def nodename(type) return node if self.respond_to?(:node) if [:class, :define, :function].include? type Puppet[:certname] else class_name end end def class_name self.class.top_level_description.downcase end def pre_cond if self.respond_to?(:pre_condition) && !pre_condition.nil? if pre_condition.is_a? Array pre_condition.compact.join("\n") else pre_condition end else nil end end def facts_hash(node) facts_val = { 'clientversion' => Puppet::PUPPETVERSION, 'environment' => environment, 'hostname' => node.split('.').first, 'fqdn' => node, 'domain' => node.split('.', 2).last, 'clientcert' => node } 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 if Puppet.version.to_f >= 4.0 settings = [ [:modulepath, :module_path], [:environmentpath, :environmentpath], [:config, :config], [:confdir, :confdir], [:hiera_config, :hiera_config], [:strict_variables, :strict_variables], ] elsif Puppet.version.to_f >= 3.0 settings = [ [:modulepath, :module_path], [:manifestdir, :manifest_dir], [:manifest, :manifest], [:templatedir, :template_dir], [:config, :config], [:confdir, :confdir], [:hiera_config, :hiera_config], [:parser, :parser], [:trusted_node_data, :trusted_node_data], [:ordering, :ordering], [:stringify_facts, :stringify_facts], [:strict_variables, :strict_variables], ] else settings = [ [:modulepath, :module_path], [:manifestdir, :manifest_dir], [:manifest, :manifest], [:templatedir, :template_dir], [:config, :config], [:confdir, :confdir], ] end settings.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 Dir["#{Puppet[:modulepath]}/*/lib"].entries.each do |lib| $LOAD_PATH << lib end vardir end def build_catalog_without_cache(nodename, facts_val, hiera_config_val, code) # If we're going to rebuild the catalog, we should clear the cached instance # of Hiera that Puppet is using. This opens the possibility of the catalog # now being rebuilt against a differently configured Hiera (i.e. :hiera_config # set differently in one example group vs. another). # It would be nice if Puppet offered a public API for invalidating their # cached instance of Hiera, but que sera sera. We will go directly against # the implementation out of absolute necessity. HieraPuppet.instance_variable_set('@hiera', nil) if defined? HieraPuppet Puppet[:code] = code stub_facts! facts_val node_obj = Puppet::Node.new(nodename) node_obj.merge(facts_val) # trying to be compatible with 2.7 as well as 2.6 if Puppet::Resource::Catalog.respond_to? :find Puppet::Resource::Catalog.find(node_obj.name, :use_node => node_obj) elsif Puppet.version.to_f >= 4.0 env = Puppet::Node::Environment.create( environment, [File.join(Puppet[:environmentpath],'fixtures','modules')], File.join(Puppet[:environmentpath],'fixtures','manifests')) loader = Puppet::Environments::Static.new(env) Puppet.override({:environments => loader}, 'fixtures') do node_obj.environment = env Puppet::Resource::Catalog.indirection.find(node_obj.name, :use_node => node_obj) end else Puppet::Resource::Catalog.indirection.find(node_obj.name, :use_node => node_obj) end end 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 def munge_facts(facts) output = {} facts.keys.each { |key| output[key.to_s] = facts[key] } output end def escape_special_chars(string) string.gsub!(/\$/, "\\$") string end def build_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 = {}) if Puppet.version.to_f >= 4.0 node_environment = Puppet::Node::Environment.create( environment, [File.join(Puppet[:environmentpath],'fixtures','modules')], File.join(Puppet[:environmentpath],'fixtures','manifests')) else node_environment = Puppet::Node::Environment.new(environment) end opts.merge!({:environment => node_environment}) Puppet::Node.new(name, opts) end def rspec_compatibility if RSpec::Version::STRING < '3' # RSpec 2 compatibility: alias_method :failure_message_for_should, :failure_message alias_method :failure_message_for_should_not, :failure_message_when_negated end end end end rspec-puppet-2.2.0/rspec-puppet.gemspec000066400000000000000000000007451253163612600201510ustar00rootroot00000000000000Gem::Specification.new do |s| s.name = 'rspec-puppet' s.version = '2.2.0' 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.license = 'MIT' s.executables = ['rspec-puppet-init'] s.files = Dir['CHANGELOG.md', 'LICENSE.md', 'README.md', 'lib/**/*', 'bin/**/*'] s.add_dependency 'rspec' s.authors = ['Tim Sharpe'] s.email = 'tim@sharpe.id.au' end rspec-puppet-2.2.0/spec/000077500000000000000000000000001253163612600151015ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/classes/000077500000000000000000000000001253163612600165365ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/classes/array_spec.rb000066400000000000000000000032121253163612600212110ustar00rootroot00000000000000require 'spec_helper' describe 'structured_data' do describe "with a single level array of strings" do let(:params) do {'data' => ['foo', 'bar', 'baz', 'quux']} end it { should contain_structured_data__def('thing').with( { 'data' => ['foo', 'bar', 'baz', 'quux'] } ) } end describe "with integers as data values" do let(:params) do { 'data' => ['first', 1, 'second', 2] } end it { should contain_structured_data__def('thing').with( { 'data' => ['first', 1, 'second', 2] } ) } end describe 'with nested arrays' do let(:params) do { 'data' => [ 'first', 'second', ['third', 'fourth'], 5, 6 ] } end # Puppet 2.6 will automatically flatten nested arrays. If we're going # to be testing recursive data structures, we might as well ensure that # we're still handling numeric values correctly. describe 'on Puppet 2.6', :if => Puppet.version =~ /^2\.6/ do it { should contain_structured_data__def('thing').with( { 'data' => [ 'first', 'second', 'third', 'fourth', 5, 6 ] } ) } end describe 'on Puppet 2.7 and later', :unless => Puppet.version =~ /^2\.6/ do it { should contain_structured_data__def('thing').with( { 'data' => [ 'first', 'second', ['third', 'fourth'], 5, 6 ] } ) } end end end rspec-puppet-2.2.0/spec/classes/boolean_regexp_spec.rb000066400000000000000000000006651253163612600230750ustar00rootroot00000000000000require 'spec_helper' describe 'boolean_test' 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) } # `should_not with_messsage` == `should without_message` it { should_not create_notify("bool testing").with_message(/true/) } it { should create_notify("bool testing").without_message(/true/) } end rspec-puppet-2.2.0/spec/classes/boolean_spec.rb000066400000000000000000000004351253163612600215160ustar00rootroot00000000000000require 'spec_helper' if Puppet::PUPPETVERSION !~ /0\.2/ describe 'boolean_test' 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 rspec-puppet-2.2.0/spec/classes/cycle_bad_spec.rb000066400000000000000000000001201253163612600217730ustar00rootroot00000000000000require 'spec_helper' describe 'cycle::bad' do it { should_not compile } end rspec-puppet-2.2.0/spec/classes/cycle_good_spec.rb000066400000000000000000000001151253163612600222010ustar00rootroot00000000000000require 'spec_helper' describe 'cycle::good' do it { should compile } end rspec-puppet-2.2.0/spec/classes/escape_spec.rb000066400000000000000000000002451253163612600213360ustar00rootroot00000000000000require 'spec_helper' describe 'escape' do let(:params) { { :content => '$MSG foo' } } it { should contain_file('/tmp/escape').with_content(/\$MSG foo/) } end rspec-puppet-2.2.0/spec/classes/hash_spec.rb000066400000000000000000000032221253163612600210170ustar00rootroot00000000000000require 'spec_helper' describe 'structured_data' do describe "with a single level hash of strings" do let(:params) do {'data' => {'foo' => 'bar', 'baz' => 'quux'}} end it { should contain_structured_data__def('thing').with( {'data' => {'foo' => 'bar', 'baz' => 'quux'}} ) } end describe "with integers as keys" do let(:params) do { 'data' => {1 => 'uno', 2 => 'dos'}} end context "puppet less than 4", :unless => Puppet.version.to_f >= 4.0 do it { should contain_structured_data__def('thing').with( { 'data' => {"1" => 'uno', "2" => 'dos'}} ) } end context "puppet 4 or greater", :if => Puppet.version.to_f >= 4.0 do it { should contain_structured_data__def('thing').with( { 'data' => {1 => 'uno', 2 => 'dos'}} ) } end end describe 'with integers as values' do let(:params) do { 'data' => {'first' => 1, 'second' => 2}} end it { should contain_structured_data__def('thing').with( { 'data' => {'first' => 1, 'second' => 2}} ) } end describe 'with nested hashes' do let(:params) do { 'data' => { 'first' => 1, 'second' => 2, 'third' => { 'alpha' => 'a', 'beta' => 'b', } } } end it { should contain_structured_data__def('thing').with( { 'data' => { 'first' => 1, 'second' => 2, 'third' => { 'alpha' => 'a', 'beta' => 'b', } } } ) } end end rspec-puppet-2.2.0/spec/classes/relationship__before_spec.rb000066400000000000000000000014301253163612600242550ustar00rootroot00000000000000require 'spec_helper' describe 'relationships::before' do it { should contain_notify('foo').that_comes_before('Notify[bar]') } it { should contain_notify('foo').that_comes_before('Notify[baz]') } it { should contain_notify('bar').that_comes_before('Notify[baz]') } it { should contain_notify('bar').that_requires('Notify[foo]') } it { should contain_notify('baz').that_requires('Notify[foo]') } it { should contain_notify('baz').that_requires('Notify[bar]') } it { should contain_notify('foo').that_comes_before(['Notify[bar]','Notify[baz]']) } it { should contain_notify('bar').that_comes_before(['Notify[baz]']) } it { should contain_notify('bar').that_requires(['Notify[foo]']) } it { should contain_notify('baz').that_requires(['Notify[foo]','Notify[bar]']) } end rspec-puppet-2.2.0/spec/classes/relationship__notify_spec.rb000066400000000000000000000014451253163612600243310ustar00rootroot00000000000000require 'spec_helper' describe 'relationships::notify' do it { should contain_notify('foo').that_notifies('Notify[bar]') } it { should contain_notify('baz').that_notifies('Notify[bar]') } it { should contain_notify('baz').that_notifies('Notify[gronk]') } it { should contain_notify('gronk').that_subscribes_to('Notify[baz]') } it { should contain_notify('bar').that_subscribes_to('Notify[baz]') } it { should contain_notify('bar').that_subscribes_to('Notify[foo]') } it { should contain_notify('foo').that_notifies(['Notify[bar]']) } it { should contain_notify('baz').that_notifies(['Notify[bar]','Notify[gronk]']) } it { should contain_notify('gronk').that_subscribes_to(['Notify[baz]']) } it { should contain_notify('bar').that_subscribes_to(['Notify[baz]','Notify[foo]']) } end rspec-puppet-2.2.0/spec/classes/sysctl_common_spec.rb000066400000000000000000000064171253163612600227760ustar00rootroot00000000000000require '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 expect(subject).to 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 expect(subject).to 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 expect(subject).to contain_exec('sysctl/reload').without('foo') end it 'should pass if the parameter names are not contained in the resource' do expect(subject).to 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 expect(subject).to contain_exec('sysctl/reload').without(['foo', 'returns']) end.to raise_error(RSpec::Expectations::ExpectationNotMetError) end end describe 'when using without to specify parameter value(s)' do it 'should pass if the parameter value is not contained in the resource' do expect(subject).to contain_exec('sysctl/reload').without_refreshonly('false') end it 'should fail if the parameter value is contained in the resource' do expect do expect(subject).to contain_exec('sysctl/reload').without_refreshonly('true') 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") } it { should have_class_count(1) } it { should have_exec_resource_count(1) } it { if Puppet.version.to_f >= 4.0 should have_resource_count(1) else should have_resource_count(2) end } end describe 'sysctl::common' do it { should contain_exec('sysctl/reload').only_with( :command => '/sbin/sysctl -p /etc/sysctl.conf', :refreshonly => true, :returns => [0, 2] )} it { should contain_exec('sysctl/reload') \ .only_with_command('/sbin/sysctl -p /etc/sysctl.conf') \ .only_with_refreshonly(true) \ .only_with_returns([0, 2]) } it 'should fail if not enough parameters are contained in the resource' do expect do expect(subject).to contain_exec('sysctl/reload').only_with( :command => '/sbin/sysctl -p /etc/sysctl.conf', :returns => [0, 2] ) end.to raise_error(RSpec::Expectations::ExpectationNotMetError) end it 'should fail if different parameters are contained in the resource' do expect do expect(subject).to contain_exec('sysctl/reload').only_with( :command => '/sbin/sysctl -p /etc/sysctl.conf', :refreshonly => true, :creates => '/tmp/bla' ) end.to raise_error(RSpec::Expectations::ExpectationNotMetError) end end rspec-puppet-2.2.0/spec/classes/test_api.rb000066400000000000000000000007601253163612600206760ustar00rootroot00000000000000require 'spec_helper' describe 'test::bare_class' do describe 'rspec group' do it 'should have a catalogue method' do catalogue.should be_a(Puppet::Resource::Catalog) end it 'subject should return a catalogue' do subject.should be_a(Puppet::Resource::Catalog) end describe 'derivative group' do subject { catalogue.resource('Notify', 'foo') } it 'can redefine subject' do subject.should be_a(Puppet::Resource) end end end end rspec-puppet-2.2.0/spec/classes/test_classes_used_spec.rb000066400000000000000000000007201253163612600236100ustar00rootroot00000000000000require 'spec_helper' describe 'test::classes_used' do it { expect(RSpec).to receive(:deprecate).with(:include_class, :contain_class) should include_class('test::bare_class') } it { expect(RSpec).to receive(:deprecate).with(:include_class, :contain_class) should include_class('test::parameterised_class') } it { should contain_class('test::parameterised_class').with_text('bar') } it { should contain_class('test::bare_class') } end rspec-puppet-2.2.0/spec/classes/test_compile_error_spec.rb000066400000000000000000000003241253163612600237740ustar00rootroot00000000000000require 'spec_helper' describe 'test::compile_error' do it 'should provide a useful message when compilation fails' do should compile.with_all_deps.and_raise_error(/Parameter managehome failed/) end end rspec-puppet-2.2.0/spec/classes/test_fail_spec.rb000066400000000000000000000001441253163612600220460ustar00rootroot00000000000000require 'spec_helper' describe 'test::fail' do it { should compile.and_raise_error(/test/) } end rspec-puppet-2.2.0/spec/coverage_spec.rb000066400000000000000000000001031253163612600202250ustar00rootroot00000000000000require 'spec_helper' at_exit { RSpec::Puppet::Coverage.report! } rspec-puppet-2.2.0/spec/defines/000077500000000000000000000000001253163612600165165ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/defines/escape_def_spec.rb000066400000000000000000000003021253163612600221260ustar00rootroot00000000000000require 'spec_helper' describe 'escape::def' do let(:title) { '/tmp/bla' } let(:params) { {:content => 'bar $BLA'} } it { should contain_file('/tmp/bla').with_content(/bar \$BLA/) } end rspec-puppet-2.2.0/spec/defines/sysctl_before_spec.rb000066400000000000000000000013531253163612600227220ustar00rootroot00000000000000require '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_sysctl__before('test define').with_value("title") } it { should contain_class("sysctl::common") } end rspec-puppet-2.2.0/spec/defines/sysctl_spec.rb000066400000000000000000000011221253163612600213720ustar00rootroot00000000000000require 'spec_helper' nodoublequotes = Proc.new do |x| not x =~ /"/ end describe 'sysctl' do let(:title) { 'vm.swappiness' } let(:params) { {:value => '60'} } let(:pre_condition) { } it { should contain_class('sysctl::common') } it { should create_augeas('sysctl/vm.swappiness') \ .with_context('/files/etc/sysctl.conf') \ .with_changes("set vm.swappiness '60'") \ .with_changes(nodoublequotes) \ .with_onlyif("match vm.swappiness[.='60'] size == 0") \ .with_notify('Exec[sysctl/reload]')\ .without_foo } it { should have_sysctl_resource_count(1) } end rspec-puppet-2.2.0/spec/defines/test_api.rb000066400000000000000000000005571253163612600206620ustar00rootroot00000000000000require 'spec_helper' describe 'sysctl' do let(:title) { 'vm.swappiness' } let(:params) { {:value => '60'} } describe 'rspec group' do it 'should have a catalogue method' do catalogue.should be_a(Puppet::Resource::Catalog) end it 'subject should return a catalogue' do subject.should be_a(Puppet::Resource::Catalog) end end end rspec-puppet-2.2.0/spec/fixtures/000077500000000000000000000000001253163612600167525ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/manifests/000077500000000000000000000000001253163612600207435ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/manifests/site.pp000066400000000000000000000013411253163612600222470ustar00rootroot00000000000000node default { notify { 'test': } } node 'testhost_a' { file { '/tmp/a': } } node /testhost/ { include sysctl::common } node 'good_dep_host' { file { 'tmpdir': alias => '/tmp', path => '/tmp', } file { '/tmp/deptest1': require => File['tmpdir'], } file { '/tmp/deptest2': require => File['/tmp'], } } node 'bad_dep_host' { file { '/tmp': require => File['/'], } } node 'facts.acme.com' { file { 'environment': path => $environment } file { 'clientversion': path => $clientversion } file { 'fqdn': path => $fqdn } file { 'hostname': path => $hostname } file { 'domain': path => $domain } file { 'clientcert': path => "cert ${clientcert}" } } rspec-puppet-2.2.0/spec/fixtures/modules/000077500000000000000000000000001253163612600204225ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/boolean_test/000077500000000000000000000000001253163612600231005ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/boolean_test/manifests/000077500000000000000000000000001253163612600250715ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/boolean_test/manifests/init.pp000066400000000000000000000003251253163612600263750ustar00rootroot00000000000000class boolean_test($bool) { $real_bool = $bool ? { true => false, false => true, } if ($real_bool) { notify {"bool testing": message => "This will print when \$bool is false." } } } rspec-puppet-2.2.0/spec/fixtures/modules/cycle/000077500000000000000000000000001253163612600215215ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/cycle/manifests/000077500000000000000000000000001253163612600235125ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/cycle/manifests/bad.pp000066400000000000000000000001721253163612600246010ustar00rootroot00000000000000class cycle::bad { notify { 'foo': require => Notify['bar']; 'bar': require => Notify['foo']; } } rspec-puppet-2.2.0/spec/fixtures/modules/cycle/manifests/good.pp000066400000000000000000000001351253163612600250020ustar00rootroot00000000000000class cycle::good { notify { 'foo': require => Notify['bar']; 'bar': ; } } rspec-puppet-2.2.0/spec/fixtures/modules/cycle/manifests/init.pp000066400000000000000000000000001253163612600250040ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/escape/000077500000000000000000000000001253163612600216625ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/escape/manifests/000077500000000000000000000000001253163612600236535ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/escape/manifests/def.pp000066400000000000000000000001511253163612600247470ustar00rootroot00000000000000define escape::def($content = '') { file { $title : ensure => file, content => $content } } rspec-puppet-2.2.0/spec/fixtures/modules/escape/manifests/init.pp000066400000000000000000000001501253163612600251530ustar00rootroot00000000000000class escape($content = '') { file { '/tmp/escape': ensure => file, content => $content } } rspec-puppet-2.2.0/spec/fixtures/modules/relationships/000077500000000000000000000000001253163612600233065ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/relationships/manifests/000077500000000000000000000000001253163612600252775ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/relationships/manifests/before.pp000066400000000000000000000003011253163612600270740ustar00rootroot00000000000000class relationships::before { notify { 'foo': before => [Notify['bar']], } notify { 'bar': } notify { 'baz': } Notify['foo'] -> Notify['baz'] Notify['baz'] <- Notify['bar'] } rspec-puppet-2.2.0/spec/fixtures/modules/relationships/manifests/notify.pp000066400000000000000000000003301253163612600271440ustar00rootroot00000000000000class relationships::notify { notify { 'foo': notify => Notify['bar'] } notify { 'bar': subscribe => Notify['baz'], } notify { 'baz': } notify { 'gronk': } Notify['gronk'] <~ Notify['baz'] } rspec-puppet-2.2.0/spec/fixtures/modules/structured_data/000077500000000000000000000000001253163612600236175ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/structured_data/manifests/000077500000000000000000000000001253163612600256105ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/structured_data/manifests/def.pp000066400000000000000000000001751253163612600267120ustar00rootroot00000000000000define structured_data::def($data = {}) { $template = inline_template('<%= @data.inspect %>') notify { "$template": } } rspec-puppet-2.2.0/spec/fixtures/modules/structured_data/manifests/init.pp000066400000000000000000000001321253163612600271100ustar00rootroot00000000000000class structured_data($data) { structured_data::def { 'thing': data => $data, } } rspec-puppet-2.2.0/spec/fixtures/modules/sysctl/000077500000000000000000000000001253163612600217435ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/sysctl/manifests/000077500000000000000000000000001253163612600237345ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/sysctl/manifests/init.pp000066400000000000000000000015131253163612600252400ustar00rootroot00000000000000class 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_test($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." } } rspec-puppet-2.2.0/spec/fixtures/modules/test/000077500000000000000000000000001253163612600214015ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/test/lib/000077500000000000000000000000001253163612600221475ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/test/lib/puppet/000077500000000000000000000000001253163612600234645ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/test/lib/puppet/functions/000077500000000000000000000000001253163612600254745ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/test/lib/puppet/functions/nil_function.rb000066400000000000000000000002721253163612600305110ustar00rootroot00000000000000Puppet::Functions.create_function(:nil_function) do def nil_function(should_raise) if should_raise raise Puppet::ParseError, 'Forced Failure - new version' end end end rspec-puppet-2.2.0/spec/fixtures/modules/test/lib/puppet/functions/test_function.rb000066400000000000000000000001621253163612600307040ustar00rootroot00000000000000Puppet::Functions.create_function(:test_function) do def test_function(value) "value is #{value}" end end rspec-puppet-2.2.0/spec/fixtures/modules/test/lib/puppet/parser/000077500000000000000000000000001253163612600247605ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/test/lib/puppet/parser/functions/000077500000000000000000000000001253163612600267705ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/test/lib/puppet/parser/functions/nasty.rb000066400000000000000000000001451253163612600304530ustar00rootroot00000000000000Puppet::Parser::Functions.newfunction(:nasty, :type => :rvalue) do |arguments| arguments.shift end rspec-puppet-2.2.0/spec/fixtures/modules/test/lib/puppet/parser/functions/nil_function.rb000066400000000000000000000002351253163612600320040ustar00rootroot00000000000000Puppet::Parser::Functions.newfunction(:nil_function) do |arguments| if arguments[0] raise Puppet::ParseError, 'Forced Failure - old version' end end rspec-puppet-2.2.0/spec/fixtures/modules/test/manifests/000077500000000000000000000000001253163612600233725ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/fixtures/modules/test/manifests/bare_class.pp000066400000000000000000000000571253163612600260330ustar00rootroot00000000000000class test::bare_class { notify { 'foo': } } rspec-puppet-2.2.0/spec/fixtures/modules/test/manifests/classes_used.pp000066400000000000000000000001671253163612600264140ustar00rootroot00000000000000class test::classes_used { include test::bare_class class { 'test::parameterised_class': text => 'bar', } } rspec-puppet-2.2.0/spec/fixtures/modules/test/manifests/compile_error.pp000066400000000000000000000002131253163612600265700ustar00rootroot00000000000000class test::compile_error { user { 'foo': ensure => present, managehome => true, provider => 'directoryservice', } } rspec-puppet-2.2.0/spec/fixtures/modules/test/manifests/fail.pp000066400000000000000000000000441253163612600246440ustar00rootroot00000000000000class test::fail { fail("test") } rspec-puppet-2.2.0/spec/fixtures/modules/test/manifests/parameterised_class.pp000066400000000000000000000000771253163612600277510ustar00rootroot00000000000000class test::parameterised_class($text) { notify { $text: } } rspec-puppet-2.2.0/spec/functions/000077500000000000000000000000001253163612600171115ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/functions/catalogue_spec.rb000066400000000000000000000007621253163612600224210ustar00rootroot00000000000000require 'spec_helper' describe 'split' do describe 'rspec group' do it 'should have a catalogue method' do expect(catalogue).to be_a(Puppet::Resource::Catalog) end it 'catalogue should not change after subject is called' do expect(catalogue).to be_a(Puppet::Resource::Catalog) pre_id = catalogue.object_id should run.with_params('aoeu', 'o').and_return(['a', 'eu']) post_id = catalogue.object_id expect(pre_id).to eq post_id end end end rspec-puppet-2.2.0/spec/functions/nasty_spec.rb000066400000000000000000000007431253163612600216120ustar00rootroot00000000000000require 'spec_helper' describe 'nasty' do it { expect(subject).not_to eq(nil) } it { expect(subject).to run.with_params('foo', 'bar').and_return('foo') } describe 'the underlying Run matcher' do it 'should not have its description manipulated by running the function' do run_matcher = run.with_params('foo', 'bar').and_return('foo') should run_matcher expect(run_matcher.description).to eq('run nasty("foo", "bar") and return "foo"') end end end rspec-puppet-2.2.0/spec/functions/nil_function_spec.rb000066400000000000000000000007041253163612600231400ustar00rootroot00000000000000require 'spec_helper' describe 'nil_function' do it { should run.with_params(false).and_return(nil) } it { should run.with_params(true).and_raise_error(Puppet::ParseError, /Forced Failure/) } let(:version) do if Puppet[:parser] == 'future' or Puppet.version.to_f >= 4 'new version' else 'old version' end end it { should run.with_params(true).and_raise_error(Puppet::ParseError, /Forced Failure - #{version}/) } end rspec-puppet-2.2.0/spec/functions/regsubst_spec.rb000066400000000000000000000007021253163612600223050ustar00rootroot00000000000000require 'spec_helper' describe 'regsubst' do # used to test the fact that expected result can be a regexp it { should run.with_params('thisisatest', '^192', '254').and_return(/sat/) } it { should run.with_params('thisisatest', 'sat', 'xyz').and_return(/ixyze/) } it { should run.with_params('thisisatest', 'sat', 'xyz').and_return('thisixyzest') } it { should run.with_params('thisisatest', 'sat', 'xyz').and_return(/^thisixyzest$/) } end rspec-puppet-2.2.0/spec/functions/split_spec.rb000066400000000000000000000013541253163612600216060ustar00rootroot00000000000000require 'spec_helper' describe 'split' do it { should run.with_params('aoeu', 'o').and_return(['a', 'eu']) } it { should_not run.with_params('foo').and_raise_error(Puppet::DevError) } if (Puppet.version.split('.').map { |s| s.to_i } <=> [3, 1]) >= 0 expected_error = ArgumentError else expected_error = Puppet::ParseError end if Puppet.version.to_f >= 4.0 expected_error_message = /mis-matched arguments/ else expected_error_message = /number of arguments/ end it { should run.with_params('foo').and_raise_error(expected_error) } it { should run.with_params('foo').and_raise_error(expected_error, expected_error_message) } it { should run.with_params('foo').and_raise_error(expected_error_message) } end rspec-puppet-2.2.0/spec/functions/test_function_spec.rb000066400000000000000000000003171253163612600233350ustar00rootroot00000000000000require 'spec_helper' describe 'test_function', :if => Puppet.version.to_f >= 4.0 do # Verify that we can load functions from modules it { should run.with_params('foo').and_return(/value is foo/) } end rspec-puppet-2.2.0/spec/hosts/000077500000000000000000000000001253163612600162415ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/hosts/array_spec.rb000066400000000000000000000003511253163612600207150ustar00rootroot00000000000000require 'spec_helper' describe 'someotherhost' do let(:pre_condition) { <<-EOF define foo($param) { } foo { 'bar': param => ['baz'], } EOF } it { should contain_foo('bar').with_param(['baz']) } end rspec-puppet-2.2.0/spec/hosts/bad_dep_host_spec.rb000066400000000000000000000001401253163612600222060ustar00rootroot00000000000000require 'spec_helper' describe 'bad_dep_host' do it { should_not compile.with_all_deps } end rspec-puppet-2.2.0/spec/hosts/environment_spec.rb000066400000000000000000000005351253163612600221470ustar00rootroot00000000000000require 'spec_helper' describe 'facts.acme.com' do context 'without an explicit environment setting' do it { should contain_file('environment').with_path('rp_env') } end context 'when specifying an explicit environment' do let(:environment) { 'test_env' } it { should contain_file('environment').with_path('test_env') } end end rspec-puppet-2.2.0/spec/hosts/facts_spec.rb000066400000000000000000000007151253163612600207030ustar00rootroot00000000000000require 'spec_helper' describe 'facts.acme.com' do it { should contain_file('environment').with_path('rp_env') } it { should contain_file('clientversion').with_path(Puppet::PUPPETVERSION) } it { should contain_file('fqdn').with_path('facts.acme.com') } it { should contain_file('hostname').with_path('facts') } it { should contain_file('domain').with_path('acme.com') } it { should contain_file('clientcert').with_path('cert facts.acme.com') } end rspec-puppet-2.2.0/spec/hosts/foo_spec.rb000066400000000000000000000002241253163612600203610ustar00rootroot00000000000000require 'spec_helper' describe 'foo.example.com' do it { should_not contain_class('sysctl::common') } it { should contain_notify('test') } end rspec-puppet-2.2.0/spec/hosts/good_dep_host_spec.rb000066400000000000000000000001351253163612600224140ustar00rootroot00000000000000require 'spec_helper' describe 'good_dep_host' do it { should compile.with_all_deps } end rspec-puppet-2.2.0/spec/hosts/test_api.rb000066400000000000000000000004611253163612600203770ustar00rootroot00000000000000require 'spec_helper' describe 'foo.example.com' do describe 'rspec group' do it 'should have a catalogue method' do catalogue.should be_a(Puppet::Resource::Catalog) end it 'subject should return a catalogue' do subject.should be_a(Puppet::Resource::Catalog) end end end rspec-puppet-2.2.0/spec/hosts/testhost_spec.rb000066400000000000000000000004031253163612600214520ustar00rootroot00000000000000require 'spec_helper' describe 'testhost' do it { should contain_class('sysctl::common') } describe 'testhost_a' do let(:node) { 'testhost_a' } it { should_not contain_class('sysctl::common') } it { should contain_file('/tmp/a') } end end rspec-puppet-2.2.0/spec/spec_helper.rb000066400000000000000000000004411253163612600177160ustar00rootroot00000000000000require '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') c.environmentpath = File.join(Dir.pwd, 'spec') end rspec-puppet-2.2.0/spec/support_spec.rb000066400000000000000000000062711253163612600201620ustar00rootroot00000000000000require 'spec_helper' describe RSpec::Puppet::Support do subject do klass = Class.new do include RSpec::Puppet::Support end klass.new end describe '#setup_puppet' do it 'updates the ruby $LOAD_PATH based on the current modulepath' do basedir = '/mymodulepath' RSpec.configuration.module_path = basedir dira = File.join(basedir, 'a', 'lib') dirb = File.join(basedir, 'b', 'lib') allow(Dir).to receive(:[]).with("#{basedir}/*/lib").and_return([dira, dirb]) subject.setup_puppet expect($LOAD_PATH).to include(dira) expect($LOAD_PATH).to include(dirb) end context 'when running on puppet 3.5 or later', :if => Puppet.version.to_f >= 3.5 do it 'sets Puppet[:strict_variables] to false by default' do subject.setup_puppet expect(Puppet[:strict_variables]).to eq(false) end it 'reads the :strict_variables setting' do allow(subject).to receive(:strict_variables).and_return(true) subject.setup_puppet expect(Puppet[:strict_variables]).to eq(true) end end context 'when running on puppet 3.x, with x >= 5', :if => Puppet.version.to_f >= 3.5 && Puppet.version.to_f < 4.0 do it 'sets Puppet[:trusted_node_data] to false by default' do subject.setup_puppet expect(Puppet[:trusted_node_data]).to eq(false) end it 'reads the :trusted_node_data setting' do allow(subject).to receive(:trusted_node_data).and_return(true) subject.setup_puppet expect(Puppet[:trusted_node_data]).to eq(true) end end context 'when running on puppet 3', :if => Puppet.version.to_f >= 3.0 && Puppet.version.to_f < 4.0 do it 'sets Puppet[:parser] to "current" by default' do subject.setup_puppet expect(Puppet[:parser]).to eq("current") end it 'reads the :parser setting' do allow(subject).to receive(:parser).and_return("future") subject.setup_puppet expect(Puppet[:parser]).to eq("future") end it 'sets Puppet[:stringify_facts] to true by default' do subject.setup_puppet expect(Puppet[:stringify_facts]).to eq(true) end it 'reads the :stringify_facts setting' do allow(subject).to receive(:stringify_facts).and_return(false) subject.setup_puppet expect(Puppet[:stringify_facts]).to eq(false) end it 'sets Puppet[:ordering] to title-hash by default' do subject.setup_puppet expect(Puppet[:ordering]).to eq('title-hash') end it 'reads the :ordering setting' do allow(subject).to receive(:ordering).and_return('manifest') subject.setup_puppet expect(Puppet[:ordering]).to eq('manifest') end end context 'when running on puppet 4', :if => Puppet.version.to_f >= 4.0 do it 'sets Puppet[:strict_variables] to false by default' do subject.setup_puppet expect(Puppet[:strict_variables]).to eq(false) end it 'reads the :strict_variables setting' do allow(subject).to receive(:strict_variables).and_return(true) subject.setup_puppet expect(Puppet[:strict_variables]).to eq(true) end end end end rspec-puppet-2.2.0/spec/unit/000077500000000000000000000000001253163612600160605ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/unit/matchers/000077500000000000000000000000001253163612600176665ustar00rootroot00000000000000rspec-puppet-2.2.0/spec/unit/matchers/parameter_matcher_spec.rb000066400000000000000000000055671253163612600247250ustar00rootroot00000000000000require 'spec_helper' describe RSpec::Puppet::ManifestMatchers::ParameterMatcher do describe '#matches?' do context 'with [1] expected' do subject do described_class.new(:foo_parameter, [1], :should) end it 'matches [1]' do expect(subject.matches?(:foo_parameter => [1])).to be(true) end it 'does not match []' do expect(subject.matches?(:foo_parameter => [])).to be(false) end it 'does not match [1,2,3]' do expect(subject.matches?(:foo_parameter => [1,2,3])).to be(false) end it 'does not match nil' do expect(subject.matches?(:foo_parameter => nil)).to be(false) end end context 'with [1,2,3] expected' do subject do described_class.new(:foo_parameter, [1,2,3], :should) end it 'matches [1,2,3]' do expect(subject.matches?(:foo_parameter => [1,2,3])).to be(true) end it 'does not match []' do expect(subject.matches?(:foo_parameter => [])).to be(false) end it 'does not match nil' do expect(subject.matches?(:foo_parameter => nil)).to be(false) end end context 'with {"foo" => "bar"} expected' do subject do described_class.new(:foo_parameter, {"foo" => "bar"}, :should) end it 'matches {"foo" => "bar"}' do expect(subject.matches?(:foo_parameter => {"foo" => "bar"})).to be(true) end it 'does not match nil' do expect(subject.matches?(:foo_parameter => nil)).to be(false) end it 'does not match {}' do expect(subject.matches?(:foo_parameter => {})).to be(false) end it 'does not match {"foo" => "baz"}' do expect(subject.matches?(:foo_parameter => {"foo" => "baz"})).to be(false) end end context 'with lambda(){"foo"} expected' do subject do block = lambda {|actual| actual == "foo" } described_class.new(:foo_parameter, block, :should) end it 'matches "foo"' do expect(subject.matches?(:foo_parameter => "foo")).to be(true) end it 'does not match nil' do expect(subject.matches?(:foo_parameter => nil)).to be(false) end end context 'with /foo/ expected' do subject do described_class.new(:foo_parameter, /foo/, :should) end it 'matches "foo"' do expect(subject.matches?(:foo_parameter => "foo")).to be(true) end it 'does not match nil' do expect(subject.matches?(:foo_parameter => nil)).to be(false) end end context 'with "foo" expected' do subject do described_class.new(:foo_parameter, "foo", :should) end it 'matches "foo"' do expect(subject.matches?(:foo_parameter => "foo")).to be(true) end it 'does not match nil' do expect(subject.matches?(:foo_parameter => nil)).to be(false) end end end end rspec-puppet-2.2.0/spec/unit/matchers/run_spec.rb000066400000000000000000000077471253163612600220500ustar00rootroot00000000000000require 'spec_helper' describe RSpec::Puppet::FunctionMatchers::Run do it 'should call the lambda with no params' do received_params = nil if Puppet.version.to_f >= 4.0 subject.matches?(lambda { |env, *params| received_params = params }) expect(received_params).to eq([]) else subject.matches?(lambda { |params| received_params = params }) expect(received_params).to be_nil end end it 'should not match a lambda that raises an error' do expect(subject.matches?(lambda { |env, *params| raise StandardError, 'Forced Error' })).to be_false end [ [], [true], [false], [''], ['string'], [nil], [0], [1.1], [[]], ['one', 'two'], [{}], [{ 'key' => 'value' }], [:undef] ].each do |supplied_params| context "with_params(#{supplied_params.collect { |p| p.inspect }.join(', ')})" do before(:each) { subject.with_params(*supplied_params) } it 'should call the lambda with the supplied params' do received_params = nil if Puppet.version.to_f >= 4.0 subject.matches?(lambda { |env, *params| received_params = params }) else subject.matches?(lambda { |params| received_params = params }) end expect(received_params).to eq(supplied_params) end it 'should not match a lambda that raises an error' do expect(subject.matches?(lambda { |env, *params| raise StandardError, 'Forced Error' })).to be_false end [ true, false, '', 'string', nil, 0, 1.1, [], {}, :undef ].each do |expected_return| context "and_return(#{expected_return.inspect})" do before(:each) { subject.and_return(expected_return) } it 'should match a lambda that does return the requested value' do expect(subject.matches?(lambda { |env, *params| expected_return })).to be_true end it 'should not match a lambda that does return a different value' do expect(subject.matches?(lambda { |env, *params| !expected_return })).to be_false end it 'should not match a lambda that raises an error' do expect(subject.matches?(lambda { |env, *params| raise StandardError, 'Forced Error' })).to be_false end end end context "and_raise_error(ArgumentError)" do before(:each) { subject.and_raise_error(ArgumentError) } it 'should match a lambda that raises ArgumentError' do expect(subject.matches?(lambda { |env, *params| raise ArgumentError, 'Forced Error' })).to be_true end [ true, false, '', 'string', nil, 0, 1.1, [], {}, :undef ].each do |value| it "should not match a lambda that returns #{value.inspect}" do expect(subject.matches?(lambda { |env, *params| value })).to be_false end end it 'should not match a lambda that raises a different error' do expect(subject.matches?(lambda { |env, *params| raise StandardError, 'Forced Error' })).to be_false end end context "and_raise_error(ArgumentError, /message/)" do before(:each) { subject.and_raise_error(ArgumentError, /message/) } it 'should match a lambda that raises ArgumentError("with matching message")' do expect(subject.matches?(lambda { |env, *params| raise ArgumentError, 'with matching message' })).to be_true end it 'should not match a lambda that raises a different ArgumentError' do expect(subject.matches?(lambda { |env, *params| raise ArgumentError, 'Forced Error' })).to be_false end [ true, false, '', 'string', nil, 0, 1.1, [], {}, :undef ].each do |value| it "should not match a lambda that returns #{value.inspect}" do expect(subject.matches?(lambda { |env, *params| value })).to be_false end end it 'should not match a lambda that raises a different error' do expect(subject.matches?(lambda { |env, *params| raise StandardError, 'Forced Error' })).to be_false end end end end end