diff options
author | Stig Sandbeck Mathisen <ssm@debian.org> | 2014-09-07 10:14:36 +0200 |
---|---|---|
committer | Stig Sandbeck Mathisen <ssm@debian.org> | 2014-09-07 10:14:36 +0200 |
commit | d4b83be375ac1dead058e091191ee7c7b7c24c8a (patch) | |
tree | dc825687392ae3068de5b764be60c53122d9e02a /spec/unit/functions | |
parent | 229cbb976fe0f70f5f30548b83517b415840f9bb (diff) | |
parent | 1681684857c6e39d60d87b0b3520d8783977ceff (diff) | |
download | puppet-upstream/3.7.0.tar.gz |
Imported Upstream version 3.7.0upstream/3.7.0
Diffstat (limited to 'spec/unit/functions')
-rw-r--r-- | spec/unit/functions/assert_type_spec.rb | 25 | ||||
-rw-r--r-- | spec/unit/functions/each_spec.rb | 111 | ||||
-rw-r--r-- | spec/unit/functions/epp_spec.rb | 155 | ||||
-rw-r--r-- | spec/unit/functions/filter_spec.rb | 131 | ||||
-rw-r--r-- | spec/unit/functions/inline_epp_spec.rb | 97 | ||||
-rw-r--r-- | spec/unit/functions/map_spec.rb | 169 | ||||
-rw-r--r-- | spec/unit/functions/match_spec.rb | 57 | ||||
-rw-r--r-- | spec/unit/functions/reduce_spec.rb | 96 | ||||
-rw-r--r-- | spec/unit/functions/slice_spec.rb | 148 | ||||
-rw-r--r-- | spec/unit/functions/with_spec.rb | 35 |
10 files changed, 1021 insertions, 3 deletions
diff --git a/spec/unit/functions/assert_type_spec.rb b/spec/unit/functions/assert_type_spec.rb index d47f47e30..13b353401 100644 --- a/spec/unit/functions/assert_type_spec.rb +++ b/spec/unit/functions/assert_type_spec.rb @@ -37,8 +37,8 @@ describe 'the assert_type function' do end.to raise_error(ArgumentError, Regexp.new(Regexp.escape( "function 'assert_type' called with mis-matched arguments expected one of: - assert_type(Type type, Optional[Object] value) - arg count {2} - assert_type(String type_string, Optional[Object] value) - arg count {2} + assert_type(Type type, Any value, Callable[Type, Type] block {0,1}) - arg count {2,3} + assert_type(String type_string, Any value, Callable[Type, Type] block {0,1}) - arg count {2,3} actual: assert_type(Integer, Integer) - arg count {2}"))) end @@ -46,7 +46,13 @@ actual: it 'allows the second arg to be undef/nil)' do expect do func.call({}, optional(String), nil) - end.to_not raise_error(ArgumentError) + end.to_not raise_error + end + + it 'can be called with a callable that receives a specific type' do + expected, actual = func.call({}, optional(String), 1, create_callable_2_args_unit) + expect(expected.to_s).to eql('Optional[String]') + expect(actual.to_s).to eql('Integer[1, 1]') end def optional(type_ref) @@ -56,4 +62,17 @@ actual: def type(type_ref) Puppet::Pops::Types::TypeFactory.type_of(type_ref) end + + def create_callable_2_args_unit() + Puppet::Functions.create_function(:func) do + dispatch :func do + param 'Type', 'expected' + param 'Type', 'actual' + end + + def func(expected, actual) + [expected, actual] + end + end.new({}, nil) + end end diff --git a/spec/unit/functions/each_spec.rb b/spec/unit/functions/each_spec.rb new file mode 100644 index 000000000..d6651cf5a --- /dev/null +++ b/spec/unit/functions/each_spec.rb @@ -0,0 +1,111 @@ +require 'puppet' +require 'spec_helper' +require 'puppet_spec/compiler' + +require 'shared_behaviours/iterative_functions' + +describe 'the each method' do + include PuppetSpec::Compiler + + before :each do + Puppet[:parser] = 'future' + end + + context "should be callable as" do + it 'each on an array selecting each value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1,2,3] + $a.each |$v| { + file { "/file_$v": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_1")['ensure'].should == 'present' + catalog.resource(:file, "/file_2")['ensure'].should == 'present' + catalog.resource(:file, "/file_3")['ensure'].should == 'present' + end + + it 'each on an array selecting each value - function call style' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1,2,3] + each ($a) |$index, $v| { + file { "/file_$v": ensure => present } + } + MANIFEST + + catalog.resource(:file, "/file_1")['ensure'].should == 'present' + catalog.resource(:file, "/file_2")['ensure'].should == 'present' + catalog.resource(:file, "/file_3")['ensure'].should == 'present' + end + + it 'each on an array with index' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [present, absent, present] + $a.each |$k,$v| { + file { "/file_${$k+1}": ensure => $v } + } + MANIFEST + + catalog.resource(:file, "/file_1")['ensure'].should == 'present' + catalog.resource(:file, "/file_2")['ensure'].should == 'absent' + catalog.resource(:file, "/file_3")['ensure'].should == 'present' + end + + it 'each on a hash selecting entries' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>'present','b'=>'absent','c'=>'present'} + $a.each |$e| { + file { "/file_${e[0]}": ensure => $e[1] } + } + MANIFEST + + catalog.resource(:file, "/file_a")['ensure'].should == 'present' + catalog.resource(:file, "/file_b")['ensure'].should == 'absent' + catalog.resource(:file, "/file_c")['ensure'].should == 'present' + end + + it 'each on a hash selecting key and value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>present,'b'=>absent,'c'=>present} + $a.each |$k, $v| { + file { "/file_$k": ensure => $v } + } + MANIFEST + + catalog.resource(:file, "/file_a")['ensure'].should == 'present' + catalog.resource(:file, "/file_b")['ensure'].should == 'absent' + catalog.resource(:file, "/file_c")['ensure'].should == 'present' + end + + it 'each on a hash selecting key and value (using captures-last parameter)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>present,'b'=>absent,'c'=>present} + $a.each |*$kv| { + file { "/file_${kv[0]}": ensure => $kv[1] } + } + MANIFEST + + catalog.resource(:file, "/file_a")['ensure'].should == 'present' + catalog.resource(:file, "/file_b")['ensure'].should == 'absent' + catalog.resource(:file, "/file_c")['ensure'].should == 'present' + end + end + + context "should produce receiver" do + it 'each checking produced value using single expression' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1, 3, 2] + $b = $a.each |$x| { "unwanted" } + file { "/file_${b[1]}": + ensure => present + } + MANIFEST + + catalog.resource(:file, "/file_3")['ensure'].should == 'present' + end + + end + it_should_behave_like 'all iterative functions argument checks', 'each' + it_should_behave_like 'all iterative functions hash handling', 'each' + +end diff --git a/spec/unit/functions/epp_spec.rb b/spec/unit/functions/epp_spec.rb new file mode 100644 index 000000000..382fd9548 --- /dev/null +++ b/spec/unit/functions/epp_spec.rb @@ -0,0 +1,155 @@ + +require 'spec_helper' + +describe "the epp function" do + include PuppetSpec::Files + + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + before :each do + Puppet[:parser] = 'future' + 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 + + context "when accessing scope variables as $ variables" do + it "looks up the value from the scope" do + scope["what"] = "are belong" + eval_template("all your base <%= $what %> to us").should == "all your base are belong to us" + end + + it "get nil accessing a variable that does not exist" do + eval_template("<%= $kryptonite == undef %>").should == "true" + end + + it "get nil accessing a variable that is undef" do + scope['undef_var'] = nil + eval_template("<%= $undef_var == undef %>").should == "true" + end + + it "gets shadowed variable if args are given" do + scope['phantom'] = 'of the opera' + eval_template_with_args("<%= $phantom == dragos %>", 'phantom' => 'dragos').should == "true" + end + + it "can use values from the enclosing scope for defaults" do + scope['phantom'] = 'of the opera' + eval_template("<%- |$phantom = $phantom| -%><%= $phantom %>").should == "of the opera" + end + + it "uses the default value if the given value is undef/nil" do + eval_template_with_args("<%- |$phantom = 'inside your mind'| -%><%= $phantom %>", 'phantom' => nil).should == "inside your mind" + end + + it "gets shadowed variable if args are given and parameters are specified" do + scope['x'] = 'wrong one' + eval_template_with_args("<%- |$x| -%><%= $x == correct %>", 'x' => 'correct').should == "true" + end + + it "raises an error if required variable is not given" do + scope['x'] = 'wrong one' + expect do + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'y' => 'correct') + end.to raise_error(/no value given for required parameters x/) + end + + it "raises an error if too many arguments are given" do + scope['x'] = 'wrong one' + expect do + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus') + end.to raise_error(/Too many arguments: 2 for 1/) + end + end + + context "when given an empty template" do + it "allows the template file to be empty" do + expect(eval_template("")).to eq("") + end + + it "allows the template to have empty body after parameters" do + expect(eval_template_with_args("<%-|$x|%>", 'x'=>1)).to eq("") + end + end + + context "when using typed parameters" do + it "allows a passed value that matches the parameter's type" do + expect(eval_template_with_args("<%-|String $x|-%><%= $x == correct %>", 'x' => 'correct')).to eq("true") + end + + it "does not allow slurped parameters" do + expect do + eval_template_with_args("<%-|*$x|-%><%= $x %>", 'x' => 'incorrect') + end.to raise_error(/'captures rest' - not supported in an Epp Template/) + end + + it "raises an error when the passed value does not match the parameter's type" do + expect do + eval_template_with_args("<%-|Integer $x|-%><%= $x %>", 'x' => 'incorrect') + end.to raise_error(/expected.*Integer.*actual.*String/m) + end + + it "raises an error when the default value does not match the parameter's type" do + expect do + eval_template("<%-|Integer $x = 'nope'|-%><%= $x %>") + end.to raise_error(/expected.*Integer.*actual.*String/m) + end + + it "allows an parameter to default to undef" do + expect(eval_template("<%-|Optional[Integer] $x = undef|-%><%= $x == undef %>")).to eq("true") + end + end + + + # although never a problem with epp + it "is not interfered with by having a variable named 'string' (#14093)" do + scope['string'] = "this output should not be seen" + eval_template("some text that is static").should == "some text that is static" + end + + it "has access to a variable named 'string' (#14093)" do + scope['string'] = "the string value" + eval_template("string was: <%= $string %>").should == "string was: the string value" + end + + describe 'when loading from modules' do + include PuppetSpec::Files + it 'an epp template is found' do + modules_dir = dir_containing('modules', { + 'testmodule' => { + 'templates' => { + 'the_x.epp' => 'The x is <%= $x %>' + } + }}) + Puppet.override({:current_environment => (env = Puppet::Node::Environment.create(:testload, [ modules_dir ]))}, "test") do + node.environment = env + expect(epp_function.call(scope, 'testmodule/the_x.epp', { 'x' => '3'} )).to eql("The x is 3") + end + end + end + + def eval_template_with_args(content, args_hash) + file_path = tmpdir('epp_spec_content') + filename = File.join(file_path, "template.epp") + File.open(filename, "w+") { |f| f.write(content) } + + Puppet::Parser::Files.stubs(:find_template).returns(filename) + epp_function.call(scope, 'template', args_hash) + end + + def eval_template(content) + file_path = tmpdir('epp_spec_content') + filename = File.join(file_path, "template.epp") + File.open(filename, "w+") { |f| f.write(content) } + + Puppet::Parser::Files.stubs(:find_template).returns(filename) + epp_function.call(scope, 'template') + end + + def epp_function() + epp_func = scope.compiler.loaders.public_environment_loader.load(:function, 'epp') + end +end diff --git a/spec/unit/functions/filter_spec.rb b/spec/unit/functions/filter_spec.rb new file mode 100644 index 000000000..e904c6751 --- /dev/null +++ b/spec/unit/functions/filter_spec.rb @@ -0,0 +1,131 @@ +require 'puppet' +require 'spec_helper' +require 'puppet_spec/compiler' +require 'matchers/resource' + +require 'shared_behaviours/iterative_functions' + +describe 'the filter method' do + include PuppetSpec::Compiler + include Matchers::Resource + + before :each do + Puppet[:parser] = 'future' + end + + it 'should filter on an array (all berries)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = ['strawberry','blueberry','orange'] + $a.filter |$x|{ $x =~ /berry$/}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_blueberry]").with_parameter(:ensure, 'present') + end + + it 'should filter on enumerable type (Integer)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[1,10] + $a.filter |$x|{ $x % 3 == 0}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_9]").with_parameter(:ensure, 'present') + end + + it 'should filter on enumerable type (Integer) using two args index/value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[10,18] + $a.filter |$i, $x|{ $i % 3 == 0}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_10]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_13]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_16]").with_parameter(:ensure, 'present') + end + + it 'should produce an array when acting on an array' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = ['strawberry','blueberry','orange'] + $b = $a.filter |$x|{ $x =~ /berry$/} + file { "/file_${b[0]}": ensure => present } + file { "/file_${b[1]}": ensure => present } + MANIFEST + + expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_blueberry]").with_parameter(:ensure, 'present') + end + + it 'can filter array using index and value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = ['strawberry','blueberry','orange'] + $b = $a.filter |$index, $x|{ $index == 0 or $index ==2} + file { "/file_${b[0]}": ensure => present } + file { "/file_${b[1]}": ensure => present } + MANIFEST + + expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_orange]").with_parameter(:ensure, 'present') + end + + it 'can filter array using index and value (using captures-rest)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = ['strawberry','blueberry','orange'] + $b = $a.filter |*$ix|{ $ix[0] == 0 or $ix[0] ==2} + file { "/file_${b[0]}": ensure => present } + file { "/file_${b[1]}": ensure => present } + MANIFEST + + expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_orange]").with_parameter(:ensure, 'present') + end + + it 'filters on a hash (all berries) by key' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'} + $a.filter |$x|{ $x[0] =~ /berry$/}.each |$v|{ + file { "/file_${v[0]}": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_blueberry]").with_parameter(:ensure, 'present') + end + + it 'should produce a hash when acting on a hash' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'} + $b = $a.filter |$x|{ $x[0] =~ /berry$/} + file { "/file_${b['strawberry']}": ensure => present } + file { "/file_${b['blueberry']}": ensure => present } + file { "/file_${b['orange']}": ensure => present } + + MANIFEST + + expect(catalog).to have_resource("File[/file_red]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_blue]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_]").with_parameter(:ensure, 'present') + end + + it 'filters on a hash (all berries) by value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'strawb'=>'red berry','blueb'=>'blue berry','orange'=>'orange fruit'} + $a.filter |$x|{ $x[1] =~ /berry$/}.each |$v|{ + file { "/file_${v[0]}": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_strawb]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_blueb]").with_parameter(:ensure, 'present') + end + + it_should_behave_like 'all iterative functions argument checks', 'filter' + it_should_behave_like 'all iterative functions hash handling', 'filter' +end diff --git a/spec/unit/functions/inline_epp_spec.rb b/spec/unit/functions/inline_epp_spec.rb new file mode 100644 index 000000000..36328c2dc --- /dev/null +++ b/spec/unit/functions/inline_epp_spec.rb @@ -0,0 +1,97 @@ + +require 'spec_helper' + +describe "the inline_epp function" do + include PuppetSpec::Files + + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + before :each do + Puppet[:parser] = 'future' + 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 + + context "when accessing scope variables as $ variables" do + it "looks up the value from the scope" do + scope["what"] = "are belong" + eval_template("all your base <%= $what %> to us").should == "all your base are belong to us" + end + + it "get nil accessing a variable that does not exist" do + eval_template("<%= $kryptonite == undef %>").should == "true" + end + + it "get nil accessing a variable that is undef" do + scope['undef_var'] = :undef + eval_template("<%= $undef_var == undef %>").should == "true" + end + + it "gets shadowed variable if args are given" do + scope['phantom'] = 'of the opera' + eval_template_with_args("<%= $phantom == dragos %>", 'phantom' => 'dragos').should == "true" + end + + it "gets shadowed variable if args are given and parameters are specified" do + scope['x'] = 'wrong one' + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct').should == "true" + end + + it "raises an error if required variable is not given" do + scope['x'] = 'wrong one' + expect { + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'y' => 'correct') + }.to raise_error(/no value given for required parameters x/) + end + + it "raises an error if too many arguments are given" do + scope['x'] = 'wrong one' + expect { + eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus') + }.to raise_error(/Too many arguments: 2 for 1/) + end + end + + context "when given an empty template" do + it "allows the template file to be empty" do + expect(eval_template("")).to eq("") + end + + it "allows the template to have empty body after parameters" do + expect(eval_template_with_args("<%-|$x|%>", 'x'=>1)).to eq("") + end + end + + it "renders a block expression" do + eval_template_with_args("<%= { $y = $x $x + 1} %>", 'x' => 2).should == "3" + end + + # although never a problem with epp + it "is not interfered with by having a variable named 'string' (#14093)" do + scope['string'] = "this output should not be seen" + eval_template("some text that is static").should == "some text that is static" + end + + it "has access to a variable named 'string' (#14093)" do + scope['string'] = "the string value" + eval_template("string was: <%= $string %>").should == "string was: the string value" + end + + + def eval_template_with_args(content, args_hash) + epp_function.call(scope, content, args_hash) + end + + def eval_template(content) + epp_function.call(scope, content) + end + + def epp_function() + epp_func = scope.compiler.loaders.public_environment_loader.load(:function, 'inline_epp') + end + +end diff --git a/spec/unit/functions/map_spec.rb b/spec/unit/functions/map_spec.rb new file mode 100644 index 000000000..e1b09cf24 --- /dev/null +++ b/spec/unit/functions/map_spec.rb @@ -0,0 +1,169 @@ +require 'puppet' +require 'spec_helper' +require 'puppet_spec/compiler' +require 'matchers/resource' + +require 'shared_behaviours/iterative_functions' + +describe 'the map method' do + include PuppetSpec::Compiler + include Matchers::Resource + + before :each do + Puppet[:parser] = "future" + end + + context "using future parser" do + it 'map on an array (multiplying each value by 2)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1,2,3] + $a.map |$x|{ $x*2}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_4]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present') + end + + it 'map on an enumerable type (multiplying each value by 2)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[1,3] + $a.map |$x|{ $x*2}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_4]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present') + end + + it 'map on an integer (multiply each by 3)' do + catalog = compile_to_catalog(<<-MANIFEST) + 3.map |$x|{ $x*3}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_0]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present') + end + + it 'map on a string' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {a=>x, b=>y} + "ab".map |$x|{$a[$x]}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_x]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_y]").with_parameter(:ensure, 'present') + end + + it 'map on an array (multiplying value by 10 in even index position)' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1,2,3] + $a.map |$i, $x|{ if $i % 2 == 0 {$x} else {$x*10}}.each |$v|{ + file { "/file_$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_20]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present') + end + + it 'map on a hash selecting keys' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>1,'b'=>2,'c'=>3} + $a.map |$x|{ $x[0]}.each |$k|{ + file { "/file_$k": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_a]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_b]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_c]").with_parameter(:ensure, 'present') + end + + it 'map on a hash selecting keys - using two block parameters' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>1,'b'=>2,'c'=>3} + $a.map |$k,$v|{ file { "/file_$k": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_a]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_b]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_c]").with_parameter(:ensure, 'present') + end + + it 'map on a hash using captures-last parameter' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>present,'b'=>absent,'c'=>present} + $a.map |*$kv|{ file { "/file_${kv[0]}": ensure => $kv[1] } } + MANIFEST + + expect(catalog).to have_resource("File[/file_a]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_b]").with_parameter(:ensure, 'absent') + expect(catalog).to have_resource("File[/file_c]").with_parameter(:ensure, 'present') + end + + it 'each on a hash selecting value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>1,'b'=>2,'c'=>3} + $a.map |$x|{ $x[1]}.each |$k|{ file { "/file_$k": ensure => present } } + MANIFEST + + expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present') + end + + it 'each on a hash selecting value - using two block parameters' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {'a'=>1,'b'=>2,'c'=>3} + $a.map |$k,$v|{ file { "/file_$v": ensure => present } } + MANIFEST + + expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present') + end + + context "handles data type corner cases" do + it "map gets values that are false" do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [false,false] + $a.map |$x| { $x }.each |$i, $v| { + file { "/file_$i.$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_0.false]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_1.false]").with_parameter(:ensure, 'present') + end + + it "map gets values that are nil" do + Puppet::Parser::Functions.newfunction(:nil_array, :type => :rvalue) do |args| + [nil] + end + catalog = compile_to_catalog(<<-MANIFEST) + $a = nil_array() + $a.map |$x| { $x }.each |$i, $v| { + file { "/file_$i.$v": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_0.]").with_parameter(:ensure, 'present') + end + end + + it_should_behave_like 'all iterative functions argument checks', 'map' + it_should_behave_like 'all iterative functions hash handling', 'map' + end +end diff --git a/spec/unit/functions/match_spec.rb b/spec/unit/functions/match_spec.rb new file mode 100644 index 000000000..f4e2e383b --- /dev/null +++ b/spec/unit/functions/match_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' +require 'puppet/pops' +require 'puppet/loaders' + +describe 'the match function' do + + before(:all) do + loaders = Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, [])) + Puppet.push_context({:loaders => loaders}, "test-examples") + end + + after(:all) do + Puppet::Pops::Loaders.clear + Puppet::pop_context() + end + + let(:func) do + Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'match') + end + + let(:type_parser) { Puppet::Pops::Types::TypeParser.new } + + + it 'matches string and regular expression without captures' do + expect(func.call({}, 'abc123', /[a-z]+[1-9]+/)).to eql(['abc123']) + end + + it 'matches string and regular expression with captures' do + expect(func.call({}, 'abc123', /([a-z]+)([1-9]+)/)).to eql(['abc123', 'abc', '123']) + end + + it 'produces nil if match is not found' do + expect(func.call({}, 'abc123', /([x]+)([6]+)/)).to be_nil + end + + [ 'Pattern[/([a-z]+)([1-9]+)/]', # regexp + 'Pattern["([a-z]+)([1-9]+)"]', # string + 'Regexp[/([a-z]+)([1-9]+)/]', # regexp type + 'Pattern[/x9/, /([a-z]+)([1-9]+)/]', # regexp, first found matches + ].each do |pattern| + it "matches string and type #{pattern} with captures" do + expect(func.call({}, 'abc123', type(pattern))).to eql(['abc123', 'abc', '123']) + end + end + + it 'matches an array of strings and yields a map of the result' do + expect(func.call({}, ['abc123', '2a', 'xyz2'], /([a-z]+)[1-9]+/)).to eql([['abc123', 'abc'], nil, ['xyz2', 'xyz']]) + end + + it 'raises error if Regexp type without regexp is used' do + expect{func.call({}, 'abc123', type('Regexp'))}.to raise_error(ArgumentError, /Given Regexp Type has no regular expression/) + end + + def type(s) + Puppet::Pops::Types::TypeParser.new.parse(s) + end +end diff --git a/spec/unit/functions/reduce_spec.rb b/spec/unit/functions/reduce_spec.rb new file mode 100644 index 000000000..032f6ccc4 --- /dev/null +++ b/spec/unit/functions/reduce_spec.rb @@ -0,0 +1,96 @@ +require 'puppet' +require 'spec_helper' +require 'puppet_spec/compiler' +require 'matchers/resource' +require 'shared_behaviours/iterative_functions' + +describe 'the reduce method' do + include PuppetSpec::Compiler + include Matchers::Resource + + before :all do + # enable switching back + @saved_parser = Puppet[:parser] + # These tests only work with future parser + end + after :all do + # switch back to original + Puppet[:parser] = @saved_parser + end + + before :each do + node = Puppet::Node.new("floppy", :environment => 'production') + @compiler = Puppet::Parser::Compiler.new(node) + @scope = Puppet::Parser::Scope.new(@compiler) + @topscope = @scope.compiler.topscope + @scope.parent = @topscope + Puppet[:parser] = 'future' + end + + context "should be callable as" do + it 'reduce on an array' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1,2,3] + $b = $a.reduce |$memo, $x| { $memo + $x } + file { "/file_$b": ensure => present } + MANIFEST + + expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present') + end + + it 'reduce on an array with captures rest in lambda' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1,2,3] + $b = $a.reduce |*$mx| { $mx[0] + $mx[1] } + file { "/file_$b": ensure => present } + MANIFEST + + expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present') + end + + it 'reduce on enumerable type' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[1,3] + $b = $a.reduce |$memo, $x| { $memo + $x } + file { "/file_$b": ensure => present } + MANIFEST + + expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present') + end + + it 'reduce on an array with start value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1,2,3] + $b = $a.reduce(4) |$memo, $x| { $memo + $x } + file { "/file_$b": ensure => present } + MANIFEST + + expect(catalog).to have_resource("File[/file_10]").with_parameter(:ensure, 'present') + end + + it 'reduce on a hash' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {a=>1, b=>2, c=>3} + $start = [ignored, 4] + $b = $a.reduce |$memo, $x| {['sum', $memo[1] + $x[1]] } + file { "/file_${$b[0]}_${$b[1]}": ensure => present } + MANIFEST + + expect(catalog).to have_resource("File[/file_sum_6]").with_parameter(:ensure, 'present') + end + + it 'reduce on a hash with start value' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {a=>1, b=>2, c=>3} + $start = ['ignored', 4] + $b = $a.reduce($start) |$memo, $x| { ['sum', $memo[1] + $x[1]] } + file { "/file_${$b[0]}_${$b[1]}": ensure => present } + MANIFEST + + expect(catalog).to have_resource("File[/file_sum_10]").with_parameter(:ensure, 'present') + end + end + + it_should_behave_like 'all iterative functions argument checks', 'reduce' + +end diff --git a/spec/unit/functions/slice_spec.rb b/spec/unit/functions/slice_spec.rb new file mode 100644 index 000000000..945cae5c7 --- /dev/null +++ b/spec/unit/functions/slice_spec.rb @@ -0,0 +1,148 @@ +require 'puppet' +require 'spec_helper' +require 'puppet_spec/compiler' +require 'matchers/resource' + +describe 'methods' do + include PuppetSpec::Compiler + include Matchers::Resource + + before :all do + # enable switching back + @saved_parser = Puppet[:parser] + # These tests only work with future parser + Puppet[:parser] = 'future' + end + after :all do + # switch back to original + Puppet[:parser] = @saved_parser + end + + before :each do + node = Puppet::Node.new("floppy", :environment => 'production') + @compiler = Puppet::Parser::Compiler.new(node) + @scope = Puppet::Parser::Scope.new(@compiler) + @topscope = @scope.compiler.topscope + @scope.parent = @topscope + Puppet[:parser] = 'future' + end + + context "should be callable on array as" do + + it 'slice with explicit parameters' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1, present, 2, absent, 3, present] + $a.slice(2) |$k,$v| { + file { "/file_${$k}": ensure => $v } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent') + expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present') + end + + it 'slice with captures last' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1, present, 2, absent, 3, present] + $a.slice(2) |*$kv| { + file { "/file_${$kv[0]}": ensure => $kv[1] } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent') + expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present') + end + + it 'slice with one parameter' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1, present, 2, absent, 3, present] + $a.slice(2) |$k| { + file { "/file_${$k[0]}": ensure => $k[1] } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent') + expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present') + end + + it 'slice with shorter last slice' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1, present, 2, present, 3, absent] + $a.slice(4) |$a, $b, $c, $d| { + file { "/file_$a.$c": ensure => $b } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_1.2]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_3.]").with_parameter(:ensure, 'absent') + end + end + + context "should be callable on hash as" do + it 'slice with explicit parameters, missing are empty' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = {1=>present, 2=>present, 3=>absent} + $a.slice(2) |$a,$b| { + file { "/file_${a[0]}.${b[0]}": ensure => $a[1] } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_1.2]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_3.]").with_parameter(:ensure, 'absent') + end + end + + context "should be callable on enumerable types as" do + it 'slice with integer range' do + catalog = compile_to_catalog(<<-MANIFEST) + $a = Integer[1,4] + $a.slice(2) |$a,$b| { + file { "/file_${a}.${b}": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_1.2]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_3.4]").with_parameter(:ensure, 'present') + end + + it 'slice with integer' do + catalog = compile_to_catalog(<<-MANIFEST) + 4.slice(2) |$a,$b| { + file { "/file_${a}.${b}": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_0.1]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_2.3]").with_parameter(:ensure, 'present') + end + + it 'slice with string' do + catalog = compile_to_catalog(<<-MANIFEST) + 'abcd'.slice(2) |$a,$b| { + file { "/file_${a}.${b}": ensure => present } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_a.b]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_c.d]").with_parameter(:ensure, 'present') + end + end + + context "when called without a block" do + it "should produce an array with the result" do + catalog = compile_to_catalog(<<-MANIFEST) + $a = [1, present, 2, absent, 3, present] + $a.slice(2).each |$k| { + file { "/file_${$k[0]}": ensure => $k[1] } + } + MANIFEST + + expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present') + expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent') + expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present') + end + end +end diff --git a/spec/unit/functions/with_spec.rb b/spec/unit/functions/with_spec.rb new file mode 100644 index 000000000..952b14412 --- /dev/null +++ b/spec/unit/functions/with_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +require 'puppet_spec/compiler' +require 'matchers/resource' + +describe 'the with function' do + include PuppetSpec::Compiler + include Matchers::Resource + + before :each do + Puppet[:parser] = 'future' + end + + it 'calls a lambda passing no arguments' do + expect(compile_to_catalog("with() || { notify { testing: } }")).to have_resource('Notify[testing]') + end + + it 'calls a lambda passing a single argument' do + expect(compile_to_catalog('with(1) |$x| { notify { "testing$x": } }')).to have_resource('Notify[testing1]') + end + + it 'calls a lambda passing more than one argument' do + expect(compile_to_catalog('with(1, 2) |*$x| { notify { "testing${x[0]}, ${x[1]}": } }')).to have_resource('Notify[testing1, 2]') + end + + it 'passes a type reference to a lambda' do + expect(compile_to_catalog('notify { test: message => "data" } with(Notify[test]) |$x| { notify { "${x[message]}": } }')).to have_resource('Notify[data]') + end + + it 'errors when not given enough arguments for the lambda' do + expect do + compile_to_catalog('with(1) |$x, $y| { }') + end.to raise_error(/Parameter \$y is required but no value was given/m) + end +end |