diff options
Diffstat (limited to 'spec')
59 files changed, 1603 insertions, 115 deletions
diff --git a/spec/acceptance/abs_spec.rb b/spec/acceptance/abs_spec.rb index 8e05642..6e41e2f 100755 --- a/spec/acceptance/abs_spec.rb +++ b/spec/acceptance/abs_spec.rb @@ -7,7 +7,7 @@ describe 'abs function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operati pp = <<-EOS $input = '-34.56' $output = abs($input) - notify { $output: } + notify { "$output": } EOS apply_manifest(pp, :catch_failures => true) do |r| @@ -19,7 +19,7 @@ describe 'abs function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operati pp = <<-EOS $input = -34.56 $output = abs($input) - notify { $output: } + notify { "$output": } EOS apply_manifest(pp, :catch_failures => true) do |r| diff --git a/spec/acceptance/any2array_spec.rb b/spec/acceptance/any2array_spec.rb index 467d6af..18ea4cd 100755 --- a/spec/acceptance/any2array_spec.rb +++ b/spec/acceptance/any2array_spec.rb @@ -25,7 +25,7 @@ describe 'any2array function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('o EOS apply_manifest(pp, :catch_failures => true) do |r| - expect(r.stdout).to match(/Notice: Output: testarray/) + expect(r.stdout).to match(/Notice: Output: (\[|)test(,\s|)array(\]|)/) end end @@ -42,7 +42,7 @@ describe 'any2array function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('o EOS apply_manifest(pp, :catch_failures => true) do |r| - expect(r.stdout).to match(/Notice: Output: testarray/) + expect(r.stdout).to match(/Notice: Output: (\[|)test(,\s|)array(\]|)/) end end end diff --git a/spec/acceptance/bool2num_spec.rb b/spec/acceptance/bool2num_spec.rb index 7a70311..52ff75b 100755 --- a/spec/acceptance/bool2num_spec.rb +++ b/spec/acceptance/bool2num_spec.rb @@ -4,11 +4,11 @@ require 'spec_helper_acceptance' describe 'bool2num function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do describe 'success' do ['false', 'f', '0', 'n', 'no'].each do |bool| - it 'should convert a given boolean, #{bool}, to 0' do + it "should convert a given boolean, #{bool}, to 0" do pp = <<-EOS - $input = #{bool} + $input = "#{bool}" $output = bool2num($input) - notify { $output: } + notify { "$output": } EOS apply_manifest(pp, :catch_failures => true) do |r| @@ -18,11 +18,11 @@ describe 'bool2num function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('op end ['true', 't', '1', 'y', 'yes'].each do |bool| - it 'should convert a given boolean, #{bool}, to 1' do + it "should convert a given boolean, #{bool}, to 1" do pp = <<-EOS - $input = #{bool} + $input = "#{bool}" $output = bool2num($input) - notify { $output: } + notify { "$output": } EOS apply_manifest(pp, :catch_failures => true) do |r| diff --git a/spec/acceptance/ceiling_spec.rb b/spec/acceptance/ceiling_spec.rb new file mode 100755 index 0000000..557986e --- /dev/null +++ b/spec/acceptance/ceiling_spec.rb @@ -0,0 +1,39 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper_acceptance' + +describe 'ceiling function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + describe 'success' do + it 'ceilings floats' do + pp = <<-EOS + $a = 12.8 + $b = 13 + $o = ceiling($a) + if $o == $b { + notify { 'output correct': } + } + EOS + + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.stdout).to match(/Notice: output correct/) + end + end + it 'ceilings integers' do + pp = <<-EOS + $a = 7 + $b = 7 + $o = ceiling($a) + if $o == $b { + notify { 'output correct': } + } + EOS + + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.stdout).to match(/Notice: output correct/) + end + end + end + describe 'failure' do + it 'handles improper argument counts' + it 'handles non-numbers' + end +end diff --git a/spec/acceptance/concat_spec.rb b/spec/acceptance/concat_spec.rb index 7bda365..06b649f 100755 --- a/spec/acceptance/concat_spec.rb +++ b/spec/acceptance/concat_spec.rb @@ -14,5 +14,27 @@ describe 'concat function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('oper apply_manifest(pp, :catch_failures => true) end + it 'should concat arrays and primitives to array' do + pp = <<-EOS + $output = concat(['1','2','3'],'4','5','6',['7','8','9']) + validate_array($output) + if size($output) != 9 { + fail("${output} should have 9 elements.") + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + it 'should concat multiple arrays to one' do + pp = <<-EOS + $output = concat(['1','2','3'],['4','5','6'],['7','8','9']) + validate_array($output) + if size($output) != 9 { + fail("${output} should have 9 elements.") + } + EOS + + apply_manifest(pp, :catch_failures => true) + end end end diff --git a/spec/acceptance/count_spec.rb b/spec/acceptance/count_spec.rb index 51a40ba..fe7ca9d 100755 --- a/spec/acceptance/count_spec.rb +++ b/spec/acceptance/count_spec.rb @@ -7,7 +7,7 @@ describe 'count function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('opera pp = <<-EOS $input = [1,2,3,4] $output = count($input) - notify { $output: } + notify { "$output": } EOS apply_manifest(pp, :catch_failures => true) do |r| @@ -19,7 +19,7 @@ describe 'count function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('opera pp = <<-EOS $input = [1,1,1,2] $output = count($input, 1) - notify { $output: } + notify { "$output": } EOS apply_manifest(pp, :catch_failures => true) do |r| diff --git a/spec/acceptance/ensure_packages_spec.rb b/spec/acceptance/ensure_packages_spec.rb index 12da0cd..aedcfb5 100755 --- a/spec/acceptance/ensure_packages_spec.rb +++ b/spec/acceptance/ensure_packages_spec.rb @@ -1,7 +1,7 @@ #! /usr/bin/env ruby -S rspec require 'spec_helper_acceptance' -describe 'ensure_packages function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do +describe 'ensure_packages function', :unless => fact('osfamily') =~ /windows/i do describe 'success' do it 'ensure_packages a package' do apply_manifest('package { "rake": ensure => absent, provider => "gem", }') diff --git a/spec/acceptance/ensure_resource_spec.rb b/spec/acceptance/ensure_resource_spec.rb index 2aad243..1cee53d 100755 --- a/spec/acceptance/ensure_resource_spec.rb +++ b/spec/acceptance/ensure_resource_spec.rb @@ -1,7 +1,7 @@ #! /usr/bin/env ruby -S rspec require 'spec_helper_acceptance' -describe 'ensure_resource function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do +describe 'ensure_resource function', :unless => fact('osfamily') =~ /windows/i do describe 'success' do it 'ensure_resource a package' do apply_manifest('package { "rake": ensure => absent, provider => "gem", }') diff --git a/spec/acceptance/fqdn_rand_string_spec.rb b/spec/acceptance/fqdn_rand_string_spec.rb new file mode 100644 index 0000000..8fe1a69 --- /dev/null +++ b/spec/acceptance/fqdn_rand_string_spec.rb @@ -0,0 +1,60 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper_acceptance' + +describe 'fqdn_rand_string function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + describe 'success' do + let(:facts_d) do + if fact('is_pe', '--puppet') == "true" + if fact('osfamily') =~ /windows/i + if fact('kernelmajversion').to_f < 6.0 + 'c:/documents and settings/all users/application data/puppetlabs/facter/facts.d' + else + 'c:/programdata/puppetlabs/facter/facts.d' + end + else + '/etc/puppetlabs/facter/facts.d' + end + else + '/etc/facter/facts.d' + end + end + after :each do + shell("if [ -f '#{facts_d}/fqdn.txt' ] ; then rm '#{facts_d}/fqdn.txt' ; fi") + end + before :each do + #no need to create on windows, pe creates by default + if fact('osfamily') !~ /windows/i + shell("mkdir -p '#{facts_d}'") + end + end + it 'generates random alphanumeric strings' do + shell("echo fqdn=fakehost.localdomain > '#{facts_d}/fqdn.txt'") + pp = <<-eos + $l = 10 + $o = fqdn_rand_string($l) + notice(inline_template('fqdn_rand_string is <%= @o.inspect %>')) + eos + + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.stdout).to match(/fqdn_rand_string is "7oDp0KOr1b"/) + end + end + it 'generates random alphanumeric strings with custom seeds' do + shell("echo fqdn=fakehost.localdomain > '#{facts_d}/fqdn.txt'") + pp = <<-eos + $l = 10 + $s = 'seed' + $o = fqdn_rand_string($l, undef, $s) + notice(inline_template('fqdn_rand_string is <%= @o.inspect %>')) + eos + + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.stdout).to match(/fqdn_rand_string is "3HS4mbuI3E"/) + end + end + end + describe 'failure' do + it 'handles improper argument counts' + it 'handles non-numbers for length argument' + end +end diff --git a/spec/acceptance/fqdn_rotate_spec.rb b/spec/acceptance/fqdn_rotate_spec.rb index c37b35a..753068b 100755 --- a/spec/acceptance/fqdn_rotate_spec.rb +++ b/spec/acceptance/fqdn_rotate_spec.rb @@ -21,7 +21,7 @@ describe 'fqdn_rotate function', :unless => UNSUPPORTED_PLATFORMS.include?(fact( after :each do shell("if [ -f '#{facts_d}/fqdn.txt' ] ; then rm '#{facts_d}/fqdn.txt' ; fi") end - before :all do + before :each do #No need to create on windows, PE creates by default if fact('osfamily') !~ /windows/i shell("mkdir -p '#{facts_d}'") diff --git a/spec/acceptance/member_spec.rb b/spec/acceptance/member_spec.rb index b467dbb..fe75a07 100755 --- a/spec/acceptance/member_spec.rb +++ b/spec/acceptance/member_spec.rb @@ -2,6 +2,13 @@ require 'spec_helper_acceptance' describe 'member function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + shared_examples 'item found' do + it 'should output correctly' do + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.stdout).to match(/Notice: output correct/) + end + end + end describe 'success' do it 'members arrays' do pp = <<-EOS @@ -18,8 +25,29 @@ describe 'member function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('oper expect(r.stdout).to match(/Notice: output correct/) end end + describe 'members array of integers' do + it_should_behave_like 'item found' do + let(:pp) { <<-EOS + if member( [1,2,3,4], 4 ){ + notify { 'output correct': } + } + EOS + } + end + end + describe 'members of mixed array' do + it_should_behave_like 'item found' do + let(:pp) { <<-EOS + if member( ['a','4',3], 'a' ){ + notify { 'output correct': } +} + EOS + } + end + end it 'members arrays without members' end + describe 'failure' do it 'handles improper argument counts' end diff --git a/spec/acceptance/merge_spec.rb b/spec/acceptance/merge_spec.rb index a60e784..227b994 100755 --- a/spec/acceptance/merge_spec.rb +++ b/spec/acceptance/merge_spec.rb @@ -14,9 +14,9 @@ describe 'merge function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('opera EOS apply_manifest(pp, :catch_failures => true) do |r| - expect(r.stdout).to match(/merge\[one\] is "1"/) + expect(r.stdout).to match(/merge\[one\] is ("1"|1)/) expect(r.stdout).to match(/merge\[two\] is "dos"/) - expect(r.stdout).to match(/merge\[three\] is {"five"=>"5"}/) + expect(r.stdout).to match(/merge\[three\] is {"five"=>("5"|5)}/) end end end diff --git a/spec/acceptance/pw_hash_spec.rb b/spec/acceptance/pw_hash_spec.rb new file mode 100644 index 0000000..eddb782 --- /dev/null +++ b/spec/acceptance/pw_hash_spec.rb @@ -0,0 +1,34 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper_acceptance' + +# Windows and OS X do not have useful implementations of crypt(3) +describe 'pw_hash function', :unless => (UNSUPPORTED_PLATFORMS + ['windows', 'Darwin']).include?(fact('operatingsystem')) do + describe 'success' do + it 'hashes passwords' do + pp = <<-EOS + $o = pw_hash('password', 'sha-512', 'salt') + notice(inline_template('pw_hash is <%= @o.inspect %>')) + EOS + + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.stdout).to match(/pw_hash is "\$6\$salt\$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy\.g\."/) + end + end + + it 'returns nil if no password is provided' do + pp = <<-EOS + $o = pw_hash('', 'sha-512', 'salt') + notice(inline_template('pw_hash is <%= @o.inspect %>')) + EOS + + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.stdout).to match(/pw_hash is nil/) + end + end + end + describe 'failure' do + it 'handles less than three arguments' + it 'handles more than three arguments' + it 'handles non strings' + end +end diff --git a/spec/acceptance/type_spec.rb b/spec/acceptance/type_spec.rb index 0043aad..67e3248 100755 --- a/spec/acceptance/type_spec.rb +++ b/spec/acceptance/type_spec.rb @@ -1,7 +1,7 @@ #! /usr/bin/env ruby -S rspec require 'spec_helper_acceptance' -describe 'type function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do +describe 'type function', :unless => (UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) || is_future_parser_enabled?) do describe 'success' do it 'types arrays' do pp = <<-EOS diff --git a/spec/acceptance/validate_cmd_spec.rb b/spec/acceptance/validate_cmd_spec.rb index 385676d..5ac66fd 100755 --- a/spec/acceptance/validate_cmd_spec.rb +++ b/spec/acceptance/validate_cmd_spec.rb @@ -37,10 +37,12 @@ describe 'validate_cmd function', :unless => UNSUPPORTED_PLATFORMS.include?(fact } else { $two = '/bin/aoeu' } - validate_cmd($one,$two,"aoeu is dvorak) + validate_cmd($one,$two,"aoeu is dvorak") EOS - expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/aoeu is dvorak/) + apply_manifest(pp, :expect_failures => true) do |output| + expect(output.stderr).to match(/aoeu is dvorak/) + end end end describe 'failure' do diff --git a/spec/acceptance/values_spec.rb b/spec/acceptance/values_spec.rb index 7ef956e..a2eff32 100755 --- a/spec/acceptance/values_spec.rb +++ b/spec/acceptance/values_spec.rb @@ -13,8 +13,12 @@ describe 'values function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('oper $output = values($arg) notice(inline_template('<%= @output.sort.inspect %>')) EOS + if is_future_parser_enabled? + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[1, 2, 3\]/) + else + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\["1", "2", "3"\]/) + end - expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\["1", "2", "3"\]/) end end describe 'failure' do diff --git a/spec/acceptance/zip_spec.rb b/spec/acceptance/zip_spec.rb index 0e924e8..139079e 100755 --- a/spec/acceptance/zip_spec.rb +++ b/spec/acceptance/zip_spec.rb @@ -11,8 +11,11 @@ describe 'zip function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operati $output = zip($one,$two) notice(inline_template('<%= @output.inspect %>')) EOS - - expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[\["1", "5"\], \["2", "6"\], \["3", "7"\], \["4", "8"\]\]/) + if is_future_parser_enabled? + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[\[1, 5\], \[2, 6\], \[3, 7\], \[4, 8\]\]/) + else + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[\["1", "5"\], \["2", "6"\], \["3", "7"\], \["4", "8"\]\]/) + end end it 'zips two arrays of numbers & bools together' do pp = <<-EOS @@ -21,8 +24,11 @@ describe 'zip function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operati $output = zip($one,$two) notice(inline_template('<%= @output.inspect %>')) EOS - - expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[\["1", true\], \["2", true\], \["three", false\], \["4", false\]\]/) + if is_future_parser_enabled? + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[\[1, true\], \[2, true\], \["three", false\], \[4, false\]\]/) + else + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[\["1", true\], \["2", true\], \["three", false\], \["4", false\]\]/) + end end it 'zips two arrays of numbers together and flattens them' do # XXX This only tests the argument `true`, even though the following are valid: @@ -35,8 +41,11 @@ describe 'zip function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operati $output = zip($one,$two,true) notice(inline_template('<%= @output.inspect %>')) EOS - - expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\["1", "5", "2", "6", "3", "7", "4", "8"\]/) + if is_future_parser_enabled? + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[1, 5, 2, 6, 3, 7, 4, 8\]/) + else + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\["1", "5", "2", "6", "3", "7", "4", "8"\]/) + end end it 'handles unmatched length' do # XXX Is this expected behavior? @@ -46,8 +55,11 @@ describe 'zip function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operati $output = zip($one,$two) notice(inline_template('<%= @output.inspect %>')) EOS - - expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[\["1", "5"\], \["2", "6"\]\]/) + if is_future_parser_enabled? + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[\[1, 5\], \[2, 6\]\]/) + else + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/\[\["1", "5"\], \["2", "6"\]\]/) + end end end describe 'failure' do diff --git a/spec/functions/assert_private_spec.rb b/spec/functions/assert_private_spec.rb new file mode 100755 index 0000000..a009d28 --- /dev/null +++ b/spec/functions/assert_private_spec.rb @@ -0,0 +1,55 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:assert_private) do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + subject do + function_name = Puppet::Parser::Functions.function(:assert_private) + scope.method(function_name) + end + + context "when called from inside module" do + it "should not fail" do + scope.expects(:lookupvar).with('module_name').returns('foo') + scope.expects(:lookupvar).with('caller_module_name').returns('foo') + expect { + subject.call [] + }.not_to raise_error + end + end + + context "with an explicit failure message" do + it "prints the failure message on error" do + scope.expects(:lookupvar).with('module_name').returns('foo') + scope.expects(:lookupvar).with('caller_module_name').returns('bar') + expect { + subject.call ['failure message!'] + }.to raise_error Puppet::ParseError, /failure message!/ + end + end + + context "when called from private class" do + it "should fail with a class error message" do + scope.expects(:lookupvar).with('module_name').returns('foo') + scope.expects(:lookupvar).with('caller_module_name').returns('bar') + scope.source.expects(:name).returns('foo::baz') + scope.source.expects(:type).returns('hostclass') + expect { + subject.call [] + }.to raise_error Puppet::ParseError, /Class foo::baz is private/ + end + end + + context "when called from private definition" do + it "should fail with a class error message" do + scope.expects(:lookupvar).with('module_name').returns('foo') + scope.expects(:lookupvar).with('caller_module_name').returns('bar') + scope.source.expects(:name).returns('foo::baz') + scope.source.expects(:type).returns('definition') + expect { + subject.call [] + }.to raise_error Puppet::ParseError, /Definition foo::baz is private/ + end + end +end diff --git a/spec/functions/bool2num_spec.rb b/spec/functions/bool2num_spec.rb index fbf461b..3904d7e 100755 --- a/spec/functions/bool2num_spec.rb +++ b/spec/functions/bool2num_spec.rb @@ -17,8 +17,22 @@ describe "the bool2num function" do expect(result).to(eq(1)) end - it "should convert false to 0" do - result = scope.function_bool2num([false]) + it "should convert 'true' to 1" do + result = scope.function_bool2num(['true']) + result.should(eq(1)) + end + + it "should convert 'false' to 0" do + result = scope.function_bool2num(['false']) expect(result).to(eq(0)) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new('true') + result = scope.function_bool2num([value]) + result.should(eq(1)) + end end diff --git a/spec/functions/capitalize_spec.rb b/spec/functions/capitalize_spec.rb index 0cc2d76..fd0e92b 100755 --- a/spec/functions/capitalize_spec.rb +++ b/spec/functions/capitalize_spec.rb @@ -16,4 +16,13 @@ describe "the capitalize function" do result = scope.function_capitalize(["abc"]) expect(result).to(eq("Abc")) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new('abc') + result = scope.function_capitalize([value]) + result.should(eq('Abc')) + end end diff --git a/spec/functions/ceiling_spec.rb b/spec/functions/ceiling_spec.rb new file mode 100755 index 0000000..814aa7c --- /dev/null +++ b/spec/functions/ceiling_spec.rb @@ -0,0 +1,39 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe "the ceiling function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + expect(Puppet::Parser::Functions.function("ceiling")).to eq("function_ceiling") + end + + it "should raise a ParseError if there is less than 1 argument" do + expect { scope.function_ceiling([]) }.to( raise_error(Puppet::ParseError, /Wrong number of arguments/)) + end + + it "should should raise a ParseError if input isn't numeric (eg. String)" do + expect { scope.function_ceiling(["foo"]) }.to( raise_error(Puppet::ParseError, /Wrong argument type/)) + end + + it "should should raise a ParseError if input isn't numeric (eg. Boolean)" do + expect { scope.function_ceiling([true]) }.to( raise_error(Puppet::ParseError, /Wrong argument type/)) + end + + it "should return an integer when a numeric type is passed" do + result = scope.function_ceiling([12.4]) + expect(result.is_a?(Integer)).to(eq(true)) + end + + it "should return the input when an integer is passed" do + result = scope.function_ceiling([7]) + expect(result).to(eq(7)) + end + + it "should return the smallest integer greater than or equal to the input" do + result = scope.function_ceiling([3.8]) + expect(result).to(eq(4)) + end +end + diff --git a/spec/functions/chomp_spec.rb b/spec/functions/chomp_spec.rb index d2ae287..b1e1e60 100755 --- a/spec/functions/chomp_spec.rb +++ b/spec/functions/chomp_spec.rb @@ -16,4 +16,13 @@ describe "the chomp function" do result = scope.function_chomp(["abc\n"]) expect(result).to(eq("abc")) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new("abc\n") + result = scope.function_chomp([value]) + result.should(eq("abc")) + end end diff --git a/spec/functions/chop_spec.rb b/spec/functions/chop_spec.rb index d9dbb88..c8a1951 100755 --- a/spec/functions/chop_spec.rb +++ b/spec/functions/chop_spec.rb @@ -16,4 +16,13 @@ describe "the chop function" do result = scope.function_chop(["asdf\n"]) expect(result).to(eq("asdf")) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new("abc\n") + result = scope.function_chop([value]) + result.should(eq('abc')) + end end diff --git a/spec/functions/concat_spec.rb b/spec/functions/concat_spec.rb index 49cb2ad..49fa6bb 100755 --- a/spec/functions/concat_spec.rb +++ b/spec/functions/concat_spec.rb @@ -4,14 +4,19 @@ require 'spec_helper' describe "the concat function" do let(:scope) { PuppetlabsSpec::PuppetInternals.scope } - it "should raise a ParseError if the client does not provide two arguments" do + it "should raise a ParseError if the client does not provide at least two arguments" do expect { scope.function_concat([]) }.to(raise_error(Puppet::ParseError)) + expect { scope.function_concat([[1]]) }.to(raise_error(Puppet::ParseError)) end it "should raise a ParseError if the first parameter is not an array" do expect { scope.function_concat([1, []])}.to(raise_error(Puppet::ParseError)) end + it "should not raise a ParseError if the client provides more than two arguments" do + expect { scope.function_concat([[1],[2],[3]]) }.not_to raise_error + end + it "should be able to concat an array" do result = scope.function_concat([['1','2','3'],['4','5','6']]) expect(result).to(eq(['1','2','3','4','5','6'])) @@ -32,4 +37,14 @@ describe "the concat function" do result = scope.function_concat([array_original,['4','5','6']]) array_original.should(eq(['1','2','3'])) end + + it "should be able to concat multiple arrays" do + result = scope.function_concat([['1','2','3'],['4','5','6'],['7','8','9']]) + expect(result).to(eq(['1','2','3','4','5','6','7','8','9'])) + end + + it "should be able to concat mix of primitives and arrays to a final array" do + result = scope.function_concat([['1','2','3'],'4',['5','6','7']]) + expect(result).to(eq(['1','2','3','4','5','6','7'])) + end end diff --git a/spec/functions/delete_spec.rb b/spec/functions/delete_spec.rb index 39b3176..c8edd78 100755 --- a/spec/functions/delete_spec.rb +++ b/spec/functions/delete_spec.rb @@ -9,48 +9,53 @@ describe "the delete function" do end it "should raise a ParseError if there are fewer than 2 arguments" do - expect { scope.function_delete([]) }.to( raise_error(Puppet::ParseError)) + expect { scope.function_delete([]) }.to(raise_error(Puppet::ParseError)) end it "should raise a ParseError if there are greater than 2 arguments" do - expect { scope.function_delete([[], 'foo', 'bar']) }.to( raise_error(Puppet::ParseError)) + expect { scope.function_delete([[], 'foo', 'bar']) }.to(raise_error(Puppet::ParseError)) end it "should raise a TypeError if a number is passed as the first argument" do - expect { scope.function_delete([1, 'bar']) }.to( raise_error(TypeError)) + expect { scope.function_delete([1, 'bar']) }.to(raise_error(TypeError)) end it "should delete all instances of an element from an array" do - result = scope.function_delete([['a','b','c','b'],'b']) - expect(result).to(eq(['a','c'])) + result = scope.function_delete([['a', 'b', 'c', 'b'], 'b']) + expect(result).to(eq(['a', 'c'])) end it "should delete all instances of a substring from a string" do - result = scope.function_delete(['foobarbabarz','bar']) + result = scope.function_delete(['foobarbabarz', 'bar']) expect(result).to(eq('foobaz')) end it "should delete a key from a hash" do - result = scope.function_delete([{ 'a' => 1, 'b' => 2, 'c' => 3 },'b']) - expect(result).to(eq({ 'a' => 1, 'c' => 3 })) + result = scope.function_delete([{'a' => 1, 'b' => 2, 'c' => 3}, 'b']) + expect(result).to(eq({'a' => 1, 'c' => 3})) + end + + it 'should accept an array of items to delete' do + result = scope.function_delete([{'a' => 1, 'b' => 2, 'c' => 3}, ['b', 'c']]) + expect(result).to(eq({'a' => 1})) end it "should not change origin array passed as argument" do - origin_array = ['a','b','c','d'] + origin_array = ['a', 'b', 'c', 'd'] result = scope.function_delete([origin_array, 'b']) - expect(origin_array).to(eq(['a','b','c','d'])) + expect(origin_array).to(eq(['a', 'b', 'c', 'd'])) end it "should not change the origin string passed as argument" do origin_string = 'foobarbabarz' - result = scope.function_delete([origin_string,'bar']) + result = scope.function_delete([origin_string, 'bar']) expect(origin_string).to(eq('foobarbabarz')) end it "should not change origin hash passed as argument" do - origin_hash = { 'a' => 1, 'b' => 2, 'c' => 3 } + origin_hash = {'a' => 1, 'b' => 2, 'c' => 3} result = scope.function_delete([origin_hash, 'b']) - expect(origin_hash).to(eq({ 'a' => 1, 'b' => 2, 'c' => 3 })) + expect(origin_hash).to(eq({'a' => 1, 'b' => 2, 'c' => 3})) end end diff --git a/spec/functions/dirname_spec.rb b/spec/functions/dirname_spec.rb index 8a3bcab..4261144 100755 --- a/spec/functions/dirname_spec.rb +++ b/spec/functions/dirname_spec.rb @@ -12,6 +12,10 @@ describe "the dirname function" do expect { scope.function_dirname([]) }.to( raise_error(Puppet::ParseError)) end + it "should raise a ParseError if there is more than 1 argument" do + expect { scope.function_dirname(['a', 'b']) }.to( raise_error(Puppet::ParseError)) + end + it "should return dirname for an absolute path" do result = scope.function_dirname(['/path/to/a/file.ext']) expect(result).to(eq('/path/to/a')) @@ -21,4 +25,14 @@ describe "the dirname function" do result = scope.function_dirname(['path/to/a/file.ext']) expect(result).to(eq('path/to/a')) end + + it "should complain about hash argument" do + expect { scope.function_dirname([{}]) }.to( raise_error(Puppet::ParseError)) + end + it "should complain about list argument" do + expect { scope.function_dirname([[]]) }.to( raise_error(Puppet::ParseError)) + end + it "should complain about numeric argument" do + expect { scope.function_dirname([2112]) }.to( raise_error(Puppet::ParseError)) + end end diff --git a/spec/functions/downcase_spec.rb b/spec/functions/downcase_spec.rb index a844780..edebc44 100755 --- a/spec/functions/downcase_spec.rb +++ b/spec/functions/downcase_spec.rb @@ -21,4 +21,13 @@ describe "the downcase function" do result = scope.function_downcase(["asdf asdf"]) expect(result).to(eq("asdf asdf")) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new("ASFD") + result = scope.function_downcase([value]) + result.should(eq('asfd')) + end end diff --git a/spec/functions/empty_spec.rb b/spec/functions/empty_spec.rb index 1f2ace4..6a97c4c 100755 --- a/spec/functions/empty_spec.rb +++ b/spec/functions/empty_spec.rb @@ -20,4 +20,13 @@ describe "the empty function" do result = scope.function_empty(['asdf']) expect(result).to(eq(false)) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new() + result = scope.function_empty([value]) + result.should(eq(true)) + end end diff --git a/spec/functions/fqdn_rand_string_spec.rb b/spec/functions/fqdn_rand_string_spec.rb new file mode 100644 index 0000000..949d930 --- /dev/null +++ b/spec/functions/fqdn_rand_string_spec.rb @@ -0,0 +1,91 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the fqdn_rand_string function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + expect(Puppet::Parser::Functions.function("fqdn_rand_string")).to eq("function_fqdn_rand_string") + end + + it "should raise an ArgumentError if there is less than 1 argument" do + expect { fqdn_rand_string() }.to( raise_error(ArgumentError, /wrong number of arguments/)) + end + + it "should raise an ArgumentError if argument 1 isn't a positive integer" do + expect { fqdn_rand_string(0) }.to( raise_error(ArgumentError, /first argument must be a positive integer/)) + expect { fqdn_rand_string(-1) }.to( raise_error(ArgumentError, /first argument must be a positive integer/)) + expect { fqdn_rand_string(0.5) }.to( raise_error(ArgumentError, /first argument must be a positive integer/)) + end + + it "provides a valid alphanumeric string when no character set is provided" do + length = 100 + string = %r{\A[a-zA-Z0-9]{#{length}}\z} + expect(fqdn_rand_string(length).match(string)).not_to eq(nil) + end + + it "provides a valid alphanumeric string when an undef character set is provided" do + length = 100 + string = %r{\A[a-zA-Z0-9]{#{length}}\z} + expect(fqdn_rand_string(length, :charset => nil).match(string)).not_to eq(nil) + end + + it "provides a valid alphanumeric string when an empty character set is provided" do + length = 100 + string = %r{\A[a-zA-Z0-9]{#{length}}\z} + expect(fqdn_rand_string(length, :charset => '').match(string)).not_to eq(nil) + end + + it "uses a provided character set" do + length = 100 + charset = '!@#$%^&*()-_=+' + string = %r{\A[#{charset}]{#{length}}\z} + expect(fqdn_rand_string(length, :charset => charset).match(string)).not_to eq(nil) + end + + it "provides a random string exactly as long as the given length" do + expect(fqdn_rand_string(10).size).to eql(10) + end + + it "provides the same 'random' value on subsequent calls for the same host" do + expect(fqdn_rand_string(10)).to eql(fqdn_rand_string(10)) + end + + it "considers the same host and same extra arguments to have the same random sequence" do + first_random = fqdn_rand_string(10, :extra_identifier => [1, "same", "host"]) + second_random = fqdn_rand_string(10, :extra_identifier => [1, "same", "host"]) + + expect(first_random).to eql(second_random) + end + + it "allows extra arguments to control the random value on a single host" do + first_random = fqdn_rand_string(10, :extra_identifier => [1, "different", "host"]) + second_different_random = fqdn_rand_string(10, :extra_identifier => [2, "different", "host"]) + + expect(first_random).not_to eql(second_different_random) + end + + it "should return different strings for different hosts" do + val1 = fqdn_rand_string(10, :host => "first.host.com") + val2 = fqdn_rand_string(10, :host => "second.host.com") + + expect(val1).not_to eql(val2) + end + + def fqdn_rand_string(max, args = {}) + host = args[:host] || '127.0.0.1' + charset = args[:charset] + extra = args[:extra_identifier] || [] + + scope = PuppetlabsSpec::PuppetInternals.scope + scope.stubs(:[]).with("::fqdn").returns(host) + scope.stubs(:lookupvar).with("::fqdn").returns(host) + + function_args = [max] + if args.has_key?(:charset) or !extra.empty? + function_args << charset + end + function_args += extra + scope.function_fqdn_rand_string(function_args) + end +end diff --git a/spec/functions/fqdn_rotate_spec.rb b/spec/functions/fqdn_rotate_spec.rb index b2dc1f5..673a8a3 100755 --- a/spec/functions/fqdn_rotate_spec.rb +++ b/spec/functions/fqdn_rotate_spec.rb @@ -30,4 +30,31 @@ describe "the fqdn_rotate function" do val2 = scope.function_fqdn_rotate(["abcdefghijklmnopqrstuvwxyz01234567890987654321"]) expect(val1).not_to eql(val2) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") + value = AlsoString.new("asdf") + result = scope.function_fqdn_rotate([value]) + result.size.should(eq(4)) + end + + it "should use the Puppet::Util.deterministic_rand function if available" do + scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") + if Puppet::Util.respond_to?(:deterministic_rand) + Puppet::Util.expects(:deterministic_rand).with(113646079810780526294648115052177588845,4) + end + scope.function_fqdn_rotate(["asdf"]) + end + + it "should not leave the global seed in a deterministic state" do + scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1").twice + scope.function_fqdn_rotate(["asdf"]) + rand1 = rand() + scope.function_fqdn_rotate(["asdf"]) + rand2 = rand() + expect(rand1).not_to eql(rand2) + end end diff --git a/spec/functions/is_domain_name_spec.rb b/spec/functions/is_domain_name_spec.rb index 4d05f5c..ef88061 100755 --- a/spec/functions/is_domain_name_spec.rb +++ b/spec/functions/is_domain_name_spec.rb @@ -61,4 +61,21 @@ describe "the is_domain_name function" do result = scope.function_is_domain_name(["not valid"]) expect(result).to(be_falsey) end + + # Values obtained from Facter values will be frozen strings + # in newer versions of Facter: + it "should not throw an exception if passed a frozen string" do + result = scope.function_is_domain_name(["my.domain.name".freeze]) + expect(result).to(be_truthy) + end + + it "should return false if top-level domain is not entirely alphabetic" do + result = scope.function_is_domain_name(["kiwi.2bar"]) + expect(result).to(be_falsey) + end + + it "should return false if domain name has the dotted-decimal form, e.g. an IPv4 address" do + result = scope.function_is_domain_name(["192.168.1.1"]) + expect(result).to(be_falsey) + end end diff --git a/spec/functions/lstrip_spec.rb b/spec/functions/lstrip_spec.rb index 7025f97..68cca1c 100755 --- a/spec/functions/lstrip_spec.rb +++ b/spec/functions/lstrip_spec.rb @@ -16,4 +16,13 @@ describe "the lstrip function" do result = scope.function_lstrip([" asdf"]) expect(result).to(eq('asdf')) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new(" asdf") + result = scope.function_lstrip([value]) + result.should(eq("asdf")) + end end diff --git a/spec/functions/member_spec.rb b/spec/functions/member_spec.rb index cee6110..1a1d7c6 100755 --- a/spec/functions/member_spec.rb +++ b/spec/functions/member_spec.rb @@ -21,4 +21,14 @@ describe "the member function" do result = scope.function_member([["a","b","c"], "d"]) expect(result).to(eq(false)) end + + it "should return true if a member array is in an array" do + result = scope.function_member([["a","b","c"], ["a", "b"]]) + expect(result).to(eq(true)) + end + + it "should return false if a member array is not in an array" do + result = scope.function_member([["a","b","c"], ["d", "e"]]) + expect(result).to(eq(false)) + end end diff --git a/spec/functions/prefix_spec.rb b/spec/functions/prefix_spec.rb index 34cac53..aec8a7d 100755 --- a/spec/functions/prefix_spec.rb +++ b/spec/functions/prefix_spec.rb @@ -25,4 +25,9 @@ describe "the prefix function" do result = scope.function_prefix([['a','b','c'], 'p']) expect(result).to(eq(['pa','pb','pc'])) end + + it "returns a prefixed hash" do + result = scope.function_prefix([{'a' => 'b','b' => 'c','c' => 'd'}, 'p']) + expect(result).to(eq({'pa'=>'b','pb'=>'c','pc'=>'d'})) + end end diff --git a/spec/functions/private_spec.rb b/spec/functions/private_spec.rb index c70759f..c90282e 100755..100644 --- a/spec/functions/private_spec.rb +++ b/spec/functions/private_spec.rb @@ -9,6 +9,11 @@ describe Puppet::Parser::Functions.function(:private) do scope.method(function_name) end + it 'should issue a warning' do + scope.expects(:warning).with("private() DEPRECATED: This function will cease to function on Puppet 4; please use assert_private() before upgrading to puppet 4 for backwards-compatibility, or migrate to the new parser's typing system.") + subject.call [] + end + context "when called from inside module" do it "should not fail" do scope.expects(:lookupvar).with('module_name').returns('foo') diff --git a/spec/functions/pw_hash_spec.rb b/spec/functions/pw_hash_spec.rb new file mode 100644 index 0000000..3378090 --- /dev/null +++ b/spec/functions/pw_hash_spec.rb @@ -0,0 +1,96 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the pw_hash function" do + + before :all do + @enhanced_salts_supported = RUBY_PLATFORM == 'java' + end + + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + expect(Puppet::Parser::Functions.function("pw_hash")).to eq("function_pw_hash") + end + + it "should raise an ArgumentError if there are less than 3 arguments" do + expect { scope.function_pw_hash([]) }.to( raise_error(ArgumentError, /[Ww]rong number of arguments/) ) + expect { scope.function_pw_hash(['password']) }.to( raise_error(ArgumentError, /[Ww]rong number of arguments/) ) + expect { scope.function_pw_hash(['password', 'sha-512']) }.to( raise_error(ArgumentError, /[Ww]rong number of arguments/) ) + end + + it "should raise an ArgumentError if there are more than 3 arguments" do + expect { scope.function_pw_hash(['password', 'sha-512', 'salt', 5]) }.to( raise_error(ArgumentError, /[Ww]rong number of arguments/) ) + end + + it "should raise an ArgumentError if the first argument is not a string" do + expect { scope.function_pw_hash([['password'], 'sha-512', 'salt']) }.to( raise_error(ArgumentError, /first argument must be a string/) ) + # in Puppet 3, numbers are passed as strings, so we can't test that + end + + it "should return nil if the first argument is empty" do + expect(scope.function_pw_hash(['', 'sha-512', 'salt'])).to eq(nil) + end + + it "should return nil if the first argument is undef" do + expect(scope.function_pw_hash([nil, 'sha-512', 'salt'])).to eq(nil) + end + + it "should raise an ArgumentError if the second argument is an invalid hash type" do + expect { scope.function_pw_hash(['', 'invalid', 'salt']) }.to( raise_error(ArgumentError, /not a valid hash type/) ) + end + + it "should raise an ArgumentError if the second argument is not a string" do + expect { scope.function_pw_hash(['', [], 'salt']) }.to( raise_error(ArgumentError, /second argument must be a string/) ) + end + + it "should raise an ArgumentError if the third argument is not a string" do + expect { scope.function_pw_hash(['password', 'sha-512', ['salt']]) }.to( raise_error(ArgumentError, /third argument must be a string/) ) + # in Puppet 3, numbers are passed as strings, so we can't test that + end + + it "should raise an ArgumentError if the third argument is empty" do + expect { scope.function_pw_hash(['password', 'sha-512', '']) }.to( raise_error(ArgumentError, /third argument must not be empty/) ) + end + + it "should raise an ArgumentError if the third argument has invalid characters" do + expect { scope.function_pw_hash(['password', 'sha-512', '%']) }.to( raise_error(ArgumentError, /characters in salt must be in the set/) ) + end + + it "should fail on platforms with weak implementations of String#crypt" do + String.any_instance.expects(:crypt).with('$1$1').returns('$1SoNol0Ye6Xk') + expect { scope.function_pw_hash(['password', 'sha-512', 'salt']) }.to( raise_error(Puppet::ParseError, /system does not support enhanced salts/) ) + end + + if @enhanced_salts_supported + describe "on systems with enhanced salts support" do + it "should return a hashed password" do + result = scope.function_pw_hash(['password', 'sha-512', 'salt']) + expect(result).to eql('$6$salt$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy.g.') + end + + it "should use the specified salt" do + result = scope.function_pw_hash(['password', 'sha-512', 'salt']) + expect(result).to match('salt') + end + + it "should use the specified hash type" do + resultmd5 = scope.function_pw_hash(['password', 'md5', 'salt']) + resultsha256 = scope.function_pw_hash(['password', 'sha-256', 'salt']) + resultsha512 = scope.function_pw_hash(['password', 'sha-512', 'salt']) + + expect(resultmd5).to eql('$1$salt$qJH7.N4xYta3aEG/dfqo/0') + expect(resultsha256).to eql('$5$salt$Gcm6FsVtF/Qa77ZKD.iwsJlCVPY0XSMgLJL0Hnww/c1') + expect(resultsha512).to eql('$6$salt$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy.g.') + end + + it "should generate a valid hash" do + password_hash = scope.function_pw_hash(['password', 'sha-512', 'salt']) + + hash_parts = password_hash.match(%r{\A\$(.*)\$([a-zA-Z0-9./]+)\$([a-zA-Z0-9./]+)\z}) + + expect(hash_parts).not_to eql(nil) + end + end + end +end diff --git a/spec/functions/range_spec.rb b/spec/functions/range_spec.rb index 9b9ece0..ef86f97 100755 --- a/spec/functions/range_spec.rb +++ b/spec/functions/range_spec.rb @@ -67,4 +67,20 @@ describe "the range function" do expect(scope.function_range(["00", "10"])).to eq expected end end + + describe 'with a numeric range' do + it "returns a range of numbers" do + expected = (1..10).to_a + expect(scope.function_range([1,10])).to eq expected + end + it "returns a range of numbers with step parameter" do + expected = (1..10).step(2).to_a + expect(scope.function_range([1,10,2])).to eq expected + end + it "works with mixed numeric like strings and numeric arguments" do + expected = (1..10).to_a + expect(scope.function_range(['1',10])).to eq expected + expect(scope.function_range([1,'10'])).to eq expected + end + end end diff --git a/spec/functions/reverse_spec.rb b/spec/functions/reverse_spec.rb index bfeabfb..1f04794 100755 --- a/spec/functions/reverse_spec.rb +++ b/spec/functions/reverse_spec.rb @@ -16,4 +16,13 @@ describe "the reverse function" do result = scope.function_reverse(["asdfghijkl"]) expect(result).to(eq('lkjihgfdsa')) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new('asdfghjkl') + result = scope.function_reverse([value]) + result.should(eq('lkjhgfdsa')) + end end diff --git a/spec/functions/rstrip_spec.rb b/spec/functions/rstrip_spec.rb index 81321d7..f6b4838 100755 --- a/spec/functions/rstrip_spec.rb +++ b/spec/functions/rstrip_spec.rb @@ -21,4 +21,13 @@ describe "the rstrip function" do result = scope.function_rstrip([["a ","b ", "c "]]) expect(result).to(eq(['a','b','c'])) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new('asdf ') + result = scope.function_rstrip([value]) + result.should(eq('asdf')) + end end diff --git a/spec/functions/shuffle_spec.rb b/spec/functions/shuffle_spec.rb index ee0e2ff..a62c1fb 100755 --- a/spec/functions/shuffle_spec.rb +++ b/spec/functions/shuffle_spec.rb @@ -21,4 +21,13 @@ describe "the shuffle function" do result = scope.function_shuffle(["adfs"]) expect(result.split("").sort.join("")).to(eq("adfs")) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new('asdf') + result = scope.function_shuffle([value]) + result.size.should(eq(4)) + end end diff --git a/spec/functions/strip_spec.rb b/spec/functions/strip_spec.rb index e228761..4ac8daf 100755 --- a/spec/functions/strip_spec.rb +++ b/spec/functions/strip_spec.rb @@ -15,4 +15,13 @@ describe "the strip function" do result = scope.function_strip([" ab cd "]) expect(result).to(eq('ab cd')) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new(' as df ') + result = scope.function_strip([value]) + result.should(eq('as df')) + end end diff --git a/spec/functions/swapcase_spec.rb b/spec/functions/swapcase_spec.rb index c6838ab..791d1df 100755 --- a/spec/functions/swapcase_spec.rb +++ b/spec/functions/swapcase_spec.rb @@ -16,4 +16,13 @@ describe "the swapcase function" do result = scope.function_swapcase(["aaBBccDD"]) expect(result).to(eq('AAbbCCdd')) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new("aaBBccDD") + result = scope.function_swapcase([value]) + result.should(eq("AAbbCCdd")) + end end diff --git a/spec/functions/to_bytes_spec.rb b/spec/functions/to_bytes_spec.rb index 68a1eb8..0f6ade9 100755 --- a/spec/functions/to_bytes_spec.rb +++ b/spec/functions/to_bytes_spec.rb @@ -18,6 +18,31 @@ describe "the to_bytes function" do expect(result).to(eq(4096)) end + it "should convert MB to B" do + result = scope.function_to_bytes(["4 MB"]) + expect(result).to(eq(4194304)) + end + + it "should convert GB to B" do + result = scope.function_to_bytes(["4 GB"]) + expect(result).to(eq(4294967296)) + end + + it "should convert TB to B" do + result = scope.function_to_bytes(["4 TB"]) + expect(result).to(eq(4398046511104)) + end + + it "should convert PB to B" do + result = scope.function_to_bytes(["4 PB"]) + expect(result).to(eq(4503599627370496)) + end + + it "should convert PB to B" do + result = scope.function_to_bytes(["4 EB"]) + expect(result).to(eq(4611686018427387904)) + end + it "should work without B in unit" do result = scope.function_to_bytes(["4 k"]) expect(result).to(eq(4096)) diff --git a/spec/functions/type3x_spec.rb b/spec/functions/type3x_spec.rb new file mode 100644 index 0000000..d21236a --- /dev/null +++ b/spec/functions/type3x_spec.rb @@ -0,0 +1,43 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the type3x function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + it "should exist" do + expect(Puppet::Parser::Functions.function("type3x")).to eq("function_type3x") + end + + it "should raise a ParseError if there is less than 1 arguments" do + expect { scope.function_type3x([]) }.to( raise_error(Puppet::ParseError)) + end + + it "should return string when given a string" do + result = scope.function_type3x(["aaabbbbcccc"]) + expect(result).to(eq('string')) + end + + it "should return array when given an array" do + result = scope.function_type3x([["aaabbbbcccc","asdf"]]) + expect(result).to(eq('array')) + end + + it "should return hash when given a hash" do + result = scope.function_type3x([{"a"=>1,"b"=>2}]) + expect(result).to(eq('hash')) + end + + it "should return integer when given an integer" do + result = scope.function_type3x(["1"]) + expect(result).to(eq('integer')) + end + + it "should return float when given a float" do + result = scope.function_type3x(["1.34"]) + expect(result).to(eq('float')) + end + + it "should return boolean when given a boolean" do + result = scope.function_type3x([true]) + expect(result).to(eq('boolean')) + end +end diff --git a/spec/functions/type_spec.rb b/spec/functions/type_spec.rb index 9dfe9d7..b683fcf 100755 --- a/spec/functions/type_spec.rb +++ b/spec/functions/type_spec.rb @@ -7,8 +7,9 @@ describe "the type function" do expect(Puppet::Parser::Functions.function("type")).to eq("function_type") end - it "should raise a ParseError if there is less than 1 arguments" do - expect { scope.function_type([]) }.to( raise_error(Puppet::ParseError)) + it "should give a deprecation warning when called" do + scope.expects(:warning).with("type() DEPRECATED: This function will cease to function on Puppet 4; please use type3x() before upgrading to puppet 4 for backwards-compatibility, or migrate to the new parser's typing system.") + scope.function_type(["aoeu"]) end it "should return string when given a string" do diff --git a/spec/functions/unique_spec.rb b/spec/functions/unique_spec.rb index 8ec1464..7cd3a56 100755 --- a/spec/functions/unique_spec.rb +++ b/spec/functions/unique_spec.rb @@ -21,4 +21,13 @@ describe "the unique function" do result = scope.function_unique([["a","a","b","b","c"]]) expect(result).to(eq(['a','b','c'])) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new('aabbc') + result = scope.function_unique([value]) + result.should(eq('abc')) + end end diff --git a/spec/functions/upcase_spec.rb b/spec/functions/upcase_spec.rb index 78e55dd..0689099 100755 --- a/spec/functions/upcase_spec.rb +++ b/spec/functions/upcase_spec.rb @@ -9,7 +9,7 @@ describe "the upcase function" do end it "should raise a ParseError if there is less than 1 arguments" do - expect { scope.function_upcase([]) }.to( raise_error(Puppet::ParseError)) + expect { scope.function_upcase([]) }.to(raise_error(Puppet::ParseError)) end it "should upcase a string" do @@ -21,4 +21,38 @@ describe "the upcase function" do result = scope.function_upcase(["ABC"]) expect(result).to(eq('ABC')) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new('abc') + result = scope.function_upcase([value]) + result.should(eq('ABC')) + end + + it 'should accept hashes and return uppercase' do + expect( + scope.function_upcase([{'test' => %w(this that and other thing)}]) + ).to eq({'TEST' => %w(THIS THAT AND OTHER THING)}) + end + + if :test.respond_to?(:upcase) + it 'should accept hashes of symbols' do + expect( + scope.function_upcase([{:test => [:this, :that, :other]}]) + ).to eq({:TEST => [:THIS, :THAT, :OTHER]}) + end + it 'should return upcase symbol' do + expect( + scope.function_upcase([:test]) + ).to eq(:TEST) + end + it 'should return mixed objects in upcease' do + expect( + scope.function_upcase([[:test, 'woot']]) + ).to eq([:TEST, 'WOOT']) + + end + end end diff --git a/spec/functions/uriescape_spec.rb b/spec/functions/uriescape_spec.rb index c44e9c1..d0f37de 100755 --- a/spec/functions/uriescape_spec.rb +++ b/spec/functions/uriescape_spec.rb @@ -17,8 +17,24 @@ describe "the uriescape function" do expect(result).to(eq(':/?%23[]@!$&\'()*+,;=%20%22%7B%7D')) end + it "should uriescape an array of strings, while not touching up nonstrings" do + teststring = ":/?#[]@!$&'()*+,;= \"{}" + expectstring = ':/?%23[]@!$&\'()*+,;=%20%22%7B%7D' + result = scope.function_uriescape([[teststring, teststring, 1]]) + expect(result).to(eq([expectstring, expectstring, 1])) + end + it "should do nothing if a string is already safe" do result = scope.function_uriescape(["ABCdef"]) expect(result).to(eq('ABCdef')) end + + it "should accept objects which extend String" do + class AlsoString < String + end + + value = AlsoString.new('abc') + result = scope.function_uriescape([value]) + result.should(eq('abc')) + end end diff --git a/spec/functions/validate_absolute_path_spec.rb b/spec/functions/validate_absolute_path_spec.rb index 342ae84..36c836b 100755 --- a/spec/functions/validate_absolute_path_spec.rb +++ b/spec/functions/validate_absolute_path_spec.rb @@ -39,6 +39,11 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do expect { subject.call [path] }.not_to raise_error end end + valid_paths do + it "validate_absolute_path(#{valid_paths.inspect}) should not fail" do + expect { subject.call [valid_paths] }.not_to raise_error + end + end end context "Puppet without mocking" do @@ -47,6 +52,11 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do expect { subject.call [path] }.not_to raise_error end end + valid_paths do + it "validate_absolute_path(#{valid_paths.inspect}) should not fail" do + expect { subject.call [valid_paths] }.not_to raise_error + end + end end end @@ -55,6 +65,7 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do [ nil, [ nil ], + [ nil, nil ], { 'foo' => 'bar' }, { }, '', @@ -66,19 +77,28 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do end context 'Relative paths' do - %w{ - relative1 - . - .. - ./foo - ../foo - etc/puppetlabs/puppet - opt/puppet/bin - }.each do |path| + def self.rel_paths + %w{ + relative1 + . + .. + ./foo + ../foo + etc/puppetlabs/puppet + opt/puppet/bin + } + end + rel_paths.each do |path| it "validate_absolute_path(#{path.inspect}) should fail" do expect { subject.call [path] }.to raise_error Puppet::ParseError end end + rel_paths do + it "validate_absolute_path(#{rel_paths.inspect}) should fail" do + expect { subject.call [rel_paths] }.to raise_error Puppet::ParseError + end + end end end end + diff --git a/spec/functions/validate_augeas_spec.rb b/spec/functions/validate_augeas_spec.rb index c695ba2..99523ab 100755 --- a/spec/functions/validate_augeas_spec.rb +++ b/spec/functions/validate_augeas_spec.rb @@ -60,7 +60,7 @@ describe Puppet::Parser::Functions.function(:validate_augeas), :if => Puppet.fea end describe "Nicer Error Messages" do - # The intent here is to make sure the function returns the 3rd argument + # The intent here is to make sure the function returns the 4th argument # in the exception thrown inputs = [ [ "root:x:0:0:root\n", 'Passwd.lns', [], 'Failed to validate passwd content' ], @@ -69,7 +69,7 @@ describe Puppet::Parser::Functions.function(:validate_augeas), :if => Puppet.fea inputs.each do |input| it "validate_augeas(#{input.inspect}) should fail" do - expect { subject.call input }.to raise_error /#{input[2]}/ + expect { subject.call input }.to raise_error /#{input[3]}/ end end end diff --git a/spec/functions/validate_cmd_spec.rb b/spec/functions/validate_cmd_spec.rb index a6e68df..7cb9782 100755 --- a/spec/functions/validate_cmd_spec.rb +++ b/spec/functions/validate_cmd_spec.rb @@ -12,37 +12,74 @@ describe Puppet::Parser::Functions.function(:validate_cmd) do scope.method(function_name) end - describe "with an explicit failure message" do - it "prints the failure message on error" do - expect { - subject.call ['', '/bin/false', 'failure message!'] - }.to raise_error Puppet::ParseError, /failure message!/ + context 'with no % placeholder' do + describe "with an explicit failure message" do + it "prints the failure message on error" do + expect { + subject.call ['', '/bin/false', 'failure message!'] + }.to raise_error Puppet::ParseError, /failure message!/ + end end - end - describe "on validation failure" do - it "includes the command error output" do - expect { - subject.call ['', "#{TOUCHEXE} /cant/touch/this"] - }.to raise_error Puppet::ParseError, /(cannot touch|o such file or)/ + describe "on validation failure" do + it "includes the command error output" do + expect { + subject.call ['', "#{TOUCHEXE} /cant/touch/this"] + }.to raise_error Puppet::ParseError, /(cannot touch|o such file or)/ + end + + it "includes the command return value" do + expect { + subject.call ['', '/cant/run/this'] + }.to raise_error Puppet::ParseError, /returned 1\b/ + end end - it "includes the command return value" do - expect { - subject.call ['', '/cant/run/this'] - }.to raise_error Puppet::ParseError, /returned 1\b/ + describe "when performing actual validation" do + it "can positively validate file content" do + expect { subject.call ["non-empty", "#{TESTEXE} -s"] }.to_not raise_error + end + + it "can negatively validate file content" do + expect { + subject.call ["", "#{TESTEXE} -s"] + }.to raise_error Puppet::ParseError, /failed to validate.*test -s/ + end end end - describe "when performing actual validation" do - it "can positively validate file content" do - expect { subject.call ["non-empty", "#{TESTEXE} -s"] }.to_not raise_error + context 'with % placeholder' do + describe "with an explicit failure message" do + it "prints the failure message on error" do + expect { + subject.call ['', '/bin/false % -f', 'failure message!'] + }.to raise_error Puppet::ParseError, /failure message!/ + end end + describe "on validation failure" do + it "includes the command error output" do + expect { + subject.call ['', "#{TOUCHEXE} /cant/touch/this"] + }.to raise_error Puppet::ParseError, /(cannot touch|o such file or)/ + end + + it "includes the command return value" do + expect { + subject.call ['', '/cant/run/this % -z'] + }.to raise_error Puppet::ParseError, /Execution of '\/cant\/run\/this .+ -z' returned 1/ + end + end + + describe "when performing actual validation" do + it "can positively validate file content" do + expect { subject.call ["non-empty", "#{TESTEXE} -s %"] }.to_not raise_error + end - it "can negatively validate file content" do - expect { - subject.call ["", "#{TESTEXE} -s"] - }.to raise_error Puppet::ParseError, /failed to validate.*test -s/ + it "can negatively validate file content" do + expect { + subject.call ["", "#{TESTEXE} -s %"] + }.to raise_error Puppet::ParseError, /failed to validate.*test -s/ + end end end end diff --git a/spec/functions/validate_integer_spec.rb b/spec/functions/validate_integer_spec.rb new file mode 100755 index 0000000..dff3415 --- /dev/null +++ b/spec/functions/validate_integer_spec.rb @@ -0,0 +1,219 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:validate_integer) do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + describe 'when calling validate_integer from puppet without any argument or to many' do + it "should not compile when no argument is passed" do + Puppet[:code] = "validate_integer()" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /Wrong number of arguments/) + end + it "should not compile when more than three arguments are passed" do + Puppet[:code] = "validate_integer(1, 1, 1, 1)" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /Wrong number of arguments/) + end + end + + describe 'when calling validate_integer from puppet only with input' do + %w{ 1 -1 }.each do |the_number| + it "should compile when #{the_number} is an encapsulated integer" do + Puppet[:code] = "validate_integer('#{the_number}')" + scope.compiler.compile + end + it "should compile when #{the_number} is an bare integer" do + Puppet[:code] = "validate_integer(#{the_number})" + scope.compiler.compile + end + end + + %w{ [1,2,3,4,5] ['1','2','3','4','5'] }.each do |the_number| + it "should compile when multiple Integer arguments are passed in an Array" do + Puppet[:code] = "validate_integer(#{the_number})" + scope.compiler.compile + end + end + + %w{ true false iAmAString 1test 7.0 -7.0 }.each do |the_number| + it "should not compile when #{the_number} is in a string" do + Puppet[:code] = "validate_integer('#{the_number}')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be an Integer/) + end + + it "should not compile when #{the_number} is a bare word" do + Puppet[:code] = "validate_integer(#{the_number})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be an Integer/) + end + end + + it "should not compile when an Integer is part of a larger String" do + Puppet[:code] = "validate_integer('1 test')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be an Integer/) + end + + it "should not compile when an Array with a non-Integer value is passed" do + Puppet[:code] = "validate_integer([1, '-7.0'])" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /at array position 1 to be an Integer/) + end + + it "should not compile when a Hash is passed" do + Puppet[:code] = "validate_integer({ 1 => 2 })" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be an Integer or Array/) + end + + it "should not compile when an explicitly undef variable is passed" do + Puppet[:code] = <<-'ENDofPUPPETcode' + $foo = undef + validate_integer($foo) + ENDofPUPPETcode + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be an Integer/) + end + + it "should not compile when an undefined variable is passed" do + Puppet[:code] = <<-'ENDofPUPPETcode' + validate_integer($foobarbazishouldnotexist) + ENDofPUPPETcode + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be an Integer/) + end + end + + describe 'when calling validate_integer from puppet with input and a maximum' do + max = 10 + %w{ 1 -1 }.each do |the_number| + it "should compile when #{the_number} is lower than a maximum of #{max}" do + Puppet[:code] = "validate_integer(#{the_number},#{max})" + scope.compiler.compile + end + end + + it "should compile when an Integer is equal the maximum" do + Puppet[:code] = "validate_integer(#{max},#{max})" + scope.compiler.compile + end + + it "should not compile when #{max+1} is greater than a maximum of #{max}" do + Puppet[:code] = "validate_integer(#{max+1},#{max})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be smaller or equal to/) + end + + %w{ [-10,1,2,3,4,5,10] ['-10','1','2','3','4','5','10'] }.each do |the_number| + it "should compile when each element of #{the_number} is lower than a maximum of #{max}" do + Puppet[:code] = "validate_integer(#{the_number},#{max})" + scope.compiler.compile + end + end + + it "should not compile when an element of an Array [-10,1,2,3,4,5,#{max+1}] is greater than a maximum of #{max}" do + Puppet[:code] = "validate_integer([-10,1,2,3,4,5,#{max+1}],#{max})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be smaller or equal to/) + end + + %w{ true false iAmAString 1test 7.0 -7.0 }.each do |the_max| + it "should not compile when a non-Integer maximum #{the_max}, encapsulated in a String, is passed" do + Puppet[:code] = "validate_integer(1,'#{the_max}')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or an Integer/) + end + + it "should not compile when a non-Integer maximum #{the_max} bare word is passed" do + Puppet[:code] = "validate_integer(1,#{the_max})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or an Integer/) + end + end + + it "should not compile when an explicitly undefined variable is passed as maximum and no minimum is passed" do + Puppet[:code] = <<-'ENDofPUPPETcode' + $foo = undef + validate_integer(10, $foo) + ENDofPUPPETcode + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or an Integer/) + end + it "should not compile when an explicitly undef is passed as maximum and no minimum is passed" do + Puppet[:code] = "validate_integer(10, undef)" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or an Integer/) + end + it "should not compile when an empty string is passed as maximum and no minimum is passed" do + Puppet[:code] = "validate_integer(10, '')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or an Integer/) + end + it "should not compile when an undefined variable for a maximum is passed" do + Puppet[:code] = "validate_integer(10, $foobarbazishouldnotexist)" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or an Integer/) + end + end + + describe 'when calling validate_integer from puppet with input, a maximum and a minimum' do + it "should not compile when a minimum larger than maximum is passed" do + Puppet[:code] = "validate_integer(1,1,2)" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /second argument to be larger than third argument/) + end + + max = 10 + min = -10 + %w{ 1 -1 }.each do |the_number| + it "should compile when each element of #{the_number} is within a range from #{min} to #{max}" do + Puppet[:code] = "validate_integer(#{the_number},#{max},#{min})" + scope.compiler.compile + end + end + + it "should compile when an Integer is equal the minimum" do + Puppet[:code] = "validate_integer(#{min},#{max},#{min})" + scope.compiler.compile + end + + it "should compile when an Integer is equal the minimum and maximum" do + Puppet[:code] = "validate_integer(#{max},#{max},#{max})" + scope.compiler.compile + end + + it "should compile when an empty maximum is passed and the Integer is greater than the minimum" do + Puppet[:code] = "validate_integer(#{max},'',#{min})" + scope.compiler.compile + end + it "should compile when an explicitly undefined maximum is passed and the Integer is greater than the minimum" do + Puppet[:code] = "validate_integer(#{max},undef,#{min})" + scope.compiler.compile + end + it "should compile when an explicitly undefined variable is passed for maximum and the Integer is greater than the minimum" do + Puppet[:code] = <<-"ENDofPUPPETcode" + $foo = undef + validate_integer(#{max}, $foo, #{min}) + ENDofPUPPETcode + scope.compiler.compile + end + it "should not compile when no maximum value is given and the Integer is greater than the minimum" do + Puppet[:code] = "validate_integer(#{max},,#{min})" + expect { scope.compiler.compile }.to raise_error(Puppet::Error, /Syntax error at ','/) + end + + it "should not compile when #{min-1} is lower than a minimum of #{min}" do + Puppet[:code] = "validate_integer(#{min-1},#{max},#{min})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be greater or equal to/) + end + + %w{ [-10,1,2,3,4,5,10] ['-10','1','2','3','4','5','10'] }.each do |the_number| + it "should compile when each element of #{the_number} is within a range from #{min} to #{max}" do + Puppet[:code] = "validate_integer(#{the_number},#{max},#{min})" + scope.compiler.compile + end + end + + it "should not compile when an element of an Array [#{min-1},1,2,3,4,5,10] is lower than a minimum of #{min}" do + Puppet[:code] = "validate_integer([#{min-1},1,2,3,4,5,10],#{max},#{min})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be greater or equal to/) + end + + %w{ true false iAmAString 1test 7.0 -7.0 }.each do |the_min| + it "should not compile when a non-Integer minimum #{the_min}, encapsulated in a String, is passed" do + Puppet[:code] = "validate_integer(1,#{max},'#{the_min}')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or an Integer/) + end + + it "should not compile when a non-Integer minimum #{the_min} bare word is passed" do + Puppet[:code] = "validate_integer(1,#{max},#{the_min})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or an Integer/) + end + end + end +end diff --git a/spec/functions/validate_numeric_spec.rb b/spec/functions/validate_numeric_spec.rb new file mode 100755 index 0000000..c8b0e4d --- /dev/null +++ b/spec/functions/validate_numeric_spec.rb @@ -0,0 +1,217 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:validate_numeric) do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + describe 'when calling validate_numeric from puppet without any argument or to many' do + it "should not compile when no argument is passed" do + Puppet[:code] = "validate_numeric()" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /Wrong number of arguments/) + end + it "should not compile when more than three arguments are passed" do + Puppet[:code] = "validate_numeric(1, 1, 1, 1)" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /Wrong number of arguments/) + end + end + + describe 'when calling validate_numeric from puppet only with input' do + %w{ 1 -1 1.0 -1.0 }.each do |the_number| + it "should compile when #{the_number} is an encapsulated numeric" do + Puppet[:code] = "validate_numeric('#{the_number}')" + scope.compiler.compile + end + it "should compile when #{the_number} is a bare numeric" do + Puppet[:code] = "validate_numeric(#{the_number})" + scope.compiler.compile + end + end + + %w{ [1,2,3,4,5] ['1','2','3','4','5'] [1.1,2.2,3.3,4.4,5.5] ['1.1','2.2','3.3','4.4','5.5'] }.each do |the_number| + it "should compile when multiple Numeric arguments are passed in an Array" do + Puppet[:code] = "validate_numeric(#{the_number})" + scope.compiler.compile + end + end + + %w{ true false iAmAString 1test }.each do |the_number| + it "should not compile when #{the_number} is in a string" do + Puppet[:code] = "validate_numeric('#{the_number}')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be a Numeric/) + end + + it "should not compile when #{the_number} is a bare word" do + Puppet[:code] = "validate_numeric(#{the_number})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be a Numeric/) + end + end + + it "should not compile when a Numeric is part of a larger String" do + Puppet[:code] = "validate_numeric('1.0 test')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be a Numeric/) + end + + it "should not compile when an Array with a non-Numeric value is passed" do + Puppet[:code] = "validate_numeric([1, 'test'])" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /at array position 1 to be a Numeric/) + end + + it "should not compile when a Hash is passed" do + Puppet[:code] = "validate_numeric({ 1 => 2 })" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be a Numeric or Array/) + end + + it "should not compile when an explicitly undef variable is passed" do + Puppet[:code] = <<-'ENDofPUPPETcode' + $foo = undef + validate_numeric($foo) + ENDofPUPPETcode + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be a Numeric/) + end + + it "should not compile when an undefined variable is passed" do + Puppet[:code] = 'validate_numeric($foobarbazishouldnotexist)' + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be a Numeric/) + end + end + + describe 'when calling validate_numeric from puppet with input and a maximum' do + max = 10 + %w{ 1 -1 1.0 -1.0 }.each do |the_number| + it "should compile when #{the_number} is lower than a maximum of #{max}" do + Puppet[:code] = "validate_numeric(#{the_number},#{max})" + scope.compiler.compile + end + end + + it "should compile when a Numeric is equal the maximum" do + Puppet[:code] = "validate_numeric(#{max},#{max})" + scope.compiler.compile + end + + it "should not compile when #{max+1} is greater than a maximum of #{max}" do + Puppet[:code] = "validate_numeric(#{max+1},#{max})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be smaller or equal to/) + end + + %w{ [-10,1,2,3,4,5,10] ['-10','1','2','3','4','5','10'] }.each do |the_number| + it "should compile when each element of #{the_number} is lower than a maximum of #{max}" do + Puppet[:code] = "validate_numeric(#{the_number},#{max})" + scope.compiler.compile + end + end + + it "should not compile when an element of an Array [-10,1,2,3,4,5,#{max+1}] is greater than a maximum of #{max}" do + Puppet[:code] = "validate_numeric([-10,1,2,3,4,5,#{max+1}],#{max})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be smaller or equal to/) + end + + %w{ true false iAmAString 1test }.each do |the_max| + it "should not compile when a non-Numeric maximum #{the_max}, encapsulated in a String, is passed" do + Puppet[:code] = "validate_numeric(1,'#{the_max}')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or a Numeric/) + end + + it "should not compile when a non-Numeric maximum #{the_max} bare word is passed" do + Puppet[:code] = "validate_numeric(1,#{the_max})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or a Numeric/) + end + end + + it "should not compile when an explicitly undefined variable is passed as maximum and no minimum is passed" do + Puppet[:code] = <<-'ENDofPUPPETcode' + $foo = undef + validate_numeric(10, $foo) + ENDofPUPPETcode + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or a Numeric/) + end + it "should not compile when an explicitly undef is passed as maximum and no minimum is passed" do + Puppet[:code] = "validate_numeric(10, undef)" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or a Numeric/) + end + it "should not compile when an empty string is passed as maximum and no minimum is passed" do + Puppet[:code] = "validate_numeric(10, '')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or a Numeric/) + end + it "should not compile when an undefined variable for a maximum is passed" do + Puppet[:code] = "validate_numeric(10, $foobarbazishouldnotexist)" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or a Numeric/) + end + end + + describe 'when calling validate_numeric from puppet with input, a maximum and a minimum' do + it "should not compile when a minimum larger than maximum is passed" do + Puppet[:code] = "validate_numeric(1,1,2)" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /second argument to be larger than third argument/) + end + + max = 10 + min = -10 + %w{ 1 -1 }.each do |the_number| + it "should compile when each element of #{the_number} is within a range from #{min} to #{max}" do + Puppet[:code] = "validate_numeric(#{the_number},#{max},#{min})" + scope.compiler.compile + end + end + + it "should compile when a Numeric is equal the minimum" do + Puppet[:code] = "validate_numeric(#{min},#{max},#{min})" + scope.compiler.compile + end + + it "should compile when a Numeric is equal the minimum and maximum" do + Puppet[:code] = "validate_numeric(#{max},#{max},#{max})" + scope.compiler.compile + end + + it "should compile when an empty maximum is passed and the Numeric is greater than the minimum" do + Puppet[:code] = "validate_numeric(#{max}.1,'',#{min})" + scope.compiler.compile + end + it "should compile when an explicitly undefined maximum is passed and the Numeric is greater than the minimum" do + Puppet[:code] = "validate_numeric(#{max}.1,undef,#{min})" + scope.compiler.compile + end + it "should compile when an explicitly undefined variable is passed for maximum and the Numeric is greater than the minimum" do + Puppet[:code] = <<-"ENDofPUPPETcode" + $foo = undef + validate_numeric(#{max}.1, $foo, #{min}) + ENDofPUPPETcode + scope.compiler.compile + end + it "should not compile when no maximum value is given and the Numeric is greater than the minimum" do + Puppet[:code] = "validate_numeric(#{max}.1,,#{min})" + expect { scope.compiler.compile }.to raise_error(Puppet::Error, /Syntax error at ','/) + end + + it "should not compile when #{min-1} is lower than a minimum of #{min}" do + Puppet[:code] = "validate_numeric(#{min-1.0},#{max},#{min})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be greater or equal to/) + end + + %w{ [-10,1,2,3,4,5,10] ['-10.0','1','2','3','4','5','10.0'] }.each do |the_number| + it "should compile when each element of #{the_number} is within a range from #{min} to #{max}" do + Puppet[:code] = "validate_numeric(#{the_number},#{max},#{min})" + scope.compiler.compile + end + end + + it "should not compile when an element of an Array [#{min-1.1},1,2,3,4,5,10.0] is lower than a minimum of #{min}" do + Puppet[:code] = "validate_numeric([#{min-1},1,2,3,4,5,10],#{max},#{min})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be greater or equal to/) + end + + %w{ true false iAmAString 1test }.each do |the_min| + it "should not compile when a non-Numeric minimum #{the_min}, encapsulated in a String, is passed" do + Puppet[:code] = "validate_numeric(1,#{max},'#{the_min}')" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or a Numeric/) + end + + it "should not compile when a non-Numeric minimum #{the_min} bare word is passed" do + Puppet[:code] = "validate_numeric(1,#{max},#{the_min})" + expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /to be unset or a Numeric/) + end + end + end +end diff --git a/spec/functions/zip_spec.rb b/spec/functions/zip_spec.rb index 744bdd7..f265fce 100755 --- a/spec/functions/zip_spec.rb +++ b/spec/functions/zip_spec.rb @@ -11,5 +11,21 @@ describe "the zip function" do it "should be able to zip an array" do result = scope.function_zip([['1','2','3'],['4','5','6']]) expect(result).to(eq([["1", "4"], ["2", "5"], ["3", "6"]])) + result = scope.function_zip([['1','2','3'],['4','5','6'], false]) + result.should(eq([["1", "4"], ["2", "5"], ["3", "6"]])) + end + + it "should be able to zip an array and flatten" do + result = scope.function_zip([['1','2','3'],['4','5','6'], true]) + result.should(eq(["1", "4", "2", "5", "3", "6"])) + end + + it "should accept objects which extend String for the second argument" do + class AlsoString < String + end + + value = AlsoString.new('false') + result = scope.function_zip([['1','2','3'],['4','5','6'],value]) + result.should(eq([["1", "4"], ["2", "5"], ["3", "6"]])) end end diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb index 53c1661..3203ce9 100755 --- a/spec/spec_helper_acceptance.rb +++ b/spec/spec_helper_acceptance.rb @@ -4,15 +4,23 @@ require 'beaker-rspec' UNSUPPORTED_PLATFORMS = [] unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no' - # This will install the latest available package on el and deb based - # systems fail on windows and osx, and install via gem on other *nixes - foss_opts = { :default_action => 'gem_install' } + foss_opts = { + :default_action => 'gem_install', + :version => (ENV['PUPPET_VERSION'] ? ENV['PUPPET_VERSION'] : '3.7.2'), + } if default.is_pe?; then install_pe; else install_puppet( foss_opts ); end hosts.each do |host| - on host, "mkdir -p #{host['distmoduledir']}" - on host, "/bin/touch #{default['puppetpath']}/hiera.yaml" + if host['platform'] !~ /windows/i + if host.is_pe? + on host, 'mkdir -p /etc/puppetlabs/facter/facts.d' + else + on host, "/bin/touch #{host['puppetpath']}/hiera.yaml" + on host, "mkdir -p #{host['distmoduledir']}" + on host, 'mkdir -p /etc/facter/facts.d' + end + end end end @@ -25,15 +33,18 @@ RSpec.configure do |c| # Configure all nodes in nodeset c.before :suite do - hosts.each do |host| - if host['platform'] !~ /windows/i - copy_root_module_to(host, :source => proj_root, :module_name => 'stdlib') - end - end - hosts.each do |host| - if host['platform'] =~ /windows/i - on host, puppet('plugin download') - end + if ENV['FUTURE_PARSER'] == 'true' + default[:default_apply_opts] ||= {} + default[:default_apply_opts].merge!({:parser => 'future'}) end + + copy_root_module_to(default, :source => proj_root, :module_name => 'stdlib') + end +end + +def is_future_parser_enabled? + if default[:default_apply_opts] + return default[:default_apply_opts][:parser] == 'future' end + return false end diff --git a/spec/unit/puppet/functions/type_of_spec.rb b/spec/unit/puppet/functions/type_of_spec.rb new file mode 100644 index 0000000..8afb624 --- /dev/null +++ b/spec/unit/puppet/functions/type_of_spec.rb @@ -0,0 +1,33 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +if ENV["FUTURE_PARSER"] == 'yes' or Puppet.version >= "4" + require 'puppet/pops' + require 'puppet/loaders' + + describe 'the type_of function' do + before(:all) do + loaders = Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, [File.join(fixtures, "modules")])) + Puppet.push_context({:loaders => loaders}, "test-examples") + end + + after(:all) do + Puppet::Pops::Loaders.clear + Puppet::pop_context() + end + + let(:func) do + # Load the function from the environment modulepath's modules (ie, fixtures) + Puppet.lookup(:loaders).private_environment_loader.load(:function, 'type_of') + end + + it 'gives the type of a string' do + expect(func.call({}, 'hello world')).to be_kind_of(Puppet::Pops::Types::PStringType) + end + + it 'gives the type of an integer' do + expect(func.call({}, 5)).to be_kind_of(Puppet::Pops::Types::PIntegerType) + end + end +end diff --git a/spec/unit/puppet/parser/functions/basename_spec.rb b/spec/unit/puppet/parser/functions/basename_spec.rb new file mode 100755 index 0000000..8a2d0dc --- /dev/null +++ b/spec/unit/puppet/parser/functions/basename_spec.rb @@ -0,0 +1,46 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the basename function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("basename").should == "function_basename" + end + + it "should raise a ParseError if there is less than 1 argument" do + lambda { scope.function_basename([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError if there are more than 2 arguments" do + lambda { scope.function_basename(['a', 'b', 'c']) }.should( raise_error(Puppet::ParseError)) + end + + it "should return basename for an absolute path" do + result = scope.function_basename(['/path/to/a/file.ext']) + result.should(eq('file.ext')) + end + + it "should return basename for a relative path" do + result = scope.function_basename(['path/to/a/file.ext']) + result.should(eq('file.ext')) + end + + it "should strip extention when extension specified (absolute path)" do + result = scope.function_basename(['/path/to/a/file.ext', '.ext']) + result.should(eq('file')) + end + + it "should strip extention when extension specified (relative path)" do + result = scope.function_basename(['path/to/a/file.ext', '.ext']) + result.should(eq('file')) + end + + it "should complain about non-string first argument" do + lambda { scope.function_basename([[]]) }.should( raise_error(Puppet::ParseError)) + end + + it "should complain about non-string second argument" do + lambda { scope.function_basename(['/path/to/a/file.ext', []]) }.should( raise_error(Puppet::ParseError)) + 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 d2a129c..a84fc78 100755 --- a/spec/unit/puppet/provider/file_line/ruby_spec.rb +++ b/spec/unit/puppet/provider/file_line/ruby_spec.rb @@ -46,12 +46,12 @@ describe provider_class do @tmpfile = tmp.path tmp.close! @resource = Puppet::Type::File_line.new( - { - :name => 'foo', - :path => @tmpfile, - :line => 'foo = bar', - :match => '^foo\s*=.*$', - } + { + :name => 'foo', + :path => @tmpfile, + :line => 'foo = bar', + :match => '^foo\s*=.*$', + } ) @provider = provider_class.new(@resource) end @@ -69,11 +69,11 @@ describe provider_class do it 'should replace all lines that matches' do @resource = Puppet::Type::File_line.new( { - :name => 'foo', - :path => @tmpfile, - :line => 'foo = bar', - :match => '^foo\s*=.*$', - :multiple => true + :name => 'foo', + :path => @tmpfile, + :line => 'foo = bar', + :match => '^foo\s*=.*$', + :multiple => true, } ) @provider = provider_class.new(@resource) @@ -89,11 +89,11 @@ describe provider_class do expect { @resource = Puppet::Type::File_line.new( { - :name => 'foo', - :path => @tmpfile, - :line => 'foo = bar', - :match => '^foo\s*=.*$', - :multiple => 'asgadga' + :name => 'foo', + :path => @tmpfile, + :line => 'foo = bar', + :match => '^foo\s*=.*$', + :multiple => 'asgadga', } ) }.to raise_error(Puppet::Error, /Invalid value "asgadga"\. Valid values are true, false\./) @@ -140,7 +140,54 @@ describe provider_class do let :provider do provider_class.new(resource) end - + context 'match and after set' do + shared_context 'resource_create' do + let(:match) { '^foo2$' } + let(:after) { '^foo1$' } + let(:resource) { + Puppet::Type::File_line.new( + { + :name => 'foo', + :path => @tmpfile, + :line => 'inserted = line', + :after => after, + :match => match, + } + ) + } + end + before :each do + File.open(@tmpfile, 'w') do |fh| + fh.write("foo1\nfoo2\nfoo = baz") + end + end + describe 'inserts at match' do + include_context 'resource_create' + it { + provider.create + expect(File.read(@tmpfile).chomp).to eq("foo1\ninserted = line\nfoo = baz") + } + end + describe 'inserts a new line after when no match' do + include_context 'resource_create' do + let(:match) { '^nevergoingtomatch$' } + end + it { + provider.create + expect(File.read(@tmpfile).chomp).to eq("foo1\ninserted = line\nfoo2\nfoo = baz") + } + end + describe 'append to end of file if no match for both after and match' do + include_context 'resource_create' do + let(:match) { '^nevergoingtomatch$' } + let(:after) { '^stillneverafter' } + end + it { + provider.create + expect(File.read(@tmpfile).chomp).to eq("foo1\nfoo2\nfoo = baz\ninserted = line") + } + end + end context 'with one line matching the after expression' do before :each do File.open(@tmpfile, 'w') do |fh| @@ -194,7 +241,12 @@ describe provider_class do @tmpfile = tmp.path tmp.close! @resource = Puppet::Type::File_line.new( - {:name => 'foo', :path => @tmpfile, :line => 'foo', :ensure => 'absent' } + { + :name => 'foo', + :path => @tmpfile, + :line => 'foo', + :ensure => 'absent', + } ) @provider = provider_class.new(@resource) end diff --git a/spec/unit/puppet/type/file_line_spec.rb b/spec/unit/puppet/type/file_line_spec.rb index 9ef49ef..410d0bf 100755 --- a/spec/unit/puppet/type/file_line_spec.rb +++ b/spec/unit/puppet/type/file_line_spec.rb @@ -15,14 +15,14 @@ describe Puppet::Type.type(:file_line) do file_line[:match] = '^foo.*$' expect(file_line[:match]).to eq('^foo.*$') end - it 'should not accept a match regex that does not match the specified line' do + it 'should accept a match regex that does not match the specified line' do expect { Puppet::Type.type(:file_line).new( :name => 'foo', :path => '/my/path', :line => 'foo=bar', :match => '^bar=blah$' - )}.to raise_error(Puppet::Error, /the value must be a regex that matches/) + )}.not_to raise_error end it 'should accept a match regex that does match the specified line' do expect { |