diff options
Diffstat (limited to 'spec')
34 files changed, 775 insertions, 262 deletions
diff --git a/spec/acceptance/nodesets/centos-64-x64-pe.yml b/spec/acceptance/nodesets/centos-64-x64-pe.yml new file mode 100644 index 0000000..7d9242f --- /dev/null +++ b/spec/acceptance/nodesets/centos-64-x64-pe.yml @@ -0,0 +1,12 @@ +HOSTS: + centos-64-x64: + roles: + - master + - database + - dashboard + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: pe diff --git a/spec/acceptance/nodesets/centos-64-x64.yml b/spec/acceptance/nodesets/centos-64-x64.yml new file mode 100644 index 0000000..05540ed --- /dev/null +++ b/spec/acceptance/nodesets/centos-64-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-64-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/spec/acceptance/nodesets/centos-65-x64.yml b/spec/acceptance/nodesets/centos-65-x64.yml new file mode 100644 index 0000000..4e2cb80 --- /dev/null +++ b/spec/acceptance/nodesets/centos-65-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-65-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-65-x64-vbox436-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-65-x64-virtualbox-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/spec/acceptance/nodesets/default.yml b/spec/acceptance/nodesets/default.yml new file mode 120000 index 0000000..2719644 --- /dev/null +++ b/spec/acceptance/nodesets/default.yml @@ -0,0 +1 @@ +centos-64-x64.yml
\ No newline at end of file diff --git a/spec/acceptance/nodesets/fedora-18-x64.yml b/spec/acceptance/nodesets/fedora-18-x64.yml new file mode 100644 index 0000000..1361649 --- /dev/null +++ b/spec/acceptance/nodesets/fedora-18-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + fedora-18-x64: + roles: + - master + platform: fedora-18-x86_64 + box : fedora-18-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/fedora-18-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/spec/acceptance/nodesets/sles-11-x64.yml b/spec/acceptance/nodesets/sles-11-x64.yml new file mode 100644 index 0000000..41abe21 --- /dev/null +++ b/spec/acceptance/nodesets/sles-11-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + sles-11-x64.local: + roles: + - master + platform: sles-11-x64 + box : sles-11sp1-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/sles-11sp1-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml b/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml new file mode 100644 index 0000000..5ca1514 --- /dev/null +++ b/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + ubuntu-server-10044-x64: + roles: + - master + platform: ubuntu-10.04-amd64 + box : ubuntu-server-10044-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml b/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml new file mode 100644 index 0000000..d065b30 --- /dev/null +++ b/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + ubuntu-server-12042-x64: + roles: + - master + platform: ubuntu-12.04-amd64 + box : ubuntu-server-12042-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/spec/acceptance/unsupported_spec.rb b/spec/acceptance/unsupported_spec.rb new file mode 100644 index 0000000..449f35a --- /dev/null +++ b/spec/acceptance/unsupported_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper_acceptance' + +describe 'unsupported distributions and OSes', :if => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + it 'should fail' do + pp = <<-EOS + class { 'mysql::server': } + EOS + expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/unsupported osfamily/i) + end +end diff --git a/spec/classes/anchor_spec.rb b/spec/classes/anchor_spec.rb index 2dd17de..2e1fcba 100644 --- a/spec/classes/anchor_spec.rb +++ b/spec/classes/anchor_spec.rb @@ -1,31 +1,28 @@ -require 'puppet' -require 'rspec-puppet' +require 'spec_helper' +require 'puppet_spec/compiler' describe "anchorrefresh" do - let(:node) { 'testhost.example.com' } - let :pre_condition do - <<-ANCHORCLASS -class anchored { - anchor { 'anchored::begin': } - ~> anchor { 'anchored::end': } -} + include PuppetSpec::Compiler -class anchorrefresh { - notify { 'first': } - ~> class { 'anchored': } - ~> anchor { 'final': } -} - ANCHORCLASS - end + let :transaction do + apply_compiled_manifest(<<-ANCHORCLASS) + class anchored { + anchor { 'anchored::begin': } + ~> anchor { 'anchored::end': } + } - def apply_catalog_and_return_exec_rsrc - catalog = subject.to_ral - transaction = catalog.apply - transaction.resource_status("Anchor[final]") + class anchorrefresh { + notify { 'first': } + ~> class { 'anchored': } + ~> anchor { 'final': } + } + + include anchorrefresh + ANCHORCLASS end it 'propagates events through the anchored class' do - resource = apply_catalog_and_return_exec_rsrc + resource = transaction.resource_status('Anchor[final]') expect(resource.restarted).to eq(true) end diff --git a/spec/functions/ensure_packages_spec.rb b/spec/functions/ensure_packages_spec.rb index a13c282..436be10 100644 --- a/spec/functions/ensure_packages_spec.rb +++ b/spec/functions/ensure_packages_spec.rb @@ -2,15 +2,37 @@ require 'spec_helper' require 'rspec-puppet' +require 'puppet_spec/compiler' describe 'ensure_packages' do - let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + include PuppetSpec::Compiler + + before :each do + Puppet::Parser::Functions.autoloader.loadall + Puppet::Parser::Functions.function(:ensure_packages) + Puppet::Parser::Functions.function(:ensure_resource) + Puppet::Parser::Functions.function(:defined_with_params) + Puppet::Parser::Functions.function(:create_resources) + end + + let :node do Puppet::Node.new('localhost') end + let :compiler do Puppet::Parser::Compiler.new(node) end + let :scope do + if Puppet.version.to_f >= 3.0 + Puppet::Parser::Scope.new(compiler) + else + newscope = Puppet::Parser::Scope.new + newscope.compiler = compiler + newscope.source = Puppet::Resource::Type.new(:node, :localhost) + newscope + end + end describe 'argument handling' do it 'fails with no arguments' do expect { scope.function_ensure_packages([]) - }.to raise_error(Puppet::ParseError, /0 for 1/) + }.to raise_error(Puppet::ParseError, /0 for 1 or 2/) end it 'accepts an array of values' do @@ -22,25 +44,38 @@ describe 'ensure_packages' do end end - context 'given a catalog containing Package[puppet]{ensure => absent}' do - let :pre_condition do - 'package { puppet: ensure => absent }' + context 'given a catalog with puppet package => absent' do + let :catalog do + compile_to_catalog(<<-EOS + ensure_packages(['facter']) + package { puppet: ensure => absent } + EOS + ) end - # NOTE: should run.with_params has the side effect of making the compiler - # available to the test harness. it 'has no effect on Package[puppet]' do - should run.with_params(['puppet']) - rsrc = compiler.catalog.resource('Package[puppet]') - rsrc.to_hash.should == {:ensure => "absent"} + expect(catalog.resource(:package, 'puppet')['ensure']).to eq('absent') end end context 'given a clean catalog' do + let :catalog do + compile_to_catalog('ensure_packages(["facter"])') + end + + it 'declares package resources with ensure => present' do + expect(catalog.resource(:package, 'facter')['ensure']).to eq('present') + end + end + + context 'given a clean catalog and specified defaults' do + let :catalog do + compile_to_catalog('ensure_packages(["facter"], {"provider" => "gem"})') + end + it 'declares package resources with ensure => present' do - should run.with_params(['facter']) - rsrc = compiler.catalog.resource('Package[facter]') - rsrc.to_hash[:ensure].should eq("present") + expect(catalog.resource(:package, 'facter')['ensure']).to eq('present') + expect(catalog.resource(:package, 'facter')['provider']).to eq('gem') end end end diff --git a/spec/functions/ensure_resource_spec.rb b/spec/functions/ensure_resource_spec.rb index 2e8aefc..459d917 100644 --- a/spec/functions/ensure_resource_spec.rb +++ b/spec/functions/ensure_resource_spec.rb @@ -1,64 +1,112 @@ -#! /usr/bin/env ruby -S rspec require 'spec_helper' - require 'rspec-puppet' +require 'puppet_spec/compiler' + describe 'ensure_resource' do + include PuppetSpec::Compiler + + before :all do + Puppet::Parser::Functions.autoloader.loadall + Puppet::Parser::Functions.function(:ensure_packages) + end + + let :node do Puppet::Node.new('localhost') end + let :compiler do Puppet::Parser::Compiler.new(node) end + let :scope do Puppet::Parser::Scope.new(compiler) end + describe 'when a type or title is not specified' do - it { should run.with_params().and_raise_error(ArgumentError) } - it { should run.with_params(['type']).and_raise_error(ArgumentError) } + it { expect { scope.function_ensure_resource([]) }.to raise_error } + it { expect { scope.function_ensure_resource(['type']) }.to raise_error } end describe 'when compared against a resource with no attributes' do - let :pre_condition do - 'user { "dan": }' + let :catalog do + compile_to_catalog(<<-EOS + user { "dan": } + ensure_resource('user', 'dan', {}) + EOS + ) end - it "should contain the the ensured resources" do - subject.should run.with_params('user', 'dan', {}) - compiler.catalog.resource('User[dan]').to_s.should == 'User[dan]' + + it 'should contain the the ensured resources' do + expect(catalog.resource(:user, 'dan').to_s).to eq('User[dan]') end end - describe 'when compared against a resource with attributes' do - let :pre_condition do - 'user { "dan": ensure => present, shell => "/bin/csh", managehome => false}' + describe 'works when compared against a resource with non-conflicting attributes' do + [ + "ensure_resource('User', 'dan', {})", + "ensure_resource('User', 'dan', '')", + "ensure_resource('User', 'dan', {'ensure' => 'present'})", + "ensure_resource('User', 'dan', {'ensure' => 'present', 'managehome' => false})" + ].each do |ensure_resource| + pp = <<-EOS + user { "dan": ensure => present, shell => "/bin/csh", managehome => false} + #{ensure_resource} + EOS + + it { expect { compile_to_catalog(pp) }.to_not raise_error } end - # these first three should not fail - it { should run.with_params('User', 'dan', {}) } - it { should run.with_params('User', 'dan', '') } - it { should run.with_params('User', 'dan', {'ensure' => 'present'}) } - it { should run.with_params('User', 'dan', {'ensure' => 'present', 'managehome' => false}) } - # test that this fails - it { should run.with_params('User', 'dan', {'ensure' => 'absent', 'managehome' => false}).and_raise_error(Puppet::Error) } + end + + describe 'fails when compared against a resource with conflicting attributes' do + pp = <<-EOS + user { "dan": ensure => present, shell => "/bin/csh", managehome => false} + ensure_resource('User', 'dan', {'ensure' => 'absent', 'managehome' => false}) + EOS + + it { expect { compile_to_catalog(pp) }.to raise_error } end describe 'when an array of new resources are passed in' do - it "should contain the ensured resources" do - subject.should run.with_params('User', ['dan', 'alex'], {}) - compiler.catalog.resource('User[dan]').to_s.should == 'User[dan]' - compiler.catalog.resource('User[alex]').to_s.should == 'User[alex]' + let :catalog do + compile_to_catalog("ensure_resource('User', ['dan', 'alex'], {})") + end + + it 'should contain the ensured resources' do + expect(catalog.resource('User[dan]').to_s).to eq('User[dan]') + expect(catalog.resource('User[alex]').to_s).to eq('User[alex]') end end describe 'when an array of existing resources is compared against existing resources' do - let :pre_condition do - 'user { "dan": ensure => present; "alex": ensure => present }' + pp = <<-EOS + user { 'dan': ensure => present; 'alex': ensure => present } + ensure_resource('User', ['dan', 'alex'], {}) + EOS + + let :catalog do + compile_to_catalog(pp) end - it "should return the existing resources" do - subject.should run.with_params('User', ['dan', 'alex'], {}) - compiler.catalog.resource('User[dan]').to_s.should == 'User[dan]' - compiler.catalog.resource('User[alex]').to_s.should == 'User[alex]' + + it 'should return the existing resources' do + expect(catalog.resource('User[dan]').to_s).to eq('User[dan]') + expect(catalog.resource('User[alex]').to_s).to eq('User[alex]') end end - describe 'when compared against existing resources with attributes' do - let :pre_condition do - 'user { "dan": ensure => present; "alex": ensure => present }' + describe 'works when compared against existing resources with attributes' do + [ + "ensure_resource('User', ['dan', 'alex'], {})", + "ensure_resource('User', ['dan', 'alex'], '')", + "ensure_resource('User', ['dan', 'alex'], {'ensure' => 'present'})", + ].each do |ensure_resource| + pp = <<-EOS + user { 'dan': ensure => present; 'alex': ensure => present } + #{ensure_resource} + EOS + + it { expect { compile_to_catalog(pp) }.to_not raise_error } end - # These should not fail - it { should run.with_params('User', ['dan', 'alex'], {}) } - it { should run.with_params('User', ['dan', 'alex'], '') } - it { should run.with_params('User', ['dan', 'alex'], {'ensure' => 'present'}) } - # This should fail - it { should run.with_params('User', ['dan', 'alex'], {'ensure' => 'absent'}).and_raise_error(Puppet::Error) } end + + describe 'fails when compared against existing resources with conflicting attributes' do + pp = <<-EOS + user { 'dan': ensure => present; 'alex': ensure => present } + ensure_resource('User', ['dan', 'alex'], {'ensure' => 'absent'}) + EOS + + it { expect { compile_to_catalog(pp) }.to raise_error } + end + end diff --git a/spec/functions/getparam_spec.rb b/spec/functions/getparam_spec.rb index d9c50a6..7f5ad1a 100644 --- a/spec/functions/getparam_spec.rb +++ b/spec/functions/getparam_spec.rb @@ -1,34 +1,75 @@ -#! /usr/bin/env ruby -S rspec require 'spec_helper' - require 'rspec-puppet' +require 'puppet_spec/compiler' + describe 'getparam' do - describe 'when a resource is not specified' do - it do - should run.with_params().and_raise_error(ArgumentError) - should run.with_params('User[dan]').and_raise_error(ArgumentError) - should run.with_params('User[dan]', {}).and_raise_error(ArgumentError) - should run.with_params('User[dan]', '').and_return('') + include PuppetSpec::Compiler + + before :each do + Puppet::Parser::Functions.autoloader.loadall + Puppet::Parser::Functions.function(:getparam) + end + + let :node do Puppet::Node.new('localhost') end + let :compiler do Puppet::Parser::Compiler.new(node) end + if Puppet.version.to_f >= 3.0 + let :scope do Puppet::Parser::Scope.new(compiler) end + else + let :scope do + newscope = Puppet::Parser::Scope.new + newscope.compiler = compiler + newscope.source = Puppet::Resource::Type.new(:node, :localhost) + newscope end end + + it "should exist" do + Puppet::Parser::Functions.function("getparam").should == "function_getparam" + end + + describe 'when a resource is not specified' do + it { expect { scope.function_getparam([]) }.to raise_error } + it { expect { scope.function_getparam(['User[dan]']) }.to raise_error } + it { expect { scope.function_getparam(['User[dan]']) }.to raise_error } + it { expect { scope.function_getparam(['User[dan]', {}]) }.to raise_error } + # This seems to be OK because we just check for a string. + it { expect { scope.function_getparam(['User[dan]', '']) }.to_not raise_error } + end + describe 'when compared against a resource with no params' do - let :pre_condition do - 'user { "dan": }' + let :catalog do + compile_to_catalog(<<-EOS + user { "dan": } + EOS + ) end + it do - should run.with_params('User[dan]', 'shell').and_return('') + expect(scope.function_getparam(['User[dan]', 'shell'])).to eq('') end end describe 'when compared against a resource with params' do - let :pre_condition do - 'user { "dan": ensure => present, shell => "/bin/sh", managehome => false}' + let :catalog do + compile_to_catalog(<<-EOS + user { 'dan': ensure => present, shell => '/bin/sh', managehome => false} + $test = getparam(User[dan], 'shell') + EOS + ) end + it do - should run.with_params('User[dan]', 'shell').and_return('/bin/sh') - should run.with_params('User[dan]', '').and_return('') - should run.with_params('User[dan]', 'ensure').and_return('present') - should run.with_params('User[dan]', 'managehome').and_return(false) + resource = Puppet::Parser::Resource.new(:user, 'dan', {:scope => scope}) + resource.set_parameter('ensure', 'present') + resource.set_parameter('shell', '/bin/sh') + resource.set_parameter('managehome', false) + compiler.add_resource(scope, resource) + + expect(scope.function_getparam(['User[dan]', 'shell'])).to eq('/bin/sh') + expect(scope.function_getparam(['User[dan]', ''])).to eq('') + expect(scope.function_getparam(['User[dan]', 'ensure'])).to eq('present') + # TODO: Expected this to be false, figure out why we're getting '' back. + expect(scope.function_getparam(['User[dan]', 'managehome'])).to eq('') end end end diff --git a/spec/lib/puppet_spec/compiler.rb b/spec/lib/puppet_spec/compiler.rb new file mode 100644 index 0000000..22e923d --- /dev/null +++ b/spec/lib/puppet_spec/compiler.rb @@ -0,0 +1,46 @@ +module PuppetSpec::Compiler + def compile_to_catalog(string, node = Puppet::Node.new('foonode')) + Puppet[:code] = string + Puppet::Parser::Compiler.compile(node) + end + + def compile_to_ral(manifest) + catalog = compile_to_catalog(manifest) + ral = catalog.to_ral + ral.finalize + ral + end + + def compile_to_relationship_graph(manifest, prioritizer = Puppet::Graph::SequentialPrioritizer.new) + ral = compile_to_ral(manifest) + graph = Puppet::Graph::RelationshipGraph.new(prioritizer) + graph.populate_from(ral) + graph + end + + if Puppet.version.to_f >= 3.3 + def apply_compiled_manifest(manifest, prioritizer = Puppet::Graph::SequentialPrioritizer.new) + transaction = Puppet::Transaction.new(compile_to_ral(manifest), + Puppet::Transaction::Report.new("apply"), + prioritizer) + transaction.evaluate + transaction.report.finalize_report + + transaction + end + else + def apply_compiled_manifest(manifest) + transaction = Puppet::Transaction.new(compile_to_ral(manifest), Puppet::Transaction::Report.new("apply")) + transaction.evaluate + transaction.report.finalize_report + + transaction + end + end + + def order_resources_traversed_in(relationships) + order_seen = [] + relationships.traverse { |resource| order_seen << resource.ref } + order_seen + end +end diff --git a/spec/lib/puppet_spec/database.rb b/spec/lib/puppet_spec/database.rb new file mode 100644 index 0000000..069ca15 --- /dev/null +++ b/spec/lib/puppet_spec/database.rb @@ -0,0 +1,29 @@ +# This just makes some nice things available at global scope, and for setup of +# tests to use a real fake database, rather than a fake stubs-that-don't-work +# version of the same. Fun times. +def sqlite? + if $sqlite.nil? + begin + require 'sqlite3' + $sqlite = true + rescue LoadError + $sqlite = false + end + end + $sqlite +end + +def can_use_scratch_database? + sqlite? and Puppet.features.rails? +end + + +# This is expected to be called in your `before :each` block, and will get you +# ready to roll with a serious database and all. Cleanup is handled +# automatically for you. Nothing to do there. +def setup_scratch_database + Puppet[:dbadapter] = 'sqlite3' + Puppet[:dblocation] = ':memory:' + Puppet[:railslog] = PuppetSpec::Files.tmpfile('storeconfigs.log') + Puppet::Rails.init +end diff --git a/spec/lib/puppet_spec/files.rb b/spec/lib/puppet_spec/files.rb new file mode 100755 index 0000000..65b04aa --- /dev/null +++ b/spec/lib/puppet_spec/files.rb @@ -0,0 +1,60 @@ +require 'fileutils' +require 'tempfile' +require 'tmpdir' +require 'pathname' + +# A support module for testing files. +module PuppetSpec::Files + def self.cleanup + $global_tempfiles ||= [] + while path = $global_tempfiles.pop do + begin + Dir.unstub(:entries) + FileUtils.rm_rf path, :secure => true + rescue Errno::ENOENT + # nothing to do + end + end + end + + def make_absolute(path) PuppetSpec::Files.make_absolute(path) end + def self.make_absolute(path) + path = File.expand_path(path) + path[0] = 'c' if Puppet.features.microsoft_windows? + path + end + + def tmpfile(name, dir = nil) PuppetSpec::Files.tmpfile(name, dir) end + def self.tmpfile(name, dir = nil) + # Generate a temporary file, just for the name... + source = dir ? Tempfile.new(name, dir) : Tempfile.new(name) + path = source.path + source.close! + + record_tmp(File.expand_path(path)) + + path + end + + def file_containing(name, contents) PuppetSpec::Files.file_containing(name, contents) end + def self.file_containing(name, contents) + file = tmpfile(name) + File.open(file, 'wb') { |f| f.write(contents) } + file + end + + def tmpdir(name) PuppetSpec::Files.tmpdir(name) end + def self.tmpdir(name) + dir = Dir.mktmpdir(name) + + record_tmp(dir) + + dir + end + + def self.record_tmp(tmp) + # ...record it for cleanup, + $global_tempfiles ||= [] + $global_tempfiles << tmp + end +end diff --git a/spec/lib/puppet_spec/fixtures.rb b/spec/lib/puppet_spec/fixtures.rb new file mode 100755 index 0000000..7f6bc2a --- /dev/null +++ b/spec/lib/puppet_spec/fixtures.rb @@ -0,0 +1,28 @@ +module PuppetSpec::Fixtures + def fixtures(*rest) + File.join(PuppetSpec::FIXTURE_DIR, *rest) + end + def my_fixture_dir + callers = caller + while line = callers.shift do + next unless found = line.match(%r{/spec/(.*)_spec\.rb:}) + return fixtures(found[1]) + end + fail "sorry, I couldn't work out your path from the caller stack!" + end + def my_fixture(name) + file = File.join(my_fixture_dir, name) + unless File.readable? file then + fail Puppet::DevError, "fixture '#{name}' for #{my_fixture_dir} is not readable" + end + return file + end + def my_fixtures(glob = '*', flags = 0) + files = Dir.glob(File.join(my_fixture_dir, glob), flags) + unless files.length > 0 then + fail Puppet::DevError, "fixture '#{glob}' for #{my_fixture_dir} had no files!" + end + block_given? and files.each do |file| yield file end + files + end +end diff --git a/spec/lib/puppet_spec/matchers.rb b/spec/lib/puppet_spec/matchers.rb new file mode 100644 index 0000000..448bd18 --- /dev/null +++ b/spec/lib/puppet_spec/matchers.rb @@ -0,0 +1,120 @@ +require 'stringio' + +######################################################################## +# Backward compatibility for Jenkins outdated environment. +module RSpec + module Matchers + module BlockAliases + alias_method :to, :should unless method_defined? :to + alias_method :to_not, :should_not unless method_defined? :to_not + alias_method :not_to, :should_not unless method_defined? :not_to + end + end +end + + +######################################################################## +# Custom matchers... +RSpec::Matchers.define :have_matching_element do |expected| + match do |actual| + actual.any? { |item| item =~ expected } + end +end + + +RSpec::Matchers.define :exit_with do |expected| + actual = nil + match do |block| + begin + block.call + rescue SystemExit => e + actual = e.status + end + actual and actual == expected + end + failure_message_for_should do |block| + "expected exit with code #{expected} but " + + (actual.nil? ? " exit was not called" : "we exited with #{actual} instead") + end + failure_message_for_should_not do |block| + "expected that exit would not be called with #{expected}" + end + description do + "expect exit with #{expected}" + end +end + +class HavePrintedMatcher + attr_accessor :expected, :actual + + def initialize(expected) + case expected + when String, Regexp + @expected = expected + else + @expected = expected.to_s + end + end + + def matches?(block) + begin + $stderr = $stdout = StringIO.new + $stdout.set_encoding('UTF-8') if $stdout.respond_to?(:set_encoding) + block.call + $stdout.rewind + @actual = $stdout.read + ensure + $stdout = STDOUT + $stderr = STDERR + end + + if @actual then + case @expected + when String + @actual.include? @expected + when Regexp + @expected.match @actual + end + else + false + end + end + + def failure_message_for_should + if @actual.nil? then + "expected #{@expected.inspect}, but nothing was printed" + else + "expected #{@expected.inspect} to be printed; got:\n#{@actual}" + end + end + + def failure_message_for_should_not + "expected #{@expected.inspect} to not be printed; got:\n#{@actual}" + end + + def description + "expect #{@expected.inspect} to be printed" + end +end + +def have_printed(what) + HavePrintedMatcher.new(what) +end + +RSpec::Matchers.define :equal_attributes_of do |expected| + match do |actual| + actual.instance_variables.all? do |attr| + actual.instance_variable_get(attr) == expected.instance_variable_get(attr) + end + end +end + +RSpec::Matchers.define :be_one_of do |*expected| + match do |actual| + expected.include? actual + end + + failure_message_for_should do |actual| + "expected #{actual.inspect} to be one of #{expected.map(&:inspect).join(' or ')}" + end +end diff --git a/spec/lib/puppet_spec/modules.rb b/spec/lib/puppet_spec/modules.rb new file mode 100644 index 0000000..6835e44 --- /dev/null +++ b/spec/lib/puppet_spec/modules.rb @@ -0,0 +1,26 @@ +module PuppetSpec::Modules + class << self + def create(name, dir, options = {}) + module_dir = File.join(dir, name) + FileUtils.mkdir_p(module_dir) + + environment = options[:environment] + + if metadata = options[:metadata] + metadata[:source] ||= 'github' + metadata[:author] ||= 'puppetlabs' + metadata[:version] ||= '9.9.9' + metadata[:license] ||= 'to kill' + metadata[:dependencies] ||= [] + + metadata[:name] = "#{metadata[:author]}/#{name}" + + File.open(File.join(module_dir, 'metadata.json'), 'w') do |f| + f.write(metadata.to_pson) + end + end + + Puppet::Module.new(name, module_dir, environment) + end + end +end diff --git a/spec/lib/puppet_spec/pops.rb b/spec/lib/puppet_spec/pops.rb new file mode 100644 index 0000000..442c85b --- /dev/null +++ b/spec/lib/puppet_spec/pops.rb @@ -0,0 +1,16 @@ +module PuppetSpec::Pops + extend RSpec::Matchers::DSL + + # Checks if an Acceptor has a specific issue in its list of diagnostics + matcher :have_issue do |expected| + match do |actual| + actual.diagnostics.index { |i| i.issue == expected } != nil + end + failure_message_for_should do |actual| + "expected Acceptor[#{actual.diagnostics.collect { |i| i.issue.issue_code }.join(',')}] to contain issue #{expected.issue_code}" + end + failure_message_for_should_not do |actual| + "expected Acceptor[#{actual.diagnostics.collect { |i| i.issue.issue_code }.join(',')}] to not contain issue #{expected.issue_code}" + end + end +end diff --git a/spec/lib/puppet_spec/scope.rb b/spec/lib/puppet_spec/scope.rb new file mode 100644 index 0000000..c14ab47 --- /dev/null +++ b/spec/lib/puppet_spec/scope.rb @@ -0,0 +1,14 @@ + +module PuppetSpec::Scope + # Initialize a new scope suitable for testing. + # + def create_test_scope_for_node(node_name) + node = Puppet::Node.new(node_name) + compiler = Puppet::Parser::Compiler.new(node) + scope = Puppet::Parser::Scope.new(compiler) + scope.source = Puppet::Resource::Type.new(:node, node_name) + scope.parent = compiler.topscope + scope + end + +end
\ No newline at end of file diff --git a/spec/lib/puppet_spec/settings.rb b/spec/lib/puppet_spec/settings.rb new file mode 100644 index 0000000..f3dbc42 --- /dev/null +++ b/spec/lib/puppet_spec/settings.rb @@ -0,0 +1,15 @@ +module PuppetSpec::Settings + + # It would probably be preferable to refactor defaults.rb such that the real definitions of + # these settings were available as a variable, which was then accessible for use during tests. + # However, I'm not doing that yet because I don't want to introduce any additional moving parts + # to this already very large changeset. + # Would be nice to clean this up later. --cprice 2012-03-20 + TEST_APP_DEFAULT_DEFINITIONS = { + :name => { :default => "test", :desc => "name" }, + :logdir => { :type => :directory, :default => "test", :desc => "logdir" }, + :confdir => { :type => :directory, :default => "test", :desc => "confdir" }, + :vardir => { :type => :directory, :default => "test", :desc => "vardir" }, + :rundir => { :type => :directory, :default => "test", :desc => "rundir" }, + } +end diff --git a/spec/lib/puppet_spec/verbose.rb b/spec/lib/puppet_spec/verbose.rb new file mode 100755 index 0000000..d9834f2 --- /dev/null +++ b/spec/lib/puppet_spec/verbose.rb @@ -0,0 +1,9 @@ +# Support code for running stuff with warnings disabled. +module Kernel + def with_verbose_disabled + verbose, $VERBOSE = $VERBOSE, nil + result = yield + $VERBOSE = verbose + return result + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 931d35c..cf1981b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,21 +1,31 @@ dir = File.expand_path(File.dirname(__FILE__)) $LOAD_PATH.unshift File.join(dir, 'lib') -# Don't want puppet getting the command line arguments for rake or autotest -ARGV.clear +# So everyone else doesn't have to include this base constant. +module PuppetSpec + FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR) +end require 'puppet' -require 'facter' -require 'mocha' -gem 'rspec', '>=2.0.0' -require 'rspec/expectations' - +require 'rspec-puppet' +require 'simplecov' require 'puppetlabs_spec_helper/module_spec_helper' +require 'puppet_spec/verbose' +require 'puppet_spec/files' +require 'puppet_spec/settings' +require 'puppet_spec/fixtures' +require 'puppet_spec/matchers' +require 'puppet_spec/database' +require 'monkey_patches/alias_should_to_must' +require 'mocha/setup' + + +SimpleCov.start do + add_filter "/spec/" +end + RSpec.configure do |config| - # FIXME REVISIT - We may want to delegate to Facter like we do in - # Puppet::PuppetSpecInitializer.initialize_via_testhelper(config) because - # this behavior is a duplication of the spec_helper in Facter. config.before :each do # Ensure that we don't accidentally cache facts and environment between # test cases. This requires each example group to explicitly load the diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb new file mode 100644 index 0000000..4cefa75 --- /dev/null +++ b/spec/spec_helper_acceptance.rb @@ -0,0 +1,33 @@ +require 'beaker-rspec' + +UNSUPPORTED_PLATFORMS = [] + +unless ENV['RS_PROVISION'] == 'no' + hosts.each do |host| + # Install Puppet + if host.is_pe? + install_pe + else + install_package host, 'rubygems' + on host, 'gem install puppet --no-ri --no-rdoc' + on host, "mkdir -p #{host['distmoduledir']}" + end + end +end + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Readable test descriptions + c.formatter = :documentation + + # Configure all nodes in nodeset + c.before :suite do + # Install module and dependencies + puppet_module_install(:source => proj_root, :module_name => 'stdlib') + hosts.each do |host| + shell('/bin/touch /etc/puppet/hiera.yaml') + end + end +end diff --git a/spec/unit/facter/facter_dot_d_spec.rb b/spec/unit/facter/facter_dot_d_spec.rb new file mode 100644 index 0000000..1ecffc8 --- /dev/null +++ b/spec/unit/facter/facter_dot_d_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' +require 'facter/facter_dot_d' + +describe Facter::Util::DotD do + + context 'returns a simple fact' do + before :each do + Facter.stubs(:version).returns('1.6.1') + subject.stubs(:entries).returns(['/etc/facter/facts.d/fake_fact.txt']) + File.stubs(:readlines).with('/etc/facter/facts.d/fake_fact.txt').returns(['fake_fact=fake fact']) + subject.create + end + + it 'should return successfully' do + Facter.fact(:fake_fact).value.should == 'fake fact' + end + end + + context 'returns a fact with equals signs' do + before :each do + Facter.stubs(:version).returns('1.6.1') + subject.stubs(:entries).returns(['/etc/facter/facts.d/foo.txt']) + File.stubs(:readlines).with('/etc/facter/facts.d/foo.txt').returns(['foo=1+1=2']) + subject.create + end + + it 'should return successfully' do + Facter.fact(:foo).value.should == '1+1=2' + end + end +end diff --git a/spec/unit/facter/pe_required_facts_spec.rb b/spec/unit/facter/pe_required_facts_spec.rb deleted file mode 100644 index 85ff6ab..0000000 --- a/spec/unit/facter/pe_required_facts_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -# Puppet Enterprise requires the following facts to be set in order to operate. -# These facts are set using the file ???? and the two facts are -# `fact_stomp_port`, and `fact_stomp_server`. -# - -require 'spec_helper' - -describe "External facts in /etc/puppetlabs/facter/facts.d/puppet_enterprise_installer.txt" do - context "With Facter 1.6.17 which does not have external facts support" do - before :each do - Facter.stubs(:version).returns("1.6.17") - Facter::Util::Root.stubs(:root?).returns(true) - # Stub out the filesystem for stdlib - Dir.stubs(:entries).with("/etc/puppetlabs/facter/facts.d"). - returns(['puppet_enterprise_installer.txt']) - Dir.stubs(:entries).with("/etc/facter/facts.d").returns([]) - File.stubs(:readlines).with('/etc/puppetlabs/facter/facts.d/puppet_enterprise_installer.txt'). - returns([ - "fact_stomp_port=61613\n", - "fact_stomp_server=puppetmaster.acme.com\n", - "fact_is_puppetagent=true\n", - "fact_is_puppetmaster=false\n", - "fact_is_puppetca=false\n", - "fact_is_puppetconsole=false\n", - ]) - if Facter.collection.respond_to? :load - Facter.collection.load(:facter_dot_d) - else - Facter.collection.loader.load(:facter_dot_d) - end - end - - it 'defines fact_stomp_port' do - Facter.fact(:fact_stomp_port).value.should == '61613' - end - it 'defines fact_stomp_server' do - Facter.fact(:fact_stomp_server).value.should == 'puppetmaster.acme.com' - end - it 'defines fact_is_puppetagent' do - Facter.fact(:fact_is_puppetagent).value.should == 'true' - end - it 'defines fact_is_puppetmaster' do - Facter.fact(:fact_is_puppetmaster).value.should == 'false' - end - it 'defines fact_is_puppetca' do - Facter.fact(:fact_is_puppetca).value.should == 'false' - end - it 'defines fact_is_puppetconsole' do - Facter.fact(:fact_is_puppetconsole).value.should == 'false' - end - end - - [ '1.7.1', '2.0.1' ].each do |v| - context "With Facter #{v} which has external facts support" do - before :each do - Facter.stubs(:version).returns(v) - end - - it 'does not call Facter::Util::DotD.new' do - Facter::Util::DotD.expects(:new).never - - if Facter.collection.respond_to? :load - Facter.collection.load(:facter_dot_d) - else - Facter.collection.loader.load(:facter_dot_d) - end - end - end - end -end diff --git a/spec/unit/puppet/parser/functions/deep_merge_spec.rb b/spec/unit/puppet/parser/functions/deep_merge_spec.rb index fffb7f7..f134701 100644 --- a/spec/unit/puppet/parser/functions/deep_merge_spec.rb +++ b/spec/unit/puppet/parser/functions/deep_merge_spec.rb @@ -30,7 +30,7 @@ describe Puppet::Parser::Functions.function(:deep_merge) do end it 'should accept empty strings as puppet undef' do - expect { new_hash = scope.function_deep_merge([{}, ''])}.not_to raise_error(Puppet::ParseError, /unexpected argument type String/) + expect { new_hash = scope.function_deep_merge([{}, ''])}.not_to raise_error end it 'should be able to deep_merge two hashes' do @@ -73,5 +73,33 @@ describe Puppet::Parser::Functions.function(:deep_merge) do hash['key1'].should == { 'a' => 1, 'b' => 99 } hash['key2'].should == { 'c' => 3 } end + + it 'should not change the original hashes' do + hash1 = {'one' => { 'two' => 2 } } + hash2 = { 'one' => { 'three' => 3 } } + hash = scope.function_deep_merge([hash1, hash2]) + hash1.should == {'one' => { 'two' => 2 } } + hash2.should == { 'one' => { 'three' => 3 } } + hash['one'].should == { 'two' => 2, 'three' => 3 } + end + + it 'should not change the original hashes 2' do + hash1 = {'one' => { 'two' => [1,2] } } + hash2 = { 'one' => { 'three' => 3 } } + hash = scope.function_deep_merge([hash1, hash2]) + hash1.should == {'one' => { 'two' => [1,2] } } + hash2.should == { 'one' => { 'three' => 3 } } + hash['one'].should == { 'two' => [1,2], 'three' => 3 } + end + + it 'should not change the original hashes 3' do + hash1 = {'one' => { 'two' => [1,2, {'two' => 2} ] } } + hash2 = { 'one' => { 'three' => 3 } } + hash = scope.function_deep_merge([hash1, hash2]) + hash1.should == {'one' => { 'two' => [1,2, {'two' => 2}] } } + hash2.should == { 'one' => { 'three' => 3 } } + hash['one'].should == { 'two' => [1,2, {'two' => 2} ], 'three' => 3 } + hash['one']['two'].should == [1,2, {'two' => 2}] + end end end diff --git a/spec/unit/puppet/parser/functions/merge_spec.rb b/spec/unit/puppet/parser/functions/merge_spec.rb index 8a170bb..15a5d94 100644 --- a/spec/unit/puppet/parser/functions/merge_spec.rb +++ b/spec/unit/puppet/parser/functions/merge_spec.rb @@ -30,7 +30,7 @@ describe Puppet::Parser::Functions.function(:merge) do end it 'should accept empty strings as puppet undef' do - expect { new_hash = scope.function_merge([{}, ''])}.not_to raise_error(Puppet::ParseError, /unexpected argument type String/) + expect { new_hash = scope.function_merge([{}, ''])}.not_to raise_error end it 'should be able to merge two hashes' do diff --git a/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb b/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb index 08aaf78..1c9cce2 100644 --- a/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb @@ -35,7 +35,7 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do end valid_paths.each do |path| it "validate_absolute_path(#{path.inspect}) should not fail" do - expect { subject.call [path] }.not_to raise_error Puppet::ParseError + expect { subject.call [path] }.not_to raise_error end end end @@ -43,7 +43,7 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do context "Puppet without mocking" do valid_paths.each do |path| it "validate_absolute_path(#{path.inspect}) should not fail" do - expect { subject.call [path] }.not_to raise_error Puppet::ParseError + expect { subject.call [path] }.not_to raise_error end end end diff --git a/spec/unit/puppet/provider/file_line/ruby_spec.rb b/spec/unit/puppet/provider/file_line/ruby_spec.rb index 65b5d20..c356bd2 100644 --- a/spec/unit/puppet/provider/file_line/ruby_spec.rb +++ b/spec/unit/puppet/provider/file_line/ruby_spec.rb @@ -1,4 +1,4 @@ -require 'puppet' +require 'spec_helper' require 'tempfile' provider_class = Puppet::Type.type(:file_line).provider(:ruby) describe provider_class do diff --git a/spec/unit/puppet/type/anchor_spec.rb b/spec/unit/puppet/type/anchor_spec.rb index 2030b83..f92065f 100644 --- a/spec/unit/puppet/type/anchor_spec.rb +++ b/spec/unit/puppet/type/anchor_spec.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -require 'puppet' +require 'spec_helper' anchor = Puppet::Type.type(:anchor).new(:name => "ntp::begin") diff --git a/spec/unit/puppet/type/file_line_spec.rb b/spec/unit/puppet/type/file_line_spec.rb index edc64bd..34d5dad 100644 --- a/spec/unit/puppet/type/file_line_spec.rb +++ b/spec/unit/puppet/type/file_line_spec.rb @@ -1,4 +1,4 @@ -require 'puppet' +require 'spec_helper' require 'tempfile' describe Puppet::Type.type(:file_line) do let :file_line do diff --git a/spec/watchr.rb b/spec/watchr.rb deleted file mode 100644 index 885ef1d..0000000 --- a/spec/watchr.rb +++ /dev/null @@ -1,86 +0,0 @@ -ENV['FOG_MOCK'] ||= 'true' -ENV['AUTOTEST'] = 'true' -ENV['WATCHR'] = '1' - -system 'clear' - -def growl(message) - growlnotify = `which growlnotify`.chomp - title = "Watchr Test Results" - image = case message - when /(\d+)\s+?(failure|error)/i - ($1.to_i == 0) ? "~/.watchr_images/passed.png" : "~/.watchr_images/failed.png" - else - '~/.watchr_images/unknown.png' - end - options = "-w -n Watchr --image '#{File.expand_path(image)}' -m '#{message}' '#{title}'" - system %(#{growlnotify} #{options} &) -end - -def run(cmd) - puts(cmd) - `#{cmd}` -end - -def run_spec_test(file) - if File.exist? file - result = run "rspec --format p --color #{file}" - growl result.split("\n").last - puts result - else - puts "FIXME: No test #{file} [#{Time.now}]" - end -end - -def filter_rspec(data) - data.split("\n").find_all do |l| - l =~ /^(\d+)\s+exampl\w+.*?(\d+).*?failur\w+.*?(\d+).*?pending/ - end.join("\n") -end - -def run_all_tests - system('clear') - files = Dir.glob("spec/**/*_spec.rb").join(" ") - result = run "rspec #{files}" - growl_results = filter_rspec result - growl growl_results - puts result - puts "GROWL: #{growl_results}" -end - -# Ctrl-\ -Signal.trap 'QUIT' do - puts " --- Running all tests ---\n\n" - run_all_tests -end - -@interrupted = false - -# Ctrl-C -Signal.trap 'INT' do - if @interrupted then - @wants_to_quit = true - abort("\n") - else - puts "Interrupt a second time to quit" - @interrupted = true - Kernel.sleep 1.5 - # raise Interrupt, nil # let the run loop catch it - run_suite - end -end - -def file2spec(file) - result = file.sub('lib/puppet/', 'spec/unit/puppet/').gsub(/\.rb$/, '_spec.rb') - result = file.sub('lib/facter/', 'spec/unit/facter/').gsub(/\.rb$/, '_spec.rb') -end - - -watch( 'spec/.*_spec\.rb' ) do |md| - #run_spec_test(md[0]) - run_all_tests -end -watch( 'lib/.*\.rb' ) do |md| - # run_spec_test(file2spec(md[0])) - run_all_tests -end |