diff options
| author | Luke Kanies <luke@madstop.com> | 2008-10-17 09:01:04 -0500 |
|---|---|---|
| committer | Luke Kanies <luke@madstop.com> | 2008-10-17 09:01:04 -0500 |
| commit | 8aee40de69e6fe8d67ab58a2e223443b15820584 (patch) | |
| tree | 89e230df3b43302a542f2cb6869f63e2fb93f6d8 /spec | |
| parent | 1b517d2fb048603bd1743a662bde74e8ae4b13dc (diff) | |
| parent | a74ec60d33dee1c592ec858faeccc23d7a7b79f3 (diff) | |
| download | puppet-8aee40de69e6fe8d67ab58a2e223443b15820584.tar.gz puppet-8aee40de69e6fe8d67ab58a2e223443b15820584.tar.xz puppet-8aee40de69e6fe8d67ab58a2e223443b15820584.zip | |
Merge branch '0.24.x' Removed the 'after' blocks that call Type.clear,
since that method is deprecated.
Conflicts:
CHANGELOG
bin/puppetca
lib/puppet/file_serving/fileset.rb
lib/puppet/network/xmlrpc/client.rb
lib/puppet/type/file/selcontext.rb
spec/unit/file_serving/metadata.rb
spec/unit/type/file.rb
Diffstat (limited to 'spec')
46 files changed, 2915 insertions, 146 deletions
diff --git a/spec/integration/provider/package.rb b/spec/integration/provider/package.rb new file mode 100755 index 000000000..24a196266 --- /dev/null +++ b/spec/integration/provider/package.rb @@ -0,0 +1,26 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +describe "Package Provider" do + Puppet::Type.type(:package).providers.each do |name| + provider = Puppet::Type.type(:package).provider(name) + + describe name do + confine "Provider %s is not suitable" % name => provider.suitable? + + it "should fail when asked to install an invalid package" do + pending("This test hangs forever with recent versions of RubyGems") if provider.name == :gem + pkg = Puppet::Type.newpackage :name => "nosuch%s" % provider.name.to_s, :provider => provider.name + lambda { pkg.provider.install }.should raise_error + end + + it "should be able to get a list of existing packages" do + provider.instances.each do |package| + package.should be_instance_of(provider) + package.properties[:provider].should == provider.name + end + end + end + end +end diff --git a/spec/integration/reference/providers.rb b/spec/integration/reference/providers.rb new file mode 100755 index 000000000..79b6ce12d --- /dev/null +++ b/spec/integration/reference/providers.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/reference' + +reference = Puppet::Util::Reference.reference(:providers) + +describe reference do + it "should exist" do + reference.should_not be_nil + end + + it "should be able to be rendered as text" do + lambda { reference.to_text }.should_not raise_error + end +end diff --git a/spec/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb b/spec/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb index 0b4a5abdb..27cd6b3df 100644 --- a/spec/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb +++ b/spec/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb @@ -15,7 +15,10 @@ module Spec prepare success = true example_groups.each do |example_group| - next unless example_group.runnable? + unless example_group.runnable? + warn "Skipping unsuitable example group %s: %s" % [example_group.description, example_group.messages.join(", ")] + next + end success = success & example_group.run end return success diff --git a/spec/monkey_patches/alias_should_to_must.rb b/spec/monkey_patches/alias_should_to_must.rb new file mode 100644 index 000000000..35d3342f2 --- /dev/null +++ b/spec/monkey_patches/alias_should_to_must.rb @@ -0,0 +1,5 @@ +class Object + # This is necessary because the RAL has a 'should' + # method. + alias :must :should +end diff --git a/spec/unit/file_serving/fileset.rb b/spec/unit/file_serving/fileset.rb index f95271050..2100b30f7 100755 --- a/spec/unit/file_serving/fileset.rb +++ b/spec/unit/file_serving/fileset.rb @@ -183,6 +183,15 @@ describe Puppet::FileServing::Fileset, " when recursing" do @fileset.links = :manage @fileset.files.sort.should == @files.sort end + + it "should succeed when paths have regexp significant characters" do + @path = "/my/path/rV1x2DafFr0R6tGG+1bbk++++TM" + File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) + @fileset = Puppet::FileServing::Fileset.new(@path) + mock_dir_structure(@path) + @fileset.recurse = true + @fileset.files.sort.should == @files.sort + end end describe Puppet::FileServing::Fileset, " when following links that point to missing files" do diff --git a/spec/unit/file_serving/metadata.rb b/spec/unit/file_serving/metadata.rb index 768a7c56d..de0c4570c 100755 --- a/spec/unit/file_serving/metadata.rb +++ b/spec/unit/file_serving/metadata.rb @@ -43,10 +43,6 @@ describe Puppet::FileServing::Metadata, " when finding the file to use for setti @metadata.collect() end - it "should fail if a base path is neither set nor provided" do - proc { @metadata.collect() }.should raise_error(Errno::ENOENT) - end - it "should raise an exception if the file does not exist" do File.expects(:lstat).with(@path).raises(Errno::ENOENT) proc { @metadata.collect()}.should raise_error(Errno::ENOENT) @@ -100,6 +96,14 @@ describe Puppet::FileServing::Metadata, " when collecting attributes" do @metadata.checksum.should == "{md5}" + @checksum end + it "should give a mtime checksum when checksum_type is set" do + time = Time.now + @metadata.checksum_type = "mtime" + @metadata.expects(:mtime_file).returns(@time) + @metadata.collect + @metadata.checksum.should == "{mtime}" + @time.to_s + end + it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do @metadata.attributes_with_tabs.should == "#{0755.to_s}\tfile\t10\t20\t{md5}#{@checksum}" end @@ -110,14 +114,22 @@ describe Puppet::FileServing::Metadata, " when collecting attributes" do @stat.stubs(:ftype).returns("directory") @time = Time.now @metadata.expects(:ctime_file).returns(@time) - @metadata.collect end it "should only use checksums of type 'ctime' for directories" do + @metadata.collect + @metadata.checksum.should == "{ctime}" + @time.to_s + end + + it "should only use checksums of type 'ctime' for directories even if checksum_type set" do + @metadata.checksum_type = "mtime" + @metadata.expects(:mtime_file).never + @metadata.collect @metadata.checksum.should == "{ctime}" + @time.to_s end it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do + @metadata.collect @metadata.attributes_with_tabs.should == "#{0755.to_s}\tdirectory\t10\t20\t{ctime}#{@time.to_s}" end end diff --git a/spec/unit/module.rb b/spec/unit/module.rb index 4d66550b5..a6608fc1b 100755 --- a/spec/unit/module.rb +++ b/spec/unit/module.rb @@ -89,6 +89,19 @@ describe Puppet::Module, " when searching for templates" do Puppet::Module.find_template("mymod/mytemplate").should == "/my/templates/mymod/mytemplate" end + it "should raise an error if no valid templatedir exists" do + Puppet::Module.stubs(:templatepath).with(nil).returns(nil) + lambda { Puppet::Module.find_template("mytemplate") }.should raise_error + end + + it "should not raise an error if no valid templatedir exists and the template exists in a module" do + Puppet::Module.stubs(:templatepath).with(nil).returns(nil) + Puppet[:modulepath] = "/one:/two" + File.stubs(:directory?).returns(true) + File.stubs(:exists?).returns(true) + Puppet::Module.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" + end + it "should use the main templatedir if no module is found" do Puppet::Module.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Module.expects(:find).with("mymod", nil).returns(nil) diff --git a/spec/unit/network/handler/fileserver.rb b/spec/unit/network/handler/fileserver.rb new file mode 100644 index 000000000..4ba8e712d --- /dev/null +++ b/spec/unit/network/handler/fileserver.rb @@ -0,0 +1,179 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/network/handler/fileserver' + + +describe Puppet::Network::Handler::FileServer do + require 'tmpdir' + + def create_file(filename) + File.open(filename, "w") { |f| f.puts filename} + end + + def create_nested_file() + dirname = File.join(@basedir, "nested_dir") + Dir.mkdir(dirname) + file = File.join(dirname, "nested_dir_file") + create_file(file) + end + + before do + @basedir = File.join(Dir.tmpdir(), "test_network_handler") + Dir.mkdir(@basedir) + @file = File.join(@basedir, "aFile") + @link = File.join(@basedir, "aLink") + create_file(@file) + @mount = Puppet::Network::Handler::FileServer::Mount.new("some_path", @basedir) + end + + it "should list a single directory" do + @mount.list("/", false, false).should == [["/", "directory"]] + end + + it "should list a file within a directory when given the file path" do + @mount.list("/aFile", false, "false").should == [["/", "file"]] + end + + it "should list a file within a directory when given the file path with recursion" do + @mount.list("/aFile", true, "false").should == [["/", "file"]] + end + + it "should return nil for a non-existent path" do + @mount.list("/no_such_file", false, false).should be(nil) + end + + it "should list a symbolic link as a file when given the link path" do + File.symlink(@file, @link) + @mount.list("/aLink", false, false).should == [["/", "file"]] + end + + it "should return nil for a dangling symbolic link when given the link path" do + File.symlink("/some/where", @link) + @mount.list("/aLink", false, false).should be(nil) + end + + it "should list directory contents of a flat directory structure when asked to recurse" do + list = @mount.list("/", true, false) + list.should include(["/aFile", "file"]) + list.should include(["/", "directory"]) + list.should have(2).items + end + + it "should list the contents of a nested directory" do + create_nested_file() + list = @mount.list("/", true, false) + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , + ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort + end + + it "should list the contents of a directory ignoring files that match" do + create_nested_file() + list = @mount.list("/", true, "*File") + list.sort.should == [ ["/", "directory"] , + ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort + end + + it "should list the contents of a directory ignoring directories that match" do + create_nested_file() + list = @mount.list("/", true, "*nested_dir") + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] ].sort + end + + it "should list the contents of a directory ignoring all ignore patterns that match" do + create_nested_file() + list = @mount.list("/", true, ["*File" , "*nested_dir"]) + list.should == [ ["/", "directory"] ] + end + + it "should list the directory when recursing to a depth of zero" do + create_nested_file() + list = @mount.list("/", 0, false) + list.should == [["/", "directory"]] + end + + it "should list the base directory and files and nested directory to a depth of one" do + create_nested_file() + list = @mount.list("/", 1, false) + list.sort.should == [ ["/aFile", "file"], ["/nested_dir", "directory"], ["/", "directory"] ].sort + end + + it "should list the base directory and files and nested directory to a depth of two" do + create_nested_file() + list = @mount.list("/", 2, false) + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , + ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort + end + + it "should list the base directory and files and nested directory to a depth greater than the directory structure" do + create_nested_file() + list = @mount.list("/", 42, false) + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , + ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort + end + + it "should list a valid symbolic link as a file when recursing base dir" do + File.symlink(@file, @link) + list = @mount.list("/", true, false) + list.sort.should == [ ["/", "directory"], ["/aFile", "file"], ["/aLink", "file"] ].sort + end + + it "should not error when a dangling symlink is present" do + File.symlink("/some/where", @link) + lambda { @mount.list("/", true, false) }.should_not raise_error + end + + it "should return the directory contents of valid entries when a dangling symlink is present" do + File.symlink("/some/where", @link) + list = @mount.list("/", true, false) + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] ].sort + end + + describe Puppet::Network::Handler::FileServer::PluginMount do + PLUGINS = Puppet::Network::Handler::FileServer::PLUGINS + + # create a module plugin hierarchy + def create_plugin(mod, plugin) + dirname = File.join(@basedir, mod) + Dir.mkdir(dirname) + plugins = File.join(dirname, PLUGINS) + Dir.mkdir(plugins) + facter = File.join(plugins, plugin) + Dir.mkdir(facter) + create_file(File.join(facter,"fact.rb")) + end + + before :each do + @modules = ["one","two"] + Puppet::Module.stubs(:all).returns(@modules.collect{ |p| File.join(@basedir,p)} ) + @modules.each { |m| create_plugin(m, "facter") } + + @modules.each do |p| + File.stubs(:directory?).with(File.join(@basedir,p,PLUGINS)).returns(true) + end + + @mount = Puppet::Network::Handler::FileServer::PluginMount.new(PLUGINS) + @mount.allow("*") + end + + it "should list a file within a directory when given the file path with recursion" do + @mount.list("facter/fact.rb", true, "false").should == [["/", "file"], ["/", "file"]] + end + + it "should return a merged view of all plugins for all modules" do + list = @mount.list("facter",true,false) + list.should == [["/", "directory"], ["/fact.rb", "file"], ["/", "directory"], ["/fact.rb", "file"]] + end + + it "should not fail for inexistant plugins type" do + lambda { @mount.list("puppet/parser",true,false) }.should_not raise_error + end + + end + + after do + FileUtils.rm_rf(@basedir) + end + +end diff --git a/spec/unit/network/xmlrpc/client.rb b/spec/unit/network/xmlrpc/client.rb new file mode 100644 index 000000000..a0a2e77fb --- /dev/null +++ b/spec/unit/network/xmlrpc/client.rb @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +describe Puppet::Network do + it "should raise an XMLRPCClientError if a generated class raises a Timeout::Error" do + http = mock 'http' + Puppet::Network::HttpPool.stubs(:http_instance).returns http + file = Puppet::Network::Client.file.new({:Server => "foo.com"}) + http.stubs(:post2).raises Timeout::Error + lambda { file.retrieve }.should raise_error(Puppet::Network::XMLRPCClientError) + end +end diff --git a/spec/unit/node.rb b/spec/unit/node.rb index 70aa8bd9a..df3b7483c 100755 --- a/spec/unit/node.rb +++ b/spec/unit/node.rb @@ -45,20 +45,15 @@ describe Puppet::Node, "when initializing" do end it "should accept an environment value" do - Puppet.settings.stubs(:value).with(:environments).returns("myenv") + Puppet.settings.stubs(:value).with(:environment).returns("myenv") @node = Puppet::Node.new("testing", :environment => "myenv") @node.environment.should == "myenv" end - - it "should validate the environment" do - Puppet.settings.stubs(:value).with(:environments).returns("myenv") - proc { Puppet::Node.new("testing", :environment => "other") }.should raise_error(ArgumentError) - end end describe Puppet::Node, "when returning the environment" do before do - Puppet.settings.stubs(:value).with(:environments).returns("one,two") + Puppet.settings.stubs(:value).with(:environment).returns("one,two") Puppet.settings.stubs(:value).with(:environment).returns("one") @node = Puppet::Node.new("testnode") end @@ -73,16 +68,6 @@ describe Puppet::Node, "when returning the environment" do Puppet::Node::Environment.expects(:new).returns(env) @node.environment.should == "myenv" end - - it "should fail if the parameter environment is invalid" do - @node.parameters = {"environment" => "three"} - proc { @node.environment }.should raise_error(ArgumentError) - end - - it "should fail if the parameter environment is invalid" do - @node.parameters = {"environment" => "three"} - proc { @node.environment }.should raise_error(ArgumentError) - end end describe Puppet::Node, "when merging facts" do diff --git a/spec/unit/node/environment.rb b/spec/unit/node/environment.rb index 8433a9877..872c70912 100755 --- a/spec/unit/node/environment.rb +++ b/spec/unit/node/environment.rb @@ -5,73 +5,21 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/node/environment' describe Puppet::Node::Environment do - it "should provide a list of valid environments" do - Puppet::Node::Environment.valid.should be_instance_of(Array) - end - - it "should determine its list of valid environments from splitting the :environments setting on commas" do - Puppet.settings.stubs(:value).with(:environments).returns("one,two") - Puppet::Node::Environment.valid.collect { |e| e.to_s }.sort.should == %w{one two}.sort - end - - it "should not use an environment when determining the list of valid environments" do - Puppet.settings.expects(:value).with(:environments).returns("one,two") - Puppet::Node::Environment.valid - end - - it "should provide a means of identifying invalid environments" do - Puppet.settings.expects(:value).with(:environments).returns("one,two") - Puppet::Node::Environment.valid?(:three).should be_false - end - - it "should provide a means of identifying valid environments" do - Puppet.settings.expects(:value).with(:environments).returns("one,two") - Puppet::Node::Environment.valid?(:one).should be_true - end - - it "should be used to determine when an environment setting is valid" do - Puppet.settings.expects(:value).with(:environments).returns("one,two") - proc { Puppet.settings[:environment] = :three }.should raise_error(ArgumentError) - end - it "should use the default environment if no name is provided while initializing an environment" do - Puppet.settings.expects(:value).with(:environments).returns("one,two") Puppet.settings.expects(:value).with(:environment).returns("one") Puppet::Node::Environment.new().name.should == :one end it "should treat environment instances as singletons" do - Puppet.settings.stubs(:value).with(:environments).returns("one") Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one")) end it "should treat an environment specified as names or strings as equivalent" do - Puppet.settings.stubs(:value).with(:environments).returns("one") Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one")) end - - it "should fail if an invalid environment instance is asked for" do - Puppet.settings.stubs(:value).with(:environments).returns("one,two") - proc { Puppet::Node::Environment.new("three") }.should raise_error(ArgumentError) - end - - it "should consider environments that are empty strings invalid" do - Puppet::Node::Environment.valid?("").should be_false - end - - it "should fail if a no-longer-valid environment instance is asked for" do - Puppet.settings.expects(:value).with(:environments).returns("one") - Puppet::Node::Environment.new("one") - Puppet.settings.expects(:value).with(:environments).returns("two") - proc { Puppet::Node::Environment.new("one") }.should raise_error(ArgumentError) - end end describe Puppet::Node::Environment, " when modeling a specific environment" do - before do - Puppet.settings.expects(:value).with(:environments).returns("testing") - end - it "should have a method for returning the environment name" do Puppet::Node::Environment.new("testing").name.should == :testing end diff --git a/spec/unit/parser/ast/arithmetic_operator.rb b/spec/unit/parser/ast/arithmetic_operator.rb new file mode 100755 index 000000000..24d6ad47d --- /dev/null +++ b/spec/unit/parser/ast/arithmetic_operator.rb @@ -0,0 +1,73 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ArithmeticOperator do + + AST = Puppet::Parser::AST + + before :each do + @scope = Puppet::Parser::Scope.new() + @one = stub 'lval', :safeevaluate => 1 + @two = stub 'rval', :safeevaluate => 2 + end + + it "should evaluate both branches" do + lval = stub "lval" + lval.expects(:safeevaluate).with(@scope).returns(1) + rval = stub "rval" + rval.expects(:safeevaluate).with(@scope).returns(2) + + operator = AST::ArithmeticOperator.new :rval => rval, :operator => "+", :lval => lval + operator.evaluate(@scope) + end + + it "should fail for an unknown operator" do + lambda { operator = AST::ArithmeticOperator.new :lval => @one, :operator => "%", :rval => @two }.should raise_error + end + + it "should call Puppet::Parser::Scope.number?" do + Puppet::Parser::Scope.expects(:number?).with(1).returns(1) + Puppet::Parser::Scope.expects(:number?).with(2).returns(2) + + AST::ArithmeticOperator.new(:lval => @one, :operator => "+", :rval => @two).evaluate(@scope) + end + + + %w{ + - * / << >>}.each do |op| + it "should call ruby Numeric '#{op}'" do + one = stub 'one' + two = stub 'two' + operator = AST::ArithmeticOperator.new :lval => @one, :operator => op, :rval => @two + Puppet::Parser::Scope.stubs(:number?).with(1).returns(one) + Puppet::Parser::Scope.stubs(:number?).with(2).returns(two) + one.expects(:send).with(op,two) + operator.evaluate(@scope) + end + end + + it "should work even with numbers embedded in strings" do + two = stub 'two', :safeevaluate => "2" + one = stub 'one', :safeevaluate => "1" + operator = AST::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one + operator.evaluate(@scope).should == 3 + end + + it "should work even with floats" do + two = stub 'two', :safeevaluate => 2.53 + one = stub 'one', :safeevaluate => 1.80 + operator = AST::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one + operator.evaluate(@scope).should == 4.33 + end + + it "should work for variables too" do + @scope.expects(:lookupvar).with("one").returns(1) + @scope.expects(:lookupvar).with("two").returns(2) + one = AST::Variable.new( :value => "one" ) + two = AST::Variable.new( :value => "two" ) + + operator = AST::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two + operator.evaluate(@scope).should == 3 + end + +end diff --git a/spec/unit/parser/ast/boolean_operator.rb b/spec/unit/parser/ast/boolean_operator.rb new file mode 100755 index 000000000..7304e2a10 --- /dev/null +++ b/spec/unit/parser/ast/boolean_operator.rb @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::BooleanOperator do + + AST = Puppet::Parser::AST + + before :each do + @scope = Puppet::Parser::Scope.new() + @true_ast = AST::Boolean.new( :value => true) + @false_ast = AST::Boolean.new( :value => false) + end + + it "should evaluate left operand inconditionally" do + lval = stub "lval" + lval.expects(:safeevaluate).with(@scope).returns("true") + rval = stub "rval", :safeevaluate => false + rval.expects(:safeevaluate).never + + operator = AST::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval + operator.evaluate(@scope) + end + + it "should evaluate right 'and' operand only if left operand is true" do + lval = stub "lval", :safeevaluate => true + rval = stub "rval", :safeevaluate => false + rval.expects(:safeevaluate).with(@scope).returns(false) + operator = AST::BooleanOperator.new :rval => rval, :operator => "and", :lval => lval + operator.evaluate(@scope) + end + + it "should evaluate right 'or' operand only if left operand is false" do + lval = stub "lval", :safeevaluate => false + rval = stub "rval", :safeevaluate => false + rval.expects(:safeevaluate).with(@scope).returns(false) + operator = AST::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval + operator.evaluate(@scope) + end + + it "should return true for false OR true" do + AST::BooleanOperator.new(:rval => @true_ast, :operator => "or", :lval => @false_ast).evaluate(@scope).should be_true + end + + it "should return false for true AND false" do + AST::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @false_ast ).evaluate(@scope).should be_false + end + + it "should return true for true AND true" do + AST::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @true_ast ).evaluate(@scope).should be_true + end + +end diff --git a/spec/unit/parser/ast/collexpr.rb b/spec/unit/parser/ast/collexpr.rb new file mode 100755 index 000000000..e5e6e0d7a --- /dev/null +++ b/spec/unit/parser/ast/collexpr.rb @@ -0,0 +1,92 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::CollExpr do + + AST = Puppet::Parser::AST + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + describe "when evaluating with two operands" do + before :each do + @test1 = mock 'test1' + @test1.expects(:safeevaluate).with(@scope).returns("test1") + @test2 = mock 'test2' + @test2.expects(:safeevaluate).with(@scope).returns("test2") + end + + it "should evaluate both" do + collexpr = AST::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==") + collexpr.evaluate(@scope) + end + + it "should produce a textual representation and code of the expression" do + collexpr = AST::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==") + result = collexpr.evaluate(@scope) + result[0].should == "param_values.value = 'test2' and param_names.name = 'test1'" + result[1].should be_an_instance_of(Proc) + end + + it "should propagate expression type and form to child if expression themselves" do + [@test1, @test2].each do |t| + t.expects(:is_a?).returns(true) + t.expects(:form).returns(false) + t.expects(:type).returns(false) + t.expects(:type=) + t.expects(:form=) + end + + collexpr = AST::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==", :form => true, :type => true) + result = collexpr.evaluate(@scope) + end + + describe "and when evaluating the produced code" do + before :each do + @resource = mock 'resource' + @resource.expects(:[]).with("test1").at_least(1).returns("test2") + end + + it "should evaluate like the original expression for ==" do + collexpr = AST::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "==") + collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] == "test2") + end + + it "should evaluate like the original expression for !=" do + collexpr = AST::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "!=") + collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] != "test2") + end + end + + it "should warn if this is an exported collection containing parenthesis (unsupported)" do + collexpr = AST::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==", :parens => true, :form => :exported) + Puppet.expects(:warning) + collexpr.evaluate(@scope) + end + + %w{and or}.each do |op| + it "should raise an error if this is an exported collection with #{op} operator (unsupported)" do + collexpr = AST::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=> op, :form => :exported) + lambda { collexpr.evaluate(@scope) }.should raise_error(Puppet::ParseError) + end + end + end + + it "should check for array member equality if resource parameter is an array for ==" do + array = mock 'array', :safeevaluate => "array" + test1 = mock 'test1' + test1.expects(:safeevaluate).with(@scope).returns("test1") + + resource = mock 'resource' + resource.expects(:[]).with("array").at_least(1).returns(["test1","test2","test3"]) + collexpr = AST::CollExpr.new(:test1 => array, :test2 => test1, :oper => "==") + collexpr.evaluate(@scope)[1].call(resource).should be_true + end + + it "should raise an error for invalid operator" do + lambda { collexpr = AST::CollExpr.new(:oper=>">") }.should raise_error + end + +end
\ No newline at end of file diff --git a/spec/unit/parser/ast/comparison_operator.rb b/spec/unit/parser/ast/comparison_operator.rb new file mode 100755 index 000000000..dbea349f2 --- /dev/null +++ b/spec/unit/parser/ast/comparison_operator.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ComparisonOperator do + before :each do + @scope = Puppet::Parser::Scope.new() + @one = Puppet::Parser::AST::FlatString.new( :value => 1 ) + @two = Puppet::Parser::AST::FlatString.new( :value => 2 ) + end + + it "should evaluate both branches" do + lval = stub "lval" + lval.expects(:safeevaluate).with(@scope) + rval = stub "rval" + rval.expects(:safeevaluate).with(@scope) + + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => lval, :operator => "==", :rval => rval + operator.evaluate(@scope) + end + + it "should fail for an unknown operator" do + lambda { operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => "or", :rval => @two }.should raise_error + end + + %w{< > <= >= ==}.each do |oper| + it "should return the result of using '#{oper}' to compare the left and right sides" do + one = stub 'one', :safeevaluate => "1" + two = stub 'two', :safeevaluate => "2" + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => one, :operator => oper, :rval => two + operator.evaluate(@scope).should == 1.send(oper,2) + end + end + + it "should return the result of using '!=' to compare the left and right sides" do + one = stub 'one', :safeevaluate => "1" + two = stub 'two', :safeevaluate => "2" + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => one, :operator => '!=', :rval => two + operator.evaluate(@scope).should == true + end + + it "should work for variables too" do + @scope.expects(:lookupvar).with("one").returns(1) + @scope.expects(:lookupvar).with("two").returns(2) + one = Puppet::Parser::AST::Variable.new( :value => "one" ) + two = Puppet::Parser::AST::Variable.new( :value => "two" ) + + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => one, :operator => "<", :rval => two + operator.evaluate(@scope).should == true + end + +end diff --git a/spec/unit/parser/ast/minus.rb b/spec/unit/parser/ast/minus.rb new file mode 100755 index 000000000..83bd92d0d --- /dev/null +++ b/spec/unit/parser/ast/minus.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Minus do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should evaluate its argument" do + value = stub "value" + value.expects(:safeevaluate).with(@scope).returns(123) + + operator = Puppet::Parser::AST::Minus.new :value => value + operator.evaluate(@scope) + end + + it "should fail if argument is not a string or integer" do + array_ast = stub 'array_ast', :safeevaluate => [2] + operator = Puppet::Parser::AST::Minus.new :value => array_ast + lambda { operator.evaluate(@scope) }.should raise_error + end + + it "should work with integer as string" do + string = stub 'string', :safeevaluate => "123" + operator = Puppet::Parser::AST::Minus.new :value => string + operator.evaluate(@scope).should == -123 + end + + it "should work with integers" do + int = stub 'int', :safeevaluate => 123 + operator = Puppet::Parser::AST::Minus.new :value => int + operator.evaluate(@scope).should == -123 + end + +end diff --git a/spec/unit/parser/ast/nop.rb b/spec/unit/parser/ast/nop.rb new file mode 100755 index 000000000..5a7132586 --- /dev/null +++ b/spec/unit/parser/ast/nop.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Nop do + + before do + @scope = mock 'scope' + end + + it "should do nothing on evaluation" do + Puppet::Parser::AST.expects(:safeevaluate).never + Puppet::Parser::AST::Nop.new({}).evaluate(@scope) + end + + it "should not return anything" do + Puppet::Parser::AST::Nop.new({}).evaluate(@scope).should be_nil + end + +end diff --git a/spec/unit/parser/ast/not.rb b/spec/unit/parser/ast/not.rb new file mode 100755 index 000000000..0fe2deddd --- /dev/null +++ b/spec/unit/parser/ast/not.rb @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Not do + before :each do + @scope = Puppet::Parser::Scope.new() + @true_ast = Puppet::Parser::AST::Boolean.new( :value => true) + @false_ast = Puppet::Parser::AST::Boolean.new( :value => false) + end + + it "should evaluate its child expression" do + val = stub "val" + val.expects(:safeevaluate).with(@scope) + + operator = Puppet::Parser::AST::Not.new :value => val + operator.evaluate(@scope) + end + + it "should return true for ! false" do + operator = Puppet::Parser::AST::Not.new :value => @false_ast + operator.evaluate(@scope).should == true + end + + it "should return false for ! true" do + operator = Puppet::Parser::AST::Not.new :value => @true_ast + operator.evaluate(@scope).should == false + end + +end diff --git a/spec/unit/parser/ast/resource_override.rb b/spec/unit/parser/ast/resource_override.rb new file mode 100755 index 000000000..3fbeb323c --- /dev/null +++ b/spec/unit/parser/ast/resource_override.rb @@ -0,0 +1,51 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ResourceOverride do + + AST = Puppet::Parser::AST + + before :each do + @compiler = stub 'compiler' + @scope = Puppet::Parser::Scope.new(:compiler => @compiler) + @params = AST::ASTArray.new({}) + @compiler.stubs(:add_override) + end + + it "should evaluate the overriden object" do + klass = stub 'klass', :title => "title", :type => "type" + object = mock 'object' + object.expects(:safeevaluate).with(@scope).returns(klass) + AST::ResourceOverride.new(:object => object, :params => @params ).evaluate(@scope) + end + + it "should tell the compiler to override the resource with our own" do + @compiler.expects(:add_override) + + klass = stub 'klass', :title => "title", :type => "one" + object = mock 'object', :safeevaluate => klass + AST::ResourceOverride.new(:object => object , :params => @params).evaluate(@scope) + end + + it "should return the overriden resource directly when called with one item" do + klass = stub 'klass', :title => "title", :type => "one" + object = mock 'object', :safeevaluate => klass + override = AST::ResourceOverride.new(:object => object , :params => @params).evaluate(@scope) + override.should be_an_instance_of(Puppet::Parser::Resource) + override.title.should == "title" + override.type.should == "One" + end + + it "should return an array of overriden resources when called with an array of titles" do + klass1 = stub 'klass1', :title => "title1", :type => "one" + klass2 = stub 'klass2', :title => "title2", :type => "one" + + object = mock 'object', :safeevaluate => [klass1,klass2] + + override = AST::ResourceOverride.new(:object => object , :params => @params).evaluate(@scope) + override.should have(2).elements + override.each {|o| o.should be_an_instance_of(Puppet::Parser::Resource) } + end + +end
\ No newline at end of file diff --git a/spec/unit/parser/ast/resource_reference.rb b/spec/unit/parser/ast/resource_reference.rb new file mode 100755 index 000000000..e4b7c763b --- /dev/null +++ b/spec/unit/parser/ast/resource_reference.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ResourceReference do + + AST = Puppet::Parser::AST + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + def newref(title, type) + title = stub 'title', :safeevaluate => title + ref = AST::ResourceReference.new(:type => type, :title => title) + end + + it "should evaluate correctly reference to builtin types" do + newref("/tmp/yay", "File").evaluate(@scope).to_s.should == "File[/tmp/yay]" + end + + %{ "one::two" "one-two"}.each do |type| + it "should evaluate correctly reference to define" do + klass = stub 'klass', :title => "three", :classname => type + @scope.stubs(:finddefine).returns(klass) + + newref("three", type).evaluate(@scope).to_ref.should == Puppet::Parser::Resource::Reference.new( :type => type, :title => "three" ).to_ref + end + end + + it "should be able to call qualified_class" do + klass = stub 'klass', :title => "three", :classname => "one" + @scope.expects(:findclass).with("one").returns(klass) + newref("three","class").qualified_class(@scope,"one").should == "one" + end + + it "should be able to find qualified classes when evaluating" do + klass = stub 'klass', :title => "one", :classname => "one" + @scope.stubs(:findclass).returns(klass) + + evaled = newref("one", "class").evaluate(@scope) + evaled.type.should == "Class" + evaled.title.should == "one" + end + + it "should return an array of reference if given an array of titles" do + titles = mock 'titles', :safeevaluate => ["title1","title2"] + ref = AST::ResourceReference.new( :title => titles, :type => "Resource" ) + ref.stubs(:qualified_type).with(@scope).returns("Resource") + + ref.evaluate(@scope).should have(2).elements + end + + it "should qualify class of all titles for Class resource references" do + titles = mock 'titles', :safeevaluate => ["title1","title2"] + ref = AST::ResourceReference.new( :title => titles, :type => "Class" ) + ref.expects(:qualified_class).with(@scope,"title1").returns("class") + ref.expects(:qualified_class).with(@scope,"title2").returns("class") + + ref.evaluate(@scope) + end + +end
\ No newline at end of file diff --git a/spec/unit/parser/collector.rb b/spec/unit/parser/collector.rb index 2dfae6786..ede583b96 100755 --- a/spec/unit/parser/collector.rb +++ b/spec/unit/parser/collector.rb @@ -234,16 +234,16 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do @collector.evaluate end - it "should return all matching resources from the current compile" do + it "should return all matching resources from the current compile and mark them non-virtual and non-exported" do stub_rails(true) one = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true two = stub 'two', :type => "Mytype", :virtual? => true, :exported? => true - one.stubs(:exported=) - one.stubs(:virtual=) - two.stubs(:exported=) - two.stubs(:virtual=) + one.expects(:exported=).with(false) + one.expects(:virtual=).with(false) + two.expects(:exported=).with(false) + two.expects(:virtual=).with(false) @compiler.expects(:resources).returns([one, two]) diff --git a/spec/unit/parser/lexer.rb b/spec/unit/parser/lexer.rb index fed1ade7d..3b0df96a9 100755 --- a/spec/unit/parser/lexer.rb +++ b/spec/unit/parser/lexer.rb @@ -4,6 +4,27 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/parser/lexer' +describe Puppet::Parser::Lexer do + describe "when reading strings" do + before { @lexer = Puppet::Parser::Lexer.new } + it "should increment the line count for every carriage return in the string" do + @lexer.line = 10 + @lexer.string = "this\nis\natest'" + @lexer.slurpstring("'") + + @lexer.line.should == 12 + end + + it "should not increment the line count for escapes in the string" do + @lexer.line = 10 + @lexer.string = "this\\nis\\natest'" + @lexer.slurpstring("'") + + @lexer.line.should == 10 + end + end +end + describe Puppet::Parser::Lexer::Token do before do @token = Puppet::Parser::Lexer::Token.new(%r{something}, :NAME) @@ -136,7 +157,13 @@ describe Puppet::Parser::Lexer::TOKENS do :BACKSLASH => '\\', :FARROW => '=>', :PARROW => '+>', - :APPENDS => '+=' + :APPENDS => '+=', + :PLUS => '+', + :MINUS => '-', + :DIV => '/', + :TIMES => '*', + :LSHIFT => '<<', + :RSHIFT => '>>', }.each do |name, string| it "should have a token named #{name.to_s}" do Puppet::Parser::Lexer::TOKENS[name].should_not be_nil @@ -213,10 +240,34 @@ describe Puppet::Parser::Lexer::TOKENS[:NAME] do end describe Puppet::Parser::Lexer::TOKENS[:NUMBER] do - before { @token = Puppet::Parser::Lexer::TOKENS[:NUMBER] } + before do + @token = Puppet::Parser::Lexer::TOKENS[:NUMBER] +# @regex = Regexp.new('^'+@token.regex.source+'$') + @regex = @token.regex + end it "should match against numeric terms" do - @token.regex.should =~ "2982383139" + @regex.should =~ "2982383139" + end + + it "should match against float terms" do + @regex.should =~ "29823.235" + end + + it "should match against hexadecimal terms" do + @regex.should =~ "0xBEEF0023" + end + + it "should match against float with exponent terms" do + @regex.should =~ "10e23" + end + + it "should match against float terms with negative exponents" do + @regex.should =~ "10e-23" + end + + it "should match against float terms with fractional parts and exponent" do + @regex.should =~ "1.234e23" end it "should return the NAME token and the value" do diff --git a/spec/unit/parser/parser.rb b/spec/unit/parser/parser.rb index 94b19be40..c0d22a2cc 100755 --- a/spec/unit/parser/parser.rb +++ b/spec/unit/parser/parser.rb @@ -8,6 +8,7 @@ describe Puppet::Parser do before :each do @parser = Puppet::Parser::Parser.new :environment => "development" + @true_ast = AST::Boolean.new :value => true end describe "when parsing append operator" do @@ -31,4 +32,114 @@ describe Puppet::Parser do end end -end + + describe Puppet::Parser, "when parsing 'if'" do + it "not, it should create the correct ast objects" do + AST::Not.expects(:new).with { |h| h[:value].is_a?(AST::Boolean) } + @parser.parse("if ! true { $var = 1 }") + + end + + it "boolean operation, it should create the correct ast objects" do + AST::BooleanOperator.expects(:new).with { + |h| h[:rval].is_a?(AST::Boolean) and h[:lval].is_a?(AST::Boolean) and h[:operator]=="or" + } + @parser.parse("if true or true { $var = 1 }") + + end + + it "comparison operation, it should create the correct ast objects" do + AST::ComparisonOperator.expects(:new).with { + |h| h[:lval].is_a?(AST::Name) and h[:rval].is_a?(AST::Name) and h[:operator]=="<" + } + @parser.parse("if 1 < 2 { $var = 1 }") + + end + + end + + describe Puppet::Parser, "when parsing if complex expressions" do + it "should create a correct ast tree" do + AST::ComparisonOperator.expects(:new).with { + |h| h[:rval].is_a?(AST::Name) and h[:lval].is_a?(AST::Name) and h[:operator]==">" + }.returns("whatever") + AST::ComparisonOperator.expects(:new).with { + |h| h[:rval].is_a?(AST::Name) and h[:lval].is_a?(AST::Name) and h[:operator]=="==" + }.returns("whatever") + AST::BooleanOperator.expects(:new).with { + |h| h[:rval]=="whatever" and h[:lval]=="whatever" and h[:operator]=="and" + } + @parser.parse("if (1 > 2) and (1 == 2) { $var = 1 }") + end + + it "should raise an error on incorrect expression" do + lambda { @parser.parse("if (1 > 2 > ) or (1 == 2) { $var = 1 }") }.should raise_error + end + + end + + describe Puppet::Parser, "when parsing resource references" do + + it "should not raise syntax errors" do + lambda { @parser.parse('exec { test: param => File["a"] }') }.should_not raise_error + end + + it "should not raise syntax errors with multiple references" do + lambda { @parser.parse('exec { test: param => File["a","b"] }') }.should_not raise_error + end + + it "should create an AST::ResourceReference" do + AST::Resource.stubs(:new) + AST::ResourceReference.expects(:new).with { |arg| + arg[:line]==1 and arg[:type]=="File" and arg[:title].is_a?(AST::ASTArray) + } + @parser.parse('exec { test: command => File["a","b"] }') + end + end + + describe Puppet::Parser, "when parsing resource overrides" do + + it "should not raise syntax errors" do + lambda { @parser.parse('Resource["title"] { param => value }') }.should_not raise_error + end + + it "should not raise syntax errors with multiple overrides" do + lambda { @parser.parse('Resource["title1","title2"] { param => value }') }.should_not raise_error + end + + it "should create an AST::ResourceOverride" do + AST::ResourceOverride.expects(:new).with { |arg| + arg[:line]==1 and arg[:object].is_a?(AST::ResourceReference) and arg[:params].is_a?(AST::ResourceParam) + } + @parser.parse('Resource["title1","title2"] { param => value }') + end + + end + + describe Puppet::Parser, "when parsing if statements" do + + it "should not raise errors with empty if" do + lambda { @parser.parse("if true { }") }.should_not raise_error + end + + it "should not raise errors with empty else" do + lambda { @parser.parse("if false { notice('if') } else { }") }.should_not raise_error + end + + it "should not raise errors with empty if and else" do + lambda { @parser.parse("if false { } else { }") }.should_not raise_error + end + + it "should create a nop node for empty branch" do + AST::Nop.expects(:new) + @parser.parse("if true { }") + end + + it "should create a nop node for empty else branch" do + AST::Nop.expects(:new) + @parser.parse("if true { notice('test') } else { }") + end + + end + + end diff --git a/spec/unit/parser/resource.rb b/spec/unit/parser/resource.rb index 6b2021916..63cfbc2ed 100755 --- a/spec/unit/parser/resource.rb +++ b/spec/unit/parser/resource.rb @@ -176,6 +176,16 @@ describe Puppet::Parser::Resource do @resource["noop"].should == "false" end + it "should copy all metaparams that it finds" do + @scope.setvar("require", "container") + @scope.setvar("notify", "container") + + @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + + @resource["require"].should == "container" + @resource["notify"].should == "container" + end + it "should stack relationship metaparams from its container if it already has them" do @resource.set_parameter("require", "resource") @scope.setvar("require", "container") diff --git a/spec/unit/parser/scope.rb b/spec/unit/parser/scope.rb index ec8ab6d7d..fa76c4ff2 100755 --- a/spec/unit/parser/scope.rb +++ b/spec/unit/parser/scope.rb @@ -34,4 +34,54 @@ describe Puppet::Parser::Scope do end end + + describe Puppet::Parser::Scope, "when calling number?" do + + it "should return nil if called with anything not a number" do + Puppet::Parser::Scope.number?([2]).should be_nil + end + + it "should return a Fixnum for a Fixnum" do + Puppet::Parser::Scope.number?(2).should be_an_instance_of(Fixnum) + end + + it "should return a Float for a Float" do + Puppet::Parser::Scope.number?(2.34).should be_an_instance_of(Float) + end + + it "should return 234 for '234'" do + Puppet::Parser::Scope.number?("234").should == 234 + end + + it "should return nil for 'not a number'" do + Puppet::Parser::Scope.number?("not a number").should be_nil + end + + it "should return 23.4 for '23.4'" do + Puppet::Parser::Scope.number?("23.4").should == 23.4 + end + + it "should return 23.4e13 for '23.4e13'" do + Puppet::Parser::Scope.number?("23.4e13").should == 23.4e13 + end + + it "should understand negative numbers" do + Puppet::Parser::Scope.number?("-234").should == -234 + end + + it "should know how to convert exponential float numbers ala '23e13'" do + Puppet::Parser::Scope.number?("23e13").should == 23e13 + end + + it "should understand hexadecimal numbers" do + Puppet::Parser::Scope.number?("0x234").should == 0x234 + end + + it "should understand octal numbers" do + Puppet::Parser::Scope.number?("0755").should == 0755 + end + + + end + end diff --git a/spec/unit/parser/templatewrapper.rb b/spec/unit/parser/templatewrapper.rb index 2d4bd141b..20ea76921 100755 --- a/spec/unit/parser/templatewrapper.rb +++ b/spec/unit/parser/templatewrapper.rb @@ -55,6 +55,20 @@ describe Puppet::Parser::TemplateWrapper do tw.has_variable?("chicken").should eql(false) end + it "should allow you to retrieve the defined classes with classes" do + catalog = mock 'catalog', :classes => ["class1", "class2"] + @scope.expects(:catalog).returns( catalog ) + tw = Puppet::Parser::TemplateWrapper.new(@scope, @file) + tw.classes().should == ["class1", "class2"] + end + + it "should allow you to retrieve the defined tags with tags" do + catalog = mock 'catalog', :tags => ["tag1", "tag2"] + @scope.expects(:catalog).returns( catalog ) + tw = Puppet::Parser::TemplateWrapper.new(@scope, @file) + tw.tags().should == ["tag1","tag2"] + end + it "should set all of the scope's variables as instance variables" do template_mock = mock("template", :result => "woot!") File.expects(:read).with("/tmp/fake_template").returns("template contents") diff --git a/spec/unit/property/list.rb b/spec/unit/property/list.rb new file mode 100644 index 000000000..9c832c0cd --- /dev/null +++ b/spec/unit/property/list.rb @@ -0,0 +1,147 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/property/list' + +list_class = Puppet::Property::List + +describe list_class do + + it "should be a subclass of Property" do + list_class.superclass.must == Puppet::Property + end + + describe "as an instance" do + before do + # Wow that's a messy interface to the resource. + list_class.initvars + @resource = stub 'resource', :[]= => nil, :property => nil + @property = list_class.new(:resource => @resource) + end + + it "should have a , as default delimiter" do + @property.delimiter.should == "," + end + + it "should have a :membership as default membership" do + @property.membership.should == :membership + end + + it "should return the same value passed into should_to_s" do + @property.should_to_s("foo") == "foo" + end + + it "should return the passed in array values joined with the delimiter from is_to_s" do + @property.is_to_s(["foo","bar"]).should == "foo,bar" + end + + describe "when adding should to current" do + it "should add the arrays when current is an array" do + @property.add_should_with_current(["foo"], ["bar"]).should == ["foo", "bar"] + end + + it "should return should if current is not a array" do + @property.add_should_with_current(["foo"], :absent).should == ["foo"] + end + + it "should return only the uniq elements" do + @property.add_should_with_current(["foo", "bar"], ["foo", "baz"]).should == ["foo", "bar", "baz"] + end + end + + describe "when calling inclusive?" do + it "should use the membership method to look up on the @resource" do + @property.expects(:membership).returns(:membership) + @resource.expects(:[]).with(:membership) + @property.inclusive? + end + + it "should return true when @resource[membership] == inclusive" do + @property.stubs(:membership).returns(:membership) + @resource.stubs(:[]).with(:membership).returns(:inclusive) + @property.inclusive?.must == true + end + + it "should return false when @resource[membership] != inclusive" do + @property.stubs(:membership).returns(:membership) + @resource.stubs(:[]).with(:membership).returns(:minimum) + @property.inclusive?.must == false + end + end + + describe "when calling should" do + it "should return nil if @should is nil" do + @property.should.must == nil + end + + it "should return the sorted values of @should as a string if inclusive" do + @property.should = ["foo", "bar"] + @property.expects(:inclusive?).returns(true) + @property.should.must == "bar,foo" + end + + it "should return the uniq sorted values of @should + retrieve as a string if !inclusive" do + @property.should = ["foo", "bar"] + @property.expects(:inclusive?).returns(false) + @property.expects(:retrieve).returns(["foo","baz"]) + @property.should.must == "bar,baz,foo" + end + end + + describe "when calling retrieve" do + before do + @provider = mock("provider") + @property.stubs(:provider).returns(@provider) + end + + it "should send 'name' to the provider" do + @provider.expects(:send).with(:group) + @property.expects(:name).returns(:group) + @property.retrieve + end + + it "should return an array with the provider returned info" do + @provider.stubs(:send).with(:group).returns("foo,bar,baz") + @property.stubs(:name).returns(:group) + @property.retrieve == ["foo", "bar", "baz"] + end + + it "should return :absent when the provider returns :absent" do + @provider.stubs(:send).with(:group).returns(:absent) + @property.stubs(:name).returns(:group) + @property.retrieve == :absent + end + end + + describe "when calling insync?" do + it "should return true unless @should is defined and not nil" do + @property.insync?("foo") == true + end + + it "should return true unless the passed in values is not nil" do + @property.should = "foo" + @property.insync?(nil) == true + end + + it "should call prepare_is_for_comparison with value passed in and should" do + @property.should = "foo" + @property.expects(:prepare_is_for_comparison).with("bar") + @property.expects(:should) + @property.insync?("bar") + end + + it "should return true if prepared value == should value" do + @property.should = "bar,foo" + @property.expects(:inclusive?).returns(true) + @property.insync?(["bar","foo"]).must == true + end + + it "should return false if prepared value != should value" do + @property.should = "bar,baz,foo" + @property.expects(:inclusive?).returns(true) + @property.insync?(["bar","foo"]).must == false + end + end + end +end diff --git a/spec/unit/provider/confine/variable.rb b/spec/unit/provider/confine/variable.rb index 093301bdc..38b3dad1c 100755 --- a/spec/unit/provider/confine/variable.rb +++ b/spec/unit/provider/confine/variable.rb @@ -85,16 +85,26 @@ describe Puppet::Provider::Confine::Variable do describe "when summarizing multiple instances" do it "should return a hash of failing variables and their values" do - c1 = stub '1', :valid? => false, :values => %w{one}, :fact => "uno" - c2 = stub '2', :valid? => true, :values => %w{two}, :fact => "dos" - c3 = stub '3', :valid? => false, :values => %w{three}, :fact => "tres" + c1 = Puppet::Provider::Confine::Variable.new("one") + c1.name = "uno" + c1.expects(:valid?).returns false + c2 = Puppet::Provider::Confine::Variable.new("two") + c2.name = "dos" + c2.expects(:valid?).returns true + c3 = Puppet::Provider::Confine::Variable.new("three") + c3.name = "tres" + c3.expects(:valid?).returns false Puppet::Provider::Confine::Variable.summarize([c1, c2, c3]).should == {"uno" => %w{one}, "tres" => %w{three}} end it "should combine the values of multiple confines with the same fact" do - c1 = stub '1', :valid? => false, :values => %w{one}, :fact => "uno" - c2 = stub '2', :valid? => false, :values => %w{two}, :fact => "uno" + c1 = Puppet::Provider::Confine::Variable.new("one") + c1.name = "uno" + c1.expects(:valid?).returns false + c2 = Puppet::Provider::Confine::Variable.new("two") + c2.name = "uno" + c2.expects(:valid?).returns false Puppet::Provider::Confine::Variable.summarize([c1, c2]).should == {"uno" => %w{one two}} end diff --git a/spec/unit/provider/package/apt.rb b/spec/unit/provider/package/apt.rb new file mode 100755 index 000000000..0ec1cd9ed --- /dev/null +++ b/spec/unit/provider/package/apt.rb @@ -0,0 +1,138 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider = Puppet::Type.type(:package).provider(:apt) + +describe provider do + before do + @resource = stub 'resource', :[] => "asdf" + @provider = provider.new(@resource) + + @fakeresult = "install ok installed asdf 1.0\n" + end + + it "should be versionable" do + provider.should be_versionable + end + + it "should use :install to update" do + @provider.expects(:install) + @provider.update + end + + it "should use 'apt-get remove' to uninstall" do + @provider.expects(:aptget).with("-y", "-q", :remove, "asdf") + + @provider.uninstall + end + + it "should use 'apt-get purge' and 'dpkg purge' to purge" do + @provider.expects(:aptget).with("-y", "-q", :remove, "--purge", "asdf") + @provider.expects(:dpkg).with("--purge", "asdf") + + @provider.purge + end + + it "should use 'apt-cache policy' to determine the latest version of a package" do + @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf: +Installed: 1:1.0 +Candidate: 1:1.1 +Version table: + 1:1.0 + 650 http://ftp.osuosl.org testing/main Packages +*** 1:1.1 + 100 /var/lib/dpkg/status" + + @provider.latest.should == "1:1.1" + end + + it "should print and error and return nil if no policy is found" do + @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf:" + + @provider.expects(:err) + @provider.latest.should be_nil + end + + it "should be able to preseed" do + @provider.should respond_to(:run_preseed) + end + + it "should preseed with the provided responsefile when preseeding is called for" do + @resource.expects(:[]).with(:responsefile).returns "/my/file" + FileTest.expects(:exist?).with("/my/file").returns true + + @provider.expects(:info) + @provider.expects(:preseed).with("/my/file") + + @provider.run_preseed + end + + it "should not preseed if no responsefile is provided" do + @resource.expects(:[]).with(:responsefile).returns nil + + @provider.expects(:info) + @provider.expects(:preseed).never + + @provider.run_preseed + end + + it "should fail if a cdrom is listed in the sources list and :allowcdrom is not specified" + + describe "when installing" do + it "should preseed if a responsefile is provided" do + @resource.expects(:[]).with(:responsefile).returns "/my/file" + @provider.expects(:run_preseed) + + @provider.stubs(:aptget) + @provider.install + end + + it "should check for a cdrom" do + @provider.expects(:checkforcdrom) + + @provider.stubs(:aptget) + @provider.install + end + + it "should use 'apt-get install' with the package name if no version is asked for" do + @resource.expects(:[]).with(:ensure).returns :installed + @provider.expects(:aptget).with { |*command| command[-1] == "asdf" and command[-2] == :install } + + @provider.install + end + + it "should specify the package version if one is asked for" do + @resource.expects(:[]).with(:ensure).returns "1.0" + @provider.expects(:aptget).with { |*command| command[-1] == "asdf=1.0" } + + @provider.install + end + + it "should do a quiet install" do + @provider.expects(:aptget).with { |*command| command.include?("-q") } + + @provider.install + end + + it "should default to 'yes' for all questions" do + @provider.expects(:aptget).with { |*command| command.include?("-y") } + + @provider.install + end + + it "should keep config files if asked" do + @resource.expects(:[]).with(:configfiles).returns :keep + @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confold") } + + @provider.install + end + + it "should replace config files if asked" do + @resource.expects(:[]).with(:configfiles).returns :replace + @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confnew") } + + @provider.install + end + end +end diff --git a/spec/unit/provider/package/dpkg.rb b/spec/unit/provider/package/dpkg.rb new file mode 100755 index 000000000..08aaca875 --- /dev/null +++ b/spec/unit/provider/package/dpkg.rb @@ -0,0 +1,163 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider = Puppet::Type.type(:package).provider(:dpkg) + +describe provider do + before do + @resource = stub 'resource', :[] => "asdf" + @provider = provider.new(@resource) + + @fakeresult = "install ok installed asdf 1.0\n" + end + + it "should have documentation" do + provider.doc.should be_instance_of(String) + end + + describe "when listing all instances" do + before do + provider.stubs(:command).with(:dpkgquery).returns "myquery" + end + + it "should use dpkg-query" do + provider.expects(:command).with(:dpkgquery).returns "myquery" + provider.expects(:execpipe).with("myquery -W --showformat '${Status} ${Package} ${Version}\\n'").returns @fakeresult + + provider.instances + end + + it "should create and return an instance with each parsed line from dpkg-query" do + pipe = mock 'pipe' + pipe.expects(:each).yields @fakeresult + provider.expects(:execpipe).yields pipe + + asdf = mock 'pkg1' + provider.expects(:new).with(:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg).returns asdf + + provider.instances.should == [asdf] + end + + it "should warn on and ignore any lines it does not understand" do + pipe = mock 'pipe' + pipe.expects(:each).yields "foobar" + provider.expects(:execpipe).yields pipe + + Puppet.expects(:warning) + provider.expects(:new).never + + provider.instances.should == [] + end + end + + describe "when querying the current state" do + it "should use dpkg-query" do + @provider.expects(:dpkgquery).with("-W", "--showformat",'${Status} ${Package} ${Version}\\n', "asdf").returns @fakeresult + + @provider.query + end + + it "should consider the package purged if dpkg-query fails" do + @provider.expects(:dpkgquery).raises Puppet::ExecutionFailure.new("eh") + + @provider.query[:ensure].should == :purged + end + + it "should return a hash of the found status with the desired state, error state, status, name, and 'ensure'" do + @provider.expects(:dpkgquery).returns @fakeresult + + @provider.query.should == {:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg} + end + + it "should consider the package absent if the dpkg-query result cannot be interpreted" do + @provider.expects(:dpkgquery).returns "somebaddata" + + @provider.query[:ensure].should == :absent + end + + it "should fail if an error is discovered" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("ok", "error") + + lambda { @provider.query }.should raise_error(Puppet::Error) + end + + it "should consider the package purged if it is marked 'not-installed'" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "not-installed") + + @provider.query[:ensure].should == :purged + end + + it "should consider the package absent if its status is neither 'installed' nor 'not-installed'" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "foo") + + @provider.query[:ensure].should == :absent + end + end + + it "should be able to install" do + @provider.should respond_to(:install) + end + + describe "when installing" do + before do + @resource.stubs(:[]).with(:source).returns "mypkg" + end + + it "should fail to install if no source is specified in the resource" do + @resource.expects(:[]).with(:source).returns nil + + lambda { @provider.install }.should raise_error(ArgumentError) + end + + it "should use 'dpkg -i' to install the package" do + @resource.expects(:[]).with(:source).returns "mypackagefile" + @provider.expects(:dpkg).with { |*command| command[-1] == "mypackagefile" and command[-2] == "-i" } + + @provider.install + end + + it "should keep old config files if told to do so" do + @resource.expects(:[]).with(:configfiles).returns :keep + @provider.expects(:dpkg).with { |*command| command[0] == "--force-confold" } + + @provider.install + end + + it "should replace old config files if told to do so" do + @resource.expects(:[]).with(:configfiles).returns :replace + @provider.expects(:dpkg).with { |*command| command[0] == "--force-confnew" } + + @provider.install + end + end + + it "should use :install to update" do + @provider.expects(:install) + @provider.update + end + + describe "when determining latest available version" do + it "should return the version found by dpkg-deb" do + @resource.expects(:[]).with(:source).returns "myfile" + @provider.expects(:dpkg_deb).with { |*command| command[-1] == "myfile" }.returns "asdf\t1.0" + @provider.latest.should == "1.0" + end + + it "should warn if the package file contains a different package" do + @provider.expects(:dpkg_deb).returns("foo\tversion") + @provider.expects(:warning) + @provider.latest + end + end + + it "should use 'dpkg -r' to uninstall" do + @provider.expects(:dpkg).with("-r", "asdf") + @provider.uninstall + end + + it "should use 'dpkg --purge' to purge" do + @provider.expects(:dpkg).with("--purge", "asdf") + @provider.purge + end +end diff --git a/spec/unit/provider/selboolean.rb b/spec/unit/provider/selboolean.rb new file mode 100755 index 000000000..4006df151 --- /dev/null +++ b/spec/unit/provider/selboolean.rb @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider_class = Puppet::Type.type(:selboolean).provider(:getsetsebool) + +describe provider_class do + before :each do + @resource = stub("resource", :name => "foo") + @resource.stubs(:[]).returns "foo" + @provider = provider_class.new(@resource) + end + + it "should return :on when getsebool returns on" do + @provider.expects(:getsebool).with("foo").returns "foo --> on\n" + @provider.value.should == :on + end + + it "should return :off when getsebool returns on" do + @provider.expects(:getsebool).with("foo").returns "foo --> off\n" + @provider.value.should == :off + end + + it "should call execpipe when updating boolean setting" do + @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" + @provider.expects(:execpipe).with("/usr/sbin/setsebool foo off") + @provider.value = :off + end + + it "should call execpipe with -P when updating persistent boolean setting" do + @resource.stubs(:[]).with(:persistent).returns :true + @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" + @provider.expects(:execpipe).with("/usr/sbin/setsebool -P foo off") + @provider.value = :off + end + +end diff --git a/spec/unit/provider/selmodule-example.pp b/spec/unit/provider/selmodule-example.pp Binary files differnew file mode 100644 index 000000000..28166ca40 --- /dev/null +++ b/spec/unit/provider/selmodule-example.pp diff --git a/spec/unit/provider/selmodule.rb b/spec/unit/provider/selmodule.rb new file mode 100755 index 000000000..e92441d23 --- /dev/null +++ b/spec/unit/provider/selmodule.rb @@ -0,0 +1,66 @@ +#!/usr/bin/env ruby + +# Note: This unit test depends on having a sample SELinux policy file +# in the same directory as this test called selmodule-example.pp +# with version 1.5.0. The provided selmodule-example.pp is the first +# 256 bytes taken from /usr/share/selinux/targeted/nagios.pp on Fedora 9 + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider_class = Puppet::Type.type(:selmodule).provider(:semodule) + +describe provider_class do + before :each do + @resource = stub("resource", :name => "foo") + @resource.stubs(:[]).returns "foo" + @provider = provider_class.new(@resource) + end + + describe "exists? method" do + it "should find a module if it is already loaded" do + @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields ["bar\t1.2.3\n", "foo\t4.4.4\n", "bang\t1.0.0\n"] + @provider.exists?.should == :true + end + + it "should return nil if not loaded" do + @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields ["bar\t1.2.3\n", "bang\t1.0.0\n"] + @provider.exists?.should be_nil + end + + it "should return nil if no modules are loaded" do + @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields [] + @provider.exists?.should be_nil + end + end + + describe "selmodversion_file" do + it "should return 1.5.0 for the example policy file" do + @provider.expects(:selmod_name_to_filename).returns "#{File.dirname(__FILE__)}/selmodule-example.pp" + @provider.selmodversion_file.should == "1.5.0" + end + end + + describe "syncversion" do + it "should return :true if loaded and file modules are in sync" do + @provider.expects(:selmodversion_loaded).returns "1.5.0" + @provider.expects(:selmodversion_file).returns "1.5.0" + @provider.syncversion.should == :true + end + + it "should return :false if loaded and file modules are not in sync" do + @provider.expects(:selmodversion_loaded).returns "1.4.0" + @provider.expects(:selmodversion_file).returns "1.5.0" + @provider.syncversion.should == :false + end + + it "should return before checking file version if no loaded policy" do + @provider.expects(:selmodversion_loaded).returns nil + @provider.syncversion.should == :false + end + + end + +end diff --git a/spec/unit/provider/user/user_role_add.rb b/spec/unit/provider/user/user_role_add.rb new file mode 100644 index 000000000..e9bd9a68f --- /dev/null +++ b/spec/unit/provider/user/user_role_add.rb @@ -0,0 +1,131 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:user).provider(:user_role_add) + +describe provider_class do + before do + @resource = stub("resource", :name => "myuser", :managehome? => nil) + @resource.stubs(:should).returns "fakeval" + @resource.stubs(:[]).returns "fakeval" + @resource.stubs(:allowdupe?).returns false + @provider = provider_class.new(@resource) + end + + describe "when calling command" do + before do + klass = stub("provider") + klass.stubs(:command).with(:foo).returns("userfoo") + klass.stubs(:command).with(:role_foo).returns("rolefoo") + @provider.stubs(:class).returns(klass) + end + + it "should use the command if not a role and ensure!=role" do + @provider.stubs(:is_role?).returns(false) + @provider.stubs(:exists?).returns(false) + @resource.stubs(:[]).with(:ensure).returns(:present) + @provider.command(:foo).should == "userfoo" + end + + it "should use the role command when a role" do + @provider.stubs(:is_role?).returns(true) + @provider.command(:foo).should == "rolefoo" + end + + it "should use the role command when !exists and ensure=role" do + @provider.stubs(:is_role?).returns(false) + @provider.stubs(:exists?).returns(false) + @resource.stubs(:[]).with(:ensure).returns(:role) + @provider.command(:foo).should == "rolefoo" + end + end + + describe "when calling transition" do + it "should return foomod setting the type to bar" do + @provider.expects(:command).with(:modify).returns("foomod") + @provider.transition("bar").should == ["foomod", "-K", "type=bar", "fakeval"] + end + end + + describe "when calling create" do + it "should use the add command when the user is not a role" do + @provider.stubs(:is_role?).returns(false) + @provider.expects(:addcmd).returns("useradd") + @provider.expects(:run) + @provider.create + end + + it "should use transition(normal) when the user is a role" do + @provider.stubs(:is_role?).returns(true) + @provider.expects(:transition).with("normal") + @provider.expects(:run) + @provider.create + end + end + + describe "when calling destroy" do + it "should use the delete command if the user exists and is not a role" do + @provider.stubs(:exists?).returns(true) + @provider.stubs(:is_role?).returns(false) + @provider.expects(:deletecmd) + @provider.expects(:run) + @provider.destroy + end + + it "should use the delete command if the user is a role" do + @provider.stubs(:exists?).returns(true) + @provider.stubs(:is_role?).returns(true) + @provider.expects(:deletecmd) + @provider.expects(:run) + @provider.destroy + end + end + + describe "when calling create_role" do + it "should use the transition(role) if the user exists" do + @provider.stubs(:exists?).returns(true) + @provider.stubs(:is_role?).returns(false) + @provider.expects(:transition).with("role") + @provider.expects(:run) + @provider.create_role + end + + it "should use the add command when role doesn't exists" do + @provider.stubs(:exists?).returns(false) + @provider.expects(:addcmd) + @provider.expects(:run) + @provider.create_role + end + end + + describe "when allow duplicate is enabled" do + before do + @resource.expects(:allowdupe?).returns true + @provider.stubs(:is_role?).returns(false) + @provider.expects(:execute).with { |args| args.include?("-o") } + end + + it "should add -o when the user is being created" do + @provider.create + end + + it "should add -o when the uid is being modified" do + @provider.uid = 150 + end + end + + describe "when getting roles" do + it "should get the user_attributes" do + @provider.expects(:user_attributes) + @provider.roles + end + + it "should get the :roles attribute" do + attributes = mock("attributes") + attributes.expects(:[]).with(:roles) + @provider.stubs(:user_attributes).returns(attributes) + @provider.roles + end + end +end diff --git a/spec/unit/type.rb b/spec/unit/type.rb index 9815ed32d..4c0cb5077 100755 --- a/spec/unit/type.rb +++ b/spec/unit/type.rb @@ -2,28 +2,61 @@ require File.dirname(__FILE__) + '/../spec_helper' -describe Puppet::Type, " when in a configuration" do - before do - @catalog = Puppet::Node::Catalog.new - @container = Puppet::Type.type(:component).create(:name => "container") - @one = Puppet::Type.type(:file).create(:path => "/file/one") - @two = Puppet::Type.type(:file).create(:path => "/file/two") - @catalog.add_resource @container - @catalog.add_resource @one - @catalog.add_resource @two - @catalog.add_edge @container, @one - @catalog.add_edge @container, @two - end +describe Puppet::Type do + describe "when retrieving current properties" do + # Use 'mount' as an example, because it doesn't override 'retrieve' + before do + @resource = Puppet::Type.type(:mount).create(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) + @properties = {} + end - it "should have no parent if there is no in edge" do - @container.parent.should be_nil - end + it "should return a hash containing values for all set properties" do + values = @resource.retrieve + [@resource.property(:fstype), @resource.property(:pass)].each { |property| values.should be_include(property) } + end + + it "should not call retrieve on non-ensure properties if the resource is absent" do + @resource.property(:ensure).expects(:retrieve).returns :absent + @resource.property(:fstype).expects(:retrieve).never + @resource.retrieve[@resource.property(:fstype)] + end + + it "should set all values to :absent if the resource is absent" do + @resource.property(:ensure).expects(:retrieve).returns :absent + @resource.retrieve[@resource.property(:fstype)].should == :absent + end - it "should set its parent to its in edge" do - @one.parent.ref.should == @container.ref + it "should include the result of retrieving each property's current value if the resource is present" do + @resource.property(:ensure).expects(:retrieve).returns :present + @resource.property(:fstype).expects(:retrieve).returns 15 + @resource.retrieve[@resource.property(:fstype)].should == 15 + end end - after do - @catalog.clear(true) + + describe "when in a catalog" do + before do + @catalog = Puppet::Node::Catalog.new + @container = Puppet::Type.type(:component).create(:name => "container") + @one = Puppet::Type.type(:file).create(:path => "/file/one") + @two = Puppet::Type.type(:file).create(:path => "/file/two") + @catalog.add_resource @container + @catalog.add_resource @one + @catalog.add_resource @two + @catalog.add_edge @container, @one + @catalog.add_edge @container, @two + end + + it "should have no parent if there is no in edge" do + @container.parent.should be_nil + end + + it "should set its parent to its in edge" do + @one.parent.ref.should == @container.ref + end + + after do + @catalog.clear(true) + end end end diff --git a/spec/unit/type/file/group.rb b/spec/unit/type/file/group.rb new file mode 100755 index 000000000..856b05b0d --- /dev/null +++ b/spec/unit/type/file/group.rb @@ -0,0 +1,118 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +property = Puppet::Type.type(:file).attrclass(:group) + +describe property do + before do + @resource = stub 'resource', :line => "foo", :file => "bar" + @resource.stubs(:[]).returns "foo" + @resource.stubs(:[]).with(:path).returns "/my/file" + @group = property.new :resource => @resource + end + + it "should have a method for testing whether a group is valid" do + @group.must respond_to(:validgroup?) + end + + it "should return the found gid if a group is valid" do + @group.expects(:gid).with("foo").returns 500 + @group.validgroup?("foo").should == 500 + end + + it "should return false if a group is not valid" do + @group.expects(:gid).with("foo").returns nil + @group.validgroup?("foo").should be_false + end + + describe "when retrieving the current value" do + it "should return :absent if the file cannot stat" do + @resource.expects(:stat).returns nil + + @group.retrieve.should == :absent + end + + it "should get the gid from the stat instance from the file" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + stat.expects(:gid).returns 500 + + @group.retrieve.should == 500 + end + + it "should warn and return :silly if the found value is higher than the maximum uid value" do + Puppet.settings.expects(:value).with(:maximum_uid).returns 500 + + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + stat.expects(:gid).returns 1000 + + @group.expects(:warning) + @group.retrieve.should == :silly + end + end + + describe "when determining if the file is in sync" do + it "should directly compare the group values if the desired group is an integer" do + @group.should = [10] + @group.must be_insync(10) + end + + it "should treat numeric strings as integers" do + @group.should = ["10"] + @group.must be_insync(10) + end + + it "should convert the group name to an integer if the desired group is a string" do + @group.expects(:gid).with("foo").returns 10 + @group.should = %w{foo} + + @group.must be_insync(10) + end + + it "should fail if it cannot convert a group name to an integer" do + @group.expects(:gid).with("foo").returns nil + @group.should = %w{foo} + + lambda { @group.insync?(10) }.should raise_error(Puppet::Error) + end + + it "should return false if the groups are not equal" do + @group.should = [10] + @group.should_not be_insync(20) + end + end + + describe "when changing the group" do + before do + @group.should = %w{one} + @group.stubs(:gid).returns 500 + end + + it "should chown the file if :links is set to :follow" do + @resource.expects(:[]).with(:links).returns :follow + File.expects(:chown) + + @group.sync + end + + it "should lchown the file if :links is set to :manage" do + @resource.expects(:[]).with(:links).returns :manage + File.expects(:lchown) + + @group.sync + end + + it "should use the first valid group in its 'should' list" do + @group.should = %w{one two three} + @group.expects(:validgroup?).with("one").returns nil + @group.expects(:validgroup?).with("two").returns 500 + @group.expects(:validgroup?).with("three").never + + File.expects(:chown).with(nil, 500, "/my/file") + + @group.sync + end + end +end diff --git a/spec/unit/type/file/selinux.rb b/spec/unit/type/file/selinux.rb new file mode 100644 index 000000000..cee47c342 --- /dev/null +++ b/spec/unit/type/file/selinux.rb @@ -0,0 +1,78 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + + +[:seluser, :selrole, :seltype, :selrange].each do |param| +property = Puppet::Type.type(:file).attrclass(param) + describe property do + before do + @resource = mock 'resource' + @resource.stubs(:[]).with(:path).returns "/my/file" + @sel = property.new :resource => @resource + end + + it "retrieve on #{param} should return :absent if the file isn't statable" do + @resource.expects(:stat).returns nil + @sel.retrieve.should == :absent + end + + it "should retrieve nil for #{param} if there is no SELinux support" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + @sel.expects(:get_selinux_current_context).with("/my/file").returns nil + @sel.retrieve.should be_nil + end + + it "should retrieve #{param} if a SELinux context is found with a range" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t:s0" + expectedresult = case param + when :seluser then "user_u" + when :selrole then "role_r" + when :seltype then "type_t" + when :selrange then "s0" + end + @sel.retrieve.should == expectedresult + end + + it "should retrieve #{param} if a SELinux context is found without a range" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t" + expectedresult = case param + when :seluser then "user_u" + when :selrole then "role_r" + when :seltype then "type_t" + when :selrange then nil + end + @sel.retrieve.should == expectedresult + end + + it "should handle no default gracefully" do + @sel.expects(:get_selinux_default_context).with("/my/file").returns nil + @sel.default.must be_nil + end + + it "should be able to detect matchpathcon defaults" do + @sel.expects(:get_selinux_default_context).with("/my/file").returns "user_u:role_r:type_t:s0" + expectedresult = case param + when :seluser then "user_u" + when :selrole then "role_r" + when :seltype then "type_t" + when :selrange then "s0" + end + @sel.default.must == expectedresult + end + + it "should be able to set a new context" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + @sel.should = %w{newone} + @sel.expects(:set_selinux_context).with("/my/file", ["newone"], param) + @sel.sync + end + end +end + diff --git a/spec/unit/type/selboolean.rb b/spec/unit/type/selboolean.rb new file mode 100755 index 000000000..6efec49ca --- /dev/null +++ b/spec/unit/type/selboolean.rb @@ -0,0 +1,45 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +describe Puppet::Type.type(:selboolean), "when validating attributes" do + [:name, :persistent].each do |param| + it "should have a #{param} parameter" do + Puppet::Type.type(:selboolean).attrtype(param).should == :param + end + end + + it "should have a value property" do + Puppet::Type.type(:selboolean).attrtype(:value).should == :property + end +end + +describe Puppet::Type.type(:selboolean), "when validating values" do + before do + @class = Puppet::Type.type(:selboolean) + + @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true + @class.stubs(:defaultprovider).returns(@provider_class) + @class.stubs(:provider).returns(@provider_class) + + @provider = stub 'provider', :class => @provider_class, :clear => nil + @provider_class.stubs(:new).returns(@provider) + end + + it "should support :on as a value to :value" do + Puppet::Type.type(:selboolean).create(:name => "yay", :value => :on) + end + + it "should support :off as a value to :value" do + Puppet::Type.type(:selboolean).create(:name => "yay", :value => :off) + end + + it "should support :true as a value to :persistent" do + Puppet::Type.type(:selboolean).create(:name => "yay", :value => :on, :persistent => :true) + end + + it "should support :false as a value to :persistent" do + Puppet::Type.type(:selboolean).create(:name => "yay", :value => :on, :persistent => :false) + end +end + diff --git a/spec/unit/type/selmodule.rb b/spec/unit/type/selmodule.rb new file mode 100755 index 000000000..f14bea9d3 --- /dev/null +++ b/spec/unit/type/selmodule.rb @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +describe Puppet::Type.type(:selmodule), "when validating attributes" do + [:name, :selmoduledir, :selmodulepath].each do |param| + it "should have a #{param} parameter" do + Puppet::Type.type(:selmodule).attrtype(param).should == :param + end + end + + [:ensure, :syncversion].each do |param| + it "should have a #{param} property" do + Puppet::Type.type(:selmodule).attrtype(param).should == :property + end + end +end + diff --git a/spec/unit/type/user.rb b/spec/unit/type/user.rb index d16d752f9..1271753d4 100755 --- a/spec/unit/type/user.rb +++ b/spec/unit/type/user.rb @@ -2,55 +2,184 @@ require File.dirname(__FILE__) + '/../../spec_helper' -module UserTestFunctions - def mkuser(name) - user = nil; - lambda { - user = Puppet::Type.type(:user).create( - :name => name, - :comment => "Puppet Testing User", - :gid => Puppet::Util::SUIDManager.gid, - :shell => "/bin/sh", - :home => "/home/%s" % name - ) }.should_not raise_error - user.should_not be_nil - user - end - - def test_provider_class(klass) - klass.should_not be_nil - klass.should be_an_instance_of(Class) - superclasses = [] - while klass = klass.superclass - superclasses << klass - end - superclasses.should include(Puppet::Provider) - end -end - -describe Puppet::Type.type(:user) do +user = Puppet::Type.type(:user) - include UserTestFunctions +describe user do + before do + @provider = stub 'provider' + @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil + end it "should have a default provider inheriting from Puppet::Provider" do - test_provider_class Puppet::Type.type(:user).defaultprovider + user.defaultprovider.ancestors.should be_include(Puppet::Provider) end it "should be able to create a instance" do - mkuser "123testuser1" + user.create(:name => "foo").should_not be_nil end -end -describe Puppet::Type.type(:user), "instances" do + it "should have an allows_duplicates feature" do + user.provider_feature(:allows_duplicates).should_not be_nil + end - include UserTestFunctions + it "should have an manages_homedir feature" do + user.provider_feature(:manages_homedir).should_not be_nil + end - it "should have a valid provider" do - user = mkuser "123testuser2" - user.provider.should_not be_nil - test_provider_class user.provider.class + it "should have an manages_passwords feature" do + user.provider_feature(:manages_passwords).should_not be_nil end -end + describe "instances" do + it "should have a valid provider" do + user.create(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) + end + end + + [:ensure, :uid, :gid, :home, :comment, :shell, :password, :groups].each do |property| + it "should have a %s property" % property do + user.attrclass(property).ancestors.should be_include(Puppet::Property) + end + + it "should have documentation for its %s property" % property do + user.attrclass(property).doc.should be_instance_of(String) + end + end + + describe "when retrieving all current values" do + before do + @user = user.create(:name => "foo", :uid => 10, :gid => 10) + @properties = {} + end + it "should return a hash containing values for all set properties" do + values = @user.retrieve + [@user.property(:uid), @user.property(:gid)].each { |property| values.should be_include(property) } + end + + it "should set all values to :absent if the user is absent" do + @user.property(:ensure).expects(:retrieve).returns :absent + @user.property(:uid).expects(:retrieve).never + @user.retrieve[@user.property(:uid)].should == :absent + end + + it "should include the result of retrieving each property's current value if the user is present" do + @user.property(:ensure).expects(:retrieve).returns :present + @user.property(:uid).expects(:retrieve).returns 15 + @user.retrieve[@user.property(:uid)].should == 15 + end + end + + describe "when managing the ensure property" do + before do + @ensure = user.attrclass(:ensure).new(:resource => @resource) + end + + it "should support a :present value" do + lambda { @ensure.should = :present }.should_not raise_error + end + + it "should support an :absent value" do + lambda { @ensure.should = :absent }.should_not raise_error + end + + it "should call :create on the provider when asked to sync to the :present state" do + @provider.expects(:create) + @ensure.should = :present + @ensure.sync + end + + it "should call :delete on the provider when asked to sync to the :absent state" do + @provider.expects(:delete) + @ensure.should = :absent + @ensure.sync + end + describe "and determining the current state" do + it "should return :present when the provider indicates the user exists" do + @provider.expects(:exists?).returns true + @ensure.retrieve.should == :present + end + + it "should return :absent when the provider indicates the user does not exist" do + @provider.expects(:exists?).returns false + @ensure.retrieve.should == :absent + end + end + end + + describe "when managing the uid property" do + it "should convert number-looking strings into actual numbers" do + uid = user.attrclass(:uid).new(:resource => @resource) + uid.should = "50" + uid.should.must == 50 + end + + it "should support UIDs as numbers" do + uid = user.attrclass(:uid).new(:resource => @resource) + uid.should = 50 + uid.should.must == 50 + end + + it "should :absent as a value" do + uid = user.attrclass(:uid).new(:resource => @resource) + uid.should = :absent + uid.should.must == :absent + end + end + + describe "when managing the gid" do + it "should :absent as a value" do + gid = user.attrclass(:gid).new(:resource => @resource) + gid.should = :absent + gid.should.must == :absent + end + + it "should convert number-looking strings into actual numbers" do + gid = user.attrclass(:gid).new(:resource => @resource) + gid.should = "50" + gid.should.must == 50 + end + + it "should support GIDs specified as integers" do + gid = user.attrclass(:gid).new(:resource => @resource) + gid.should = 50 + gid.should.must == 50 + end + + it "should support groups specified by name" do + gid = user.attrclass(:gid).new(:resource => @resource) + gid.should = "foo" + gid.should.must == "foo" + end + + describe "when syncing" do + before do + @gid = user.attrclass(:gid).new(:resource => @resource, :should => %w{foo bar}) + end + + it "should use the first found, specified group as the desired value and send it to the provider" do + Puppet::Util.expects(:gid).with("foo").returns nil + Puppet::Util.expects(:gid).with("bar").returns 500 + + @provider.expects(:gid=).with 500 + + @gid.sync + end + end + end + + describe "when managing passwords" do + before do + @password = user.attrclass(:password).new(:resource => @resource, :should => "mypass") + end + + it "should not include the password in the change log when adding the password" do + @password.change_to_s(:absent, "mypass").should_not be_include("mypass") + end + + it "should not include the password in the change log when changing the password" do + @password.change_to_s("other", "mypass").should_not be_include("mypass") + end + end +end diff --git a/spec/unit/util/log.rb b/spec/unit/util/log.rb new file mode 100755 index 000000000..aa00602a9 --- /dev/null +++ b/spec/unit/util/log.rb @@ -0,0 +1,153 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/log' + +describe Puppet::Util::Log do + describe "instances" do + before do + Puppet::Util::Log.stubs(:newmessage) + end + + [:level, :message, :time, :remote].each do |attr| + it "should have a %s attribute" % attr do + log = Puppet::Util::Log.new :level => :notice, :message => "A test message" + log.should respond_to(attr) + log.should respond_to(attr.to_s + "=") + end + end + + it "should fail if created without a level" do + lambda { Puppet::Util::Log.new(:message => "A test message") }.should raise_error(ArgumentError) + end + + it "should fail if created without a message" do + lambda { Puppet::Util::Log.new(:level => :notice) }.should raise_error(ArgumentError) + end + + it "should make available the level passed in at initialization" do + Puppet::Util::Log.new(:level => :notice, :message => "A test message").level.should == :notice + end + + it "should make available the message passed in at initialization" do + Puppet::Util::Log.new(:level => :notice, :message => "A test message").message.should == "A test message" + end + + # LAK:NOTE I don't know why this behavior is here, I'm just testing what's in the code, + # at least at first. + it "should always convert messages to strings" do + Puppet::Util::Log.new(:level => :notice, :message => :foo).message.should == "foo" + end + + it "should convert the level to a symbol if it's passed in as a string" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).level.should == :notice + end + + it "should fail if the level is not a symbol or string" do + lambda { Puppet::Util::Log.new(:level => 50, :message => :foo) }.should raise_error(ArgumentError) + end + + it "should fail if the provided level is not valid" do + Puppet::Util::Log.expects(:validlevel?).with(:notice).returns false + lambda { Puppet::Util::Log.new(:level => :notice, :message => :foo) }.should raise_error(ArgumentError) + end + + it "should set its time to the initialization time" do + time = mock 'time' + Time.expects(:now).returns time + Puppet::Util::Log.new(:level => "notice", :message => :foo).time.should equal(time) + end + + it "should make available any passed-in tags" do + log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{foo bar}) + log.tags.should be_include("foo") + log.tags.should be_include("bar") + end + + it "should use an passed-in source" do + Puppet::Util::Log.any_instance.expects(:source=).with "foo" + Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => "foo") + end + + it "should default to 'Puppet' as its source" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).source.should == "Puppet" + end + + it "should register itself with Log" do + Puppet::Util::Log.expects(:newmessage) + Puppet::Util::Log.new(:level => "notice", :message => :foo) + end + + it "should have a method for determining if a tag is present" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).should respond_to(:tagged?) + end + + it "should match a tag if any of the tags are equivalent to the passed tag as a string" do + Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{one two}).should be_tagged(:one) + end + + it "should tag itself with its log level" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).should be_tagged(:notice) + end + + it "should return its message when converted to a string" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).to_s.should == "foo" + end + + it "should include its time, source, level, and message when prepared for reporting" do + log = Puppet::Util::Log.new(:level => "notice", :message => :foo) + report = log.to_report + report.should be_include("notice") + report.should be_include("foo") + report.should be_include(log.source) + report.should be_include(log.time.to_s) + end + + it "should have a method for indicating whether it was created by a resource" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).should respond_to(:objectsource?) + end + + describe "when setting a source" do + it "should mark itself as from a Puppet resource if its source is a Puppet resource" do + file = Puppet::Type.type(:file).create :path => "/testing/object/source/in/logs" + Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => file).should be_objectsource + end + + it "should use the resource's path when its source is a resource" do + # Use a different path, so we don't use 'clear', which is deprecated in master + file = Puppet::Type.type(:file).create :path => "/testing/object/source/in/logs/with/path" + file.expects(:path).returns "mypath" + Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => file).source.should == "mypath" + end + + it "should mark itself as from a Puppet resource if its source is a Puppet parameter" do + file = Puppet::Type.type(:file).create :path => "/testing/object/source/in/logs/with/parameters", :mode => "500" + mode = file.property(:mode) + Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => mode).should be_objectsource + end + + it "should use the resource's path when its source is a Puppet parameter" do + # Use a different path, so we don't use 'clear', which is deprecated in master + file = Puppet::Type.type(:file).create :path => "/testing/object/source/in/logs/with/path/in/parameters", :mode => "500" + mode = file.property(:mode) + mode.expects(:path).returns "mypath" + Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => mode).source.should == "mypath" + end + + it "should acquire its source's tags if its source has any" do + file = Puppet::Type.type(:file).create :path => "/testing/object/source/in/logs/with/tags" + file.tag("foo") + file.tag("bar") + log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => file) + + log.should be_tagged("foo") + log.should be_tagged("bar") + end + + it "should not set objectsource if the source is not a Parameter or Resource" do + Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => "mysource").should_not be_objectsource + end + end + end +end diff --git a/spec/unit/util/metric.rb b/spec/unit/util/metric.rb new file mode 100755 index 000000000..3501aac90 --- /dev/null +++ b/spec/unit/util/metric.rb @@ -0,0 +1,95 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/metric' + +describe Puppet::Util::Metric do + before do + @metric = Puppet::Util::Metric.new("foo") + end + + it "should be aliased to Puppet::Metric" do + Puppet::Util::Metric.should equal(Puppet::Metric) + end + + [:type, :name, :value, :label, :basedir].each do |name| + it "should have a #{name} attribute" do + @metric.should respond_to(name) + @metric.should respond_to(name.to_s + "=") + end + end + + it "should default to the :rrdir as the basedir "do + Puppet.settings.expects(:value).with(:rrddir).returns "myrrd" + @metric.basedir.should == "myrrd" + end + + it "should use any provided basedir" do + @metric.basedir = "foo" + @metric.basedir.should == "foo" + end + + it "should require a name at initialization" do + lambda { Puppet::Util::Metric.new }.should raise_error(ArgumentError) + end + + it "should always convert its name to a string" do + Puppet::Util::Metric.new(:foo).name.should == "foo" + end + + it "should support a label" do + Puppet::Util::Metric.new("foo", "mylabel").label.should == "mylabel" + end + + it "should autogenerate a label if none is provided" do + Puppet::Util::Metric.new("foo_bar").label.should == "Foo bar" + end + + it "should have a method for adding values" do + @metric.should respond_to(:newvalue) + end + + it "should have a method for returning values" do + @metric.should respond_to(:values) + end + + it "should require a name and value for its values" do + lambda { @metric.newvalue }.should raise_error(ArgumentError) + end + + it "should support a label for values" do + @metric.newvalue(:foo, 10, "label") + @metric.values[0][1].should == "label" + end + + it "should autogenerate value labels if none is provided" do + @metric.newvalue("foo_bar", 10) + @metric.values[0][1].should == "Foo bar" + end + + it "should return its values sorted by label" do + @metric.newvalue(:foo, 10, "b") + @metric.newvalue(:bar, 10, "a") + + @metric.values.should == [[:bar, "a", 10], [:foo, "b", 10]] + end + + it "should use an array indexer method to retrieve individual values" do + @metric.newvalue(:foo, 10) + @metric[:foo].should == 10 + end + + it "should return nil if the named value cannot be found" do + @metric[:foo].should be_nil + end + + # LAK: I'm not taking the time to develop these tests right now. + # I expect they should actually be extracted into a separate class + # anyway. + it "should be able to graph metrics using RRDTool" + + it "should be able to create a new RRDTool database" + + it "should be able to store metrics into an RRDTool database" +end diff --git a/spec/unit/util/posix.rb b/spec/unit/util/posix.rb new file mode 100755 index 000000000..95a5608bd --- /dev/null +++ b/spec/unit/util/posix.rb @@ -0,0 +1,256 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/posix' + +class PosixTest + include Puppet::Util::POSIX +end + +describe Puppet::Util::POSIX do + before do + @posix = PosixTest.new + end + + [:group, :gr].each do |name| + it "should return :gid as the field for %s" % name do + @posix.idfield(name).should == :gid + end + + it "should return :getgrgid as the id method for %s" % name do + @posix.methodbyid(name).should == :getgrgid + end + + it "should return :getgrnam as the name method for %s" % name do + @posix.methodbyname(name).should == :getgrnam + end + end + + [:user, :pw, :passwd].each do |name| + it "should return :uid as the field for %s" % name do + @posix.idfield(name).should == :uid + end + + it "should return :getpwuid as the id method for %s" % name do + @posix.methodbyid(name).should == :getpwuid + end + + it "should return :getpwnam as the name method for %s" % name do + @posix.methodbyname(name).should == :getpwnam + end + end + + describe "when retrieving a posix field" do + before do + @thing = stub 'thing', :field => "asdf" + end + + it "should fail if no id was passed" do + lambda { @posix.get_posix_field("asdf", "bar", nil) }.should raise_error(Puppet::DevError) + end + + describe "and the id is an integer" do + it "should log an error and return nil if the specified id is greater than the maximum allowed ID" do + Puppet.settings.expects(:value).with(:maximum_uid).returns 100 + Puppet.expects(:err) + + @posix.get_posix_field("asdf", "bar", 200).should be_nil + end + + it "should use the method return by :methodbyid and return the specified field" do + Etc.expects(:getgrgid).returns @thing + + @thing.expects(:field).returns "myval" + + @posix.get_posix_field(:gr, :field, 200).should == "myval" + end + + it "should return nil if the method throws an exception" do + Etc.expects(:getgrgid).raises ArgumentError + + @thing.expects(:field).never + + @posix.get_posix_field(:gr, :field, 200).should be_nil + end + end + + describe "and the id is not an integer" do + it "should use the method return by :methodbyid and return the specified field" do + Etc.expects(:getgrnam).returns @thing + + @thing.expects(:field).returns "myval" + + @posix.get_posix_field(:gr, :field, "asdf").should == "myval" + end + + it "should return nil if the method throws an exception" do + Etc.expects(:getgrnam).raises ArgumentError + + @thing.expects(:field).never + + @posix.get_posix_field(:gr, :field, "asdf").should be_nil + end + end + end + + describe "when returning the gid" do + before do + @posix.stubs(:get_posix_field) + end + + describe "and the group is an integer" do + it "should convert integers specified as a string into an integer" do + @posix.expects(:get_posix_field).with(:group, :name, 100) + + @posix.gid("100") + end + + it "should look up the name for the group" do + @posix.expects(:get_posix_field).with(:group, :name, 100) + + @posix.gid(100) + end + + it "should return nil if the group cannot be found" do + @posix.expects(:get_posix_field).once.returns nil + @posix.expects(:search_posix_field).never + + @posix.gid(100).should be_nil + end + + it "should use the found name to look up the id" do + @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" + @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 + + @posix.gid(100).should == 100 + end + + # LAK: This is because some platforms have a broken Etc module that always return + # the same group. + it "should use :search_posix_field if the discovered id does not match the passed-in id" do + @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" + @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 50 + + @posix.expects(:search_posix_field).with(:group, :gid, 100).returns "asdf" + + @posix.gid(100).should == "asdf" + end + end + + describe "and the group is a string" do + it "should look up the gid for the group" do + @posix.expects(:get_posix_field).with(:group, :gid, "asdf") + + @posix.gid("asdf") + end + + it "should return nil if the group cannot be found" do + @posix.expects(:get_posix_field).once.returns nil + @posix.expects(:search_posix_field).never + + @posix.gid("asdf").should be_nil + end + + it "should use the found gid to look up the nam" do + @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 + @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" + + @posix.gid("asdf").should == 100 + end + + it "should use :search_posix_field if the discovered name does not match the passed-in name" do + @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 + @posix.expects(:get_posix_field).with(:group, :name, 100).returns "boo" + + @posix.expects(:search_posix_field).with(:group, :gid, "asdf").returns "asdf" + + @posix.gid("asdf").should == "asdf" + end + end + end + + describe "when returning the uid" do + before do + @posix.stubs(:get_posix_field) + end + + describe "and the group is an integer" do + it "should convert integers specified as a string into an integer" do + @posix.expects(:get_posix_field).with(:passwd, :name, 100) + + @posix.uid("100") + end + + it "should look up the name for the group" do + @posix.expects(:get_posix_field).with(:passwd, :name, 100) + + @posix.uid(100) + end + + it "should return nil if the group cannot be found" do + @posix.expects(:get_posix_field).once.returns nil + @posix.expects(:search_posix_field).never + + @posix.uid(100).should be_nil + end + + it "should use the found name to look up the id" do + @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 + + @posix.uid(100).should == 100 + end + + # LAK: This is because some platforms have a broken Etc module that always return + # the same group. + it "should use :search_posix_field if the discovered id does not match the passed-in id" do + @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 50 + + @posix.expects(:search_posix_field).with(:passwd, :uid, 100).returns "asdf" + + @posix.uid(100).should == "asdf" + end + end + + describe "and the group is a string" do + it "should look up the uid for the group" do + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf") + + @posix.uid("asdf") + end + + it "should return nil if the group cannot be found" do + @posix.expects(:get_posix_field).once.returns nil + @posix.expects(:search_posix_field).never + + @posix.uid("asdf").should be_nil + end + + it "should use the found uid to look up the nam" do + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 + @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" + + @posix.uid("asdf").should == 100 + end + + it "should use :search_posix_field if the discovered name does not match the passed-in name" do + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 + @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "boo" + + @posix.expects(:search_posix_field).with(:passwd, :uid, "asdf").returns "asdf" + + @posix.uid("asdf").should == "asdf" + end + end + end + + it "should be able to iteratively search for posix values" do + @posix.should respond_to(:search_posix_field) + end + + describe "when searching for posix values iteratively" do + it "should iterate across all of the structs returned by Etc and return the appropriate field from the first matching value" + end +end diff --git a/spec/unit/util/selinux.rb b/spec/unit/util/selinux.rb new file mode 100644 index 000000000..515c3a273 --- /dev/null +++ b/spec/unit/util/selinux.rb @@ -0,0 +1,174 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/selinux' +include Puppet::Util::SELinux + +describe Puppet::Util::SELinux do + + describe "selinux_support?" do + it "should return :true if this system has SELinux enabled" do + FileTest.expects(:exists?).with("/selinux/enforce").returns true + selinux_support?.should be_true + end + + it "should return :false if this system lacks SELinux" do + FileTest.expects(:exists?).with("/selinux/enforce").returns false + selinux_support?.should be_false + end + end + + describe "get_selinux_current_context" do + it "should return nil if no SELinux support" do + self.expects(:selinux_support?).returns false + get_selinux_current_context("/foo").should be_nil + end + + it "should return a context" do + self.expects(:selinux_support?).returns true + self.expects(:execpipe).with("/usr/bin/stat -c %C /foo").yields ["user_u:role_r:type_t:s0\n"] + get_selinux_current_context("/foo").should == "user_u:role_r:type_t:s0" + end + + it "should return nil if an exception is raised calling stat" do + self.expects(:selinux_support?).returns true + self.expects(:execpipe).with("/usr/bin/stat -c %C /foo").raises(Puppet::ExecutionFailure, 'error') + get_selinux_current_context("/foo").should be_nil + end + + it "should return nil if stat finds an unlabeled file" do + self.expects(:selinux_support?).returns true + self.expects(:execpipe).with("/usr/bin/stat -c %C /foo").yields ["(null)\n"] + get_selinux_current_context("/foo").should be_nil + end + end + + describe "get_selinux_default_context" do + it "should return nil if no SELinux support" do + self.expects(:selinux_support?).returns false + get_selinux_default_context("/foo").should be_nil + end + + it "should return nil if matchpathcon is not executable" do + self.expects(:selinux_support?).returns true + FileTest.expects(:executable?).with("/usr/sbin/matchpathcon").returns false + get_selinux_default_context("/foo").should be_nil + end + + it "should return a context if a default context exists" do + self.expects(:selinux_support?).returns true + FileTest.expects(:executable?).with("/usr/sbin/matchpathcon").returns true + self.expects(:execpipe).with("/usr/sbin/matchpathcon /foo").yields ["/foo\tuser_u:role_r:type_t:s0\n"] + get_selinux_default_context("/foo").should == "user_u:role_r:type_t:s0" + end + + it "should return nil if an exception is raised calling matchpathcon" do + self.expects(:selinux_support?).returns true + FileTest.expects(:executable?).with("/usr/sbin/matchpathcon").returns true + self.expects(:execpipe).with("/usr/sbin/matchpathcon /foo").raises(Puppet::ExecutionFailure, 'error') + get_selinux_default_context("/foo").should be_nil + end + end + + describe "parse_selinux_context" do + it "should return nil if no context is passed" do + parse_selinux_context(:seluser, nil).should be_nil + end + + it "should return nil if the context is 'unlabeled'" do + parse_selinux_context(:seluser, "unlabeled").should be_nil + end + + it "should return the user type when called with :seluser" do + parse_selinux_context(:seluser, "user_u:role_r:type_t:s0").should == "user_u" + end + + it "should return the role type when called with :selrole" do + parse_selinux_context(:selrole, "user_u:role_r:type_t:s0").should == "role_r" + end + + it "should return the type type when called with :seltype" do + parse_selinux_context(:seltype, "user_u:role_r:type_t:s0").should == "type_t" + end + + it "should return nil for :selrange when no range is returned" do + parse_selinux_context(:selrange, "user_u:role_r:type_t").should be_nil + end + + it "should return the range type when called with :selrange" do + parse_selinux_context(:selrange, "user_u:role_r:type_t:s0").should == "s0" + end + + describe "with a variety of SELinux range formats" do + ['s0', 's0:c3', 's0:c3.c123', 's0:c3,c5,c8', 'TopSecret', 'TopSecret,Classified', 'Patient_Record'].each do |range| + it "should parse range '#{range}'" do + parse_selinux_context(:selrange, "user_u:role_r:type_t:#{range}").should == range + end + end + end + end + + describe "set_selinux_context" do + it "should return nil if there is no SELinux support" do + self.expects(:selinux_support?).returns false + set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil + end + + it "should use chcon to set a context" do + self.expects(:selinux_support?).returns true + self.expects(:execute).with(["/usr/bin/chcon","-h","","user_u:role_r:type_t:s0","/foo"]).returns 0 + set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_true + end + + it "should use chcon to set user_u user context" do + self.expects(:selinux_support?).returns true + self.expects(:execute).with(["/usr/bin/chcon","-h","-u","user_u","/foo"]).returns 0 + set_selinux_context("/foo", "user_u", :seluser).should be_true + end + + it "should use chcon to set role_r role context" do + self.expects(:selinux_support?).returns true + self.expects(:execute).with(["/usr/bin/chcon","-h","-r","role_r","/foo"]).returns 0 + set_selinux_context("/foo", "role_r", :selrole).should be_true + end + + it "should use chcon to set type_t type context" do + self.expects(:selinux_support?).returns true + self.expects(:execute).with(["/usr/bin/chcon","-h","-t","type_t","/foo"]).returns 0 + set_selinux_context("/foo", "type_t", :seltype).should be_true + end + + it "should use chcon to set s0:c3,c5 range context" do + self.expects(:selinux_support?).returns true + self.expects(:execute).with(["/usr/bin/chcon","-h","-l","s0:c3,c5","/foo"]).returns 0 + set_selinux_context("/foo", "s0:c3,c5", :selrange).should be_true + end + end + + describe "set_selinux_default_context" do + it "should return nil if there is no SELinux support" do + self.expects(:selinux_support?).returns false + set_selinux_default_context("/foo").should be_nil + end + + it "should return nil if no default context exists" do + self.expects(:get_selinux_default_context).with("/foo").returns nil + set_selinux_default_context("/foo").should be_nil + end + + it "should do nothing and return nil if the current context matches the default context" do + self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" + self.expects(:get_selinux_current_context).with("/foo").returns "user_u:role_r:type_t" + set_selinux_default_context("/foo").should be_nil + end + + it "should set and return the default context if current and default do not match" do + self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" + self.expects(:get_selinux_current_context).with("/foo").returns "olduser_u:role_r:type_t" + self.expects(:set_selinux_context).with("/foo", "user_u:role_r:type_t").returns true + set_selinux_default_context("/foo").should == "user_u:role_r:type_t" + end + end + +end diff --git a/spec/unit/util/settings.rb b/spec/unit/util/settings.rb index 0faa92c68..a333c513e 100755 --- a/spec/unit/util/settings.rb +++ b/spec/unit/util/settings.rb @@ -254,6 +254,12 @@ describe Puppet::Util::Settings do @settings.value(:one, "env").should == "envval" end + it "should interpolate found values using the current environment" do + @settings.setdefaults :main, :myval => ["$environment/foo", "mydocs"] + + @settings.value(:myval, "myenv").should == "myenv/foo" + end + it "should return values in a specified environment before values in the main or name sections" do text = "[env]\none = envval\n[main]\none = mainval\n[myname]\none = nameval\n" file = "/some/file" @@ -760,6 +766,7 @@ describe Puppet::Util::Settings do end it "should return false if a config param is not found" do + @settings.stubs :puts @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(false) @settings.print_configs.should be_false @@ -767,6 +774,10 @@ describe Puppet::Util::Settings do end describe "when genconfig is true" do + before do + @settings.stubs :puts + end + it "should call to_config" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.expects(:to_config) @@ -781,6 +792,10 @@ describe Puppet::Util::Settings do end describe "when genmanifest is true" do + before do + @settings.stubs :puts + end + it "should call to_config" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.expects(:to_manifest) diff --git a/spec/unit/util/user_attr.rb b/spec/unit/util/user_attr.rb new file mode 100644 index 000000000..57787b1a6 --- /dev/null +++ b/spec/unit/util/user_attr.rb @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/user_attr' + +describe UserAttr do + before do + user_attr = ["foo::::type=role", "bar::::type=normal;profile=foobar"] + File.stubs(:readlines).returns(user_attr) + end + + describe "when getting attributes by name" do + it "should return nil if there is no entry for that name" do + UserAttr.get_attributes_by_name('baz').should == nil + end + + it "should return a hash if there is an entry in /etc/user_attr" do + UserAttr.get_attributes_by_name('foo').class.should == Hash + end + + it "should return a hash with the name value from /etc/user_attr" do + UserAttr.get_attributes_by_name('foo')[:name].should == 'foo' + end + + #this test is contrived + #there are a bunch of possible parameters that could be in the hash + #the role/normal is just a the convention of the file + describe "when the name is a role" do + it "should contain :type = role" do + UserAttr.get_attributes_by_name('foo')[:type].should == 'role' + end + end + + describe "when the name is not a role" do + it "should contain :type = normal" do + UserAttr.get_attributes_by_name('bar')[:type].should == 'normal' + end + end + + describe "when the name has more attributes" do + it "should contain all the attributes" do + UserAttr.get_attributes_by_name('bar')[:profile].should == 'foobar' + end + end + end +end |
