summaryrefslogtreecommitdiffstats
path: root/spec
diff options
context:
space:
mode:
authorMichael V. O'Brien <michael@reductivelabs.com>2007-09-25 12:00:07 -0500
committerMichael V. O'Brien <michael@reductivelabs.com>2007-09-25 12:00:07 -0500
commitff2828f5dbe68ff1cb06a3503590a3e4bd1b59e3 (patch)
tree8c8960cac1d7b3e8b48e44163062be3b3f4c201f /spec
parentf8ab62b212788a4591276c95b5f67217f7517e4e (diff)
parentffaa8ce07979f4db860950fa9be08ca37964206f (diff)
Merge branch 'master' of git://reductivelabs.com/puppet
Diffstat (limited to 'spec')
-rw-r--r--spec/Rakefile6
-rwxr-xr-xspec/bin/spec1
-rwxr-xr-xspec/integration/checksum.rb48
-rwxr-xr-xspec/integration/node.rb46
-rw-r--r--spec/lib/spec/dsl/behaviour.rb6
-rw-r--r--spec/lib/spec/runner/behaviour_runner.rb2
-rw-r--r--spec/spec_helper.rb14
-rwxr-xr-xspec/unit/indirector/code.rb33
-rwxr-xr-xspec/unit/indirector/code/configuration.rb158
-rwxr-xr-xspec/unit/indirector/exec.rb49
-rwxr-xr-xspec/unit/indirector/exec/node.rb62
-rwxr-xr-xspec/unit/indirector/file.rb160
-rwxr-xr-xspec/unit/indirector/file/checksum.rb142
-rwxr-xr-xspec/unit/indirector/indirection.rb160
-rwxr-xr-xspec/unit/indirector/indirector.rb86
-rwxr-xr-xspec/unit/indirector/ldap.rb147
-rwxr-xr-xspec/unit/indirector/ldap/node.rb240
-rwxr-xr-xspec/unit/indirector/memory.rb53
-rwxr-xr-xspec/unit/indirector/memory/node.rb19
-rwxr-xr-xspec/unit/indirector/null.rb27
-rwxr-xr-xspec/unit/indirector/null/node.rb18
-rwxr-xr-xspec/unit/indirector/terminus.rb226
-rwxr-xr-xspec/unit/indirector/yaml.rb104
-rwxr-xr-xspec/unit/indirector/yaml/facts.rb26
-rwxr-xr-xspec/unit/node/configuration.rb337
-rwxr-xr-xspec/unit/node/facts.rb39
-rwxr-xr-xspec/unit/node/node.rb (renamed from spec/unit/other/node.rb)42
-rwxr-xr-xspec/unit/node/searching.rb79
-rwxr-xr-xspec/unit/other/checksum.rb92
-rwxr-xr-xspec/unit/other/modules.rb24
-rwxr-xr-xspec/unit/other/pgraph.rb285
-rwxr-xr-xspec/unit/other/transaction.rb26
-rwxr-xr-xspec/unit/other/transbucket.rb133
-rwxr-xr-xspec/unit/other/transobject.rb116
-rwxr-xr-xspec/unit/parser/interpreter.rb4
-rwxr-xr-xspec/unit/ral/type.rb25
-rwxr-xr-xspec/unit/util/config.rb408
-rwxr-xr-xspec/unit/util/settings.rb535
38 files changed, 3548 insertions, 430 deletions
diff --git a/spec/Rakefile b/spec/Rakefile
index 40d107312..bb2a75de5 100644
--- a/spec/Rakefile
+++ b/spec/Rakefile
@@ -2,9 +2,15 @@ require File.join(File.dirname(__FILE__), "spec_helper.rb")
require 'rake'
require 'spec/rake/spectask'
+basedir = File.dirname(__FILE__)
+puppetlibdir = File.join(basedir, "../lib")
+puppettestlibdir = File.join(basedir, "../test/lib")
+speclibdir = File.join(basedir, "lib")
+
desc "Run all spec unit tests"
Spec::Rake::SpecTask.new('unit') do |t|
t.spec_files = FileList['unit/**/*.rb']
+ t.libs = [puppetlibdir, puppettestlibdir, speclibdir]
end
task :default => [:unit]
diff --git a/spec/bin/spec b/spec/bin/spec
index a7e6ce0cb..aaf320f34 100755
--- a/spec/bin/spec
+++ b/spec/bin/spec
@@ -1,3 +1,4 @@
+#!/usr/bin/env ruby
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
require 'spec'
::Spec::Runner::CommandLine.run(ARGV, STDERR, STDOUT, true, true)
diff --git a/spec/integration/checksum.rb b/spec/integration/checksum.rb
new file mode 100755
index 000000000..f112f7502
--- /dev/null
+++ b/spec/integration/checksum.rb
@@ -0,0 +1,48 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2007-9-22.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../spec_helper'
+
+require 'puppet/checksum'
+
+describe Puppet::Checksum, " when using the file terminus" do
+ before do
+ Puppet[:checksum_terminus] = "file"
+
+ @content = "this is some content"
+ @sum = Puppet::Checksum.new(@content)
+
+ @file = Puppet::Checksum.indirection.terminus.path(@sum.checksum)
+ end
+
+ it "should store content at a path determined by its checksum" do
+ File.stubs(:directory?).returns(true)
+ filehandle = mock 'filehandle'
+ filehandle.expects(:print).with(@content)
+ File.expects(:open).with(@file, "w").yields(filehandle)
+
+ @sum.save
+ end
+
+ it "should retrieve stored content when the checksum is provided as the key" do
+ File.stubs(:exist?).returns(true)
+ File.expects(:read).with(@file).returns(@content)
+
+ newsum = Puppet::Checksum.find(@sum.checksum)
+
+ newsum.content.should == @content
+ end
+
+ it "should remove specified files when asked" do
+ File.stubs(:exist?).returns(true)
+ File.expects(:unlink).with(@file)
+
+ Puppet::Checksum.destroy(@sum)
+ end
+
+ after do
+ Puppet.settings.clear
+ end
+end
diff --git a/spec/integration/node.rb b/spec/integration/node.rb
new file mode 100755
index 000000000..8bc641bae
--- /dev/null
+++ b/spec/integration/node.rb
@@ -0,0 +1,46 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2007-9-23.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../spec_helper'
+
+require 'puppet/node'
+
+describe Puppet::Node, " when using the memory terminus" do
+ before do
+ @name = "me"
+ @node = Puppet::Node.new(@name)
+ Puppet[:node_terminus] = "memory"
+ end
+
+ it "should find no nodes by default" do
+ Puppet::Node.find(@name).should be_nil
+ end
+
+ it "should be able to find nodes that were previously saved" do
+ @node.save
+ Puppet::Node.find(@name).should equal(@node)
+ end
+
+ it "should replace existing saved nodes when a new node with the same name is saved" do
+ @node.save
+ two = Puppet::Node.new(@name)
+ two.save
+ Puppet::Node.find(@name).should equal(two)
+ end
+
+ it "should be able to remove previously saved nodes" do
+ @node.save
+ Puppet::Node.destroy(@node)
+ Puppet::Node.find(@name).should be_nil
+ end
+
+ it "should fail when asked to destroy a node that does not exist" do
+ proc { Puppet::Node.destroy(@node) }.should raise_error(ArgumentError)
+ end
+
+ after do
+ Puppet.settings.clear
+ end
+end
diff --git a/spec/lib/spec/dsl/behaviour.rb b/spec/lib/spec/dsl/behaviour.rb
index 5158bb673..159a0ba7e 100644
--- a/spec/lib/spec/dsl/behaviour.rb
+++ b/spec/lib/spec/dsl/behaviour.rb
@@ -1,6 +1,10 @@
+require(File.expand_path(File.dirname(__FILE__) + '../../../../../test/lib/puppettest/runnable_test.rb'))
+
module Spec
module DSL
- class EvalModule < Module; end
+ class EvalModule < Module;
+ include PuppetTest::RunnableTest
+ end
class Behaviour
extend BehaviourCallbacks
diff --git a/spec/lib/spec/runner/behaviour_runner.rb b/spec/lib/spec/runner/behaviour_runner.rb
index 1ac891f3c..078490e92 100644
--- a/spec/lib/spec/runner/behaviour_runner.rb
+++ b/spec/lib/spec/runner/behaviour_runner.rb
@@ -55,6 +55,8 @@ module Spec
def run_behaviours
@behaviours.each do |behaviour|
+ # LAK:NOTE: this 'runnable' test is Puppet-specific.
+ next unless behaviour.runnable?
behaviour.run(@options.reporter, @options.dry_run, @options.reverse, @options.timeout)
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index aa56fd93e..3017f272a 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,13 +1,21 @@
-dir = File.dirname(__FILE__)
-$:.unshift("#{dir}/lib").unshift("#{dir}/../lib")
+dir = File.expand_path(File.dirname(__FILE__))
+$:.unshift("#{dir}/lib")
+$:.unshift("#{dir}/../lib")
# Add the old test dir, so that we can still find mocha and spec
$:.unshift("#{dir}/../test/lib")
require 'mocha'
-require 'spec'
require 'puppettest'
+require 'spec'
Spec::Runner.configure do |config|
config.mock_with :mocha
+ config.prepend_before :each do
+ setup() if respond_to? :setup
+ end
+
+ config.prepend_after :each do
+ teardown() if respond_to? :teardown
+ end
end
diff --git a/spec/unit/indirector/code.rb b/spec/unit/indirector/code.rb
new file mode 100755
index 000000000..f34dcf402
--- /dev/null
+++ b/spec/unit/indirector/code.rb
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'puppet/indirector/code'
+
+describe Puppet::Indirector::Code do
+ before do
+ Puppet::Indirector::Terminus.stubs(:register_terminus_class)
+ @model = mock 'model'
+ @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model
+ Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection)
+
+ @code_class = Class.new(Puppet::Indirector::Code) do
+ def self.to_s
+ "Testing"
+ end
+ end
+
+ @searcher = @code_class.new
+ end
+
+ it "should not have a find() method defined" do
+ @searcher.should_not respond_to(:find)
+ end
+
+ it "should not have a save() method defined" do
+ @searcher.should_not respond_to(:save)
+ end
+
+ it "should not have a destroy() method defined" do
+ @searcher.should_not respond_to(:destroy)
+ end
+end
diff --git a/spec/unit/indirector/code/configuration.rb b/spec/unit/indirector/code/configuration.rb
new file mode 100755
index 000000000..8652f342d
--- /dev/null
+++ b/spec/unit/indirector/code/configuration.rb
@@ -0,0 +1,158 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2007-9-23.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/code/configuration'
+
+describe Puppet::Indirector::Code::Configuration do
+ # LAK:TODO I have no idea how to do this, or even if it should be in this class or test or what.
+ # This is used for determining if the client should recompile its configuration, so it's not sufficient
+ # to recompile and compare versions.
+ # It might be that the right solution is to require configuration caching, and then compare the cached
+ # configuration version to the current version, via some querying mechanism (i.e., the client asks for just
+ # the configuration's 'up-to-date' attribute, rather than the whole configuration).
+ it "should provide a mechanism for determining if the client's configuration is up to date"
+end
+
+describe Puppet::Indirector::Code::Configuration do
+ before do
+ Puppet.expects(:version).returns(1)
+ Facter.expects(:value).with('fqdn').returns("my.server.com")
+ Facter.expects(:value).with('ipaddress').returns("my.ip.address")
+ end
+
+ it "should gather data about itself" do
+ Puppet::Indirector::Code::Configuration.new
+ end
+
+ it "should cache the server metadata and reuse it" do
+ compiler = Puppet::Indirector::Code::Configuration.new
+ node1 = stub 'node1', :merge => nil
+ node2 = stub 'node2', :merge => nil
+ compiler.stubs(:compile)
+ Puppet::Node.stubs(:search).with('node1').returns(node1)
+ Puppet::Node.stubs(:search).with('node2').returns(node2)
+
+ compiler.find('node1')
+ compiler.find('node2')
+ end
+end
+
+describe Puppet::Indirector::Code::Configuration, " when creating the interpreter" do
+ before do
+ @compiler = Puppet::Indirector::Code::Configuration.new
+ end
+
+ it "should not create the interpreter until it is asked for the first time" do
+ interp = mock 'interp'
+ Puppet::Parser::Interpreter.expects(:new).with({}).returns(interp)
+ @compiler.interpreter.should equal(interp)
+ end
+
+ it "should use the same interpreter for all compiles" do
+ interp = mock 'interp'
+ Puppet::Parser::Interpreter.expects(:new).with({}).returns(interp)
+ @compiler.interpreter.should equal(interp)
+ @compiler.interpreter.should equal(interp)
+ end
+
+ it "should provide a mechanism for setting the code to pass to the interpreter" do
+ @compiler.should respond_to(:code=)
+ end
+
+ it "should pass any specified code on to the interpreter when it is being initialized" do
+ code = "some code"
+ @compiler.code = code
+ interp = mock 'interp'
+ Puppet::Parser::Interpreter.expects(:new).with(:Code => code).returns(interp)
+ @compiler.send(:interpreter).should equal(interp)
+ end
+
+end
+
+describe Puppet::Indirector::Code::Configuration, " when finding nodes" do
+ before do
+ @compiler = Puppet::Indirector::Code::Configuration.new
+ @name = "me"
+ @node = mock 'node'
+ @compiler.stubs(:compile)
+ end
+
+ it "should look node information up via the Node class with the provided key" do
+ @node.stubs :merge
+ Puppet::Node.expects(:search).with(@name).returns(@node)
+ @compiler.find(@name)
+ end
+
+ it "should fail if it cannot find the node" do
+ @node.stubs :merge
+ Puppet::Node.expects(:search).with(@name).returns(nil)
+ proc { @compiler.find(@name) }.should raise_error(Puppet::Error)
+ end
+end
+
+describe Puppet::Indirector::Code::Configuration, " after finding nodes" do
+ before do
+ Puppet.expects(:version).returns(1)
+ Puppet.settings.stubs(:value).with(:node_name).returns("cert")
+ Facter.expects(:value).with('fqdn').returns("my.server.com")
+ Facter.expects(:value).with('ipaddress').returns("my.ip.address")
+ @compiler = Puppet::Indirector::Code::Configuration.new
+ @name = "me"
+ @node = mock 'node'
+ @compiler.stubs(:compile)
+ Puppet::Node.stubs(:search).with(@name).returns(@node)
+ end
+
+ it "should add the server's Puppet version to the node's parameters as 'serverversion'" do
+ @node.expects(:merge).with { |args| args["serverversion"] == "1" }
+ @compiler.find(@name)
+ end
+
+ it "should add the server's fqdn to the node's parameters as 'servername'" do
+ @node.expects(:merge).with { |args| args["servername"] == "my.server.com" }
+ @compiler.find(@name)
+ end
+
+ it "should add the server's IP address to the node's parameters as 'serverip'" do
+ @node.expects(:merge).with { |args| args["serverip"] == "my.ip.address" }
+ @compiler.find(@name)
+ end
+
+ # LAK:TODO This is going to be difficult, because this whole process is so
+ # far removed from the actual connection that the certificate information
+ # will be quite hard to come by, dum by, gum by.
+ it "should search for the name using the client certificate's DN if the :node_name setting is set to 'cert'"
+end
+
+describe Puppet::Indirector::Code::Configuration, " when creating configurations" do
+ before do
+ @compiler = Puppet::Indirector::Code::Configuration.new
+ @name = "me"
+ @node = stub 'node', :merge => nil, :name => @name, :environment => "yay"
+ Puppet::Node.stubs(:search).with(@name).returns(@node)
+ end
+
+ it "should pass the found node to the interpreter for compiling" do
+ config = mock 'config'
+ @compiler.interpreter.expects(:compile).with(@node)
+ @compiler.find(@name)
+ end
+
+ it "should return the results of compiling as the configuration" do
+ config = mock 'config'
+ @compiler.interpreter.expects(:compile).with(@node).returns(:configuration)
+ @compiler.find(@name).should == :configuration
+ end
+
+ it "should benchmark the compile process" do
+ @compiler.expects(:benchmark).with do |level, message|
+ level == :notice and message =~ /^Compiled configuration/
+ end
+ @compiler.interpreter.stubs(:compile).with(@node)
+ @compiler.find(@name)
+ end
+end
diff --git a/spec/unit/indirector/exec.rb b/spec/unit/indirector/exec.rb
new file mode 100755
index 000000000..42fbe0955
--- /dev/null
+++ b/spec/unit/indirector/exec.rb
@@ -0,0 +1,49 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/indirector/exec'
+
+describe Puppet::Indirector::Exec do
+ before do
+ @indirection = mock 'indirection'
+ Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection)
+ @exec_class = Class.new(Puppet::Indirector::Exec) do
+ def self.to_s
+ "Testing"
+ end
+
+ attr_accessor :command
+ end
+
+ @searcher = @exec_class.new
+ @searcher.command = ["/echo"]
+ end
+
+ it "should throw an exception if the command is not an array" do
+ @searcher.command = "/usr/bin/echo"
+ proc { @searcher.find("foo") }.should raise_error(Puppet::DevError)
+ end
+
+ it "should throw an exception if the command is not fully qualified" do
+ @searcher.command = ["mycommand"]
+ proc { @searcher.find("foo") }.should raise_error(ArgumentError)
+ end
+
+ it "should execute the command with the object name as the only argument" do
+ @searcher.expects(:execute).with(%w{/echo yay})
+ @searcher.find("yay")
+ end
+
+ it "should return the output of the script" do
+ @searcher.expects(:execute).with(%w{/echo yay}).returns("whatever")
+ @searcher.find("yay").should == "whatever"
+ end
+
+ it "should return nil when the command produces no output" do
+ @searcher.expects(:execute).with(%w{/echo yay}).returns(nil)
+ @searcher.find("yay").should be_nil
+ end
+
+ it "should be able to execute commands with multiple arguments"
+end
diff --git a/spec/unit/indirector/exec/node.rb b/spec/unit/indirector/exec/node.rb
new file mode 100755
index 000000000..47f4ce7a5
--- /dev/null
+++ b/spec/unit/indirector/exec/node.rb
@@ -0,0 +1,62 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/exec/node'
+
+describe Puppet::Indirector::Exec::Node, " when constructing the command to run" do
+ before do
+ @indirection = mock 'indirection'
+ Puppet.settings.stubs(:value).with(:external_nodes).returns("/echo")
+ @searcher = Puppet::Indirector::Exec::Node.new
+ end
+
+ it "should use the external_node script as the command" do
+ Puppet.expects(:[]).with(:external_nodes).returns("/bin/echo")
+ @searcher.command.should == %w{/bin/echo}
+ end
+
+ it "should throw an exception if no external node command is set" do
+ Puppet.expects(:[]).with(:external_nodes).returns("none")
+ proc { @searcher.find("foo") }.should raise_error(ArgumentError)
+ end
+end
+
+describe Puppet::Indirector::Exec::Node, " when handling the results of the command" do
+ before do
+ @indirection = mock 'indirection'
+ Puppet.settings.stubs(:value).with(:external_nodes).returns("/echo")
+ @searcher = Puppet::Indirector::Exec::Node.new
+ @node = stub 'node', :fact_merge => nil
+ @name = "yay"
+ Puppet::Node.expects(:new).with(@name).returns(@node)
+ @result = {}
+ # Use a local variable so the reference is usable in the execute() definition.
+ result = @result
+ @searcher.meta_def(:execute) do |command|
+ return YAML.dump(result)
+ end
+ end
+
+ it "should translate the YAML into a Node instance" do
+ # Use an empty hash
+ @searcher.find(@name).should equal(@node)
+ end
+
+ it "should set the resulting parameters as the node parameters" do
+ @result[:parameters] = {"a" => "b", "c" => "d"}
+ @node.expects(:parameters=).with "a" => "b", "c" => "d"
+ @searcher.find(@name)
+ end
+
+ it "should set the resulting classes as the node classes" do
+ @result[:classes] = %w{one two}
+ @node.expects(:classes=).with %w{one two}
+ @searcher.find(@name)
+ end
+
+ it "should merge the node's facts with its parameters" do
+ @node.expects(:fact_merge)
+ @searcher.find(@name)
+ end
+end
diff --git a/spec/unit/indirector/file.rb b/spec/unit/indirector/file.rb
new file mode 100755
index 000000000..cc86f9fa9
--- /dev/null
+++ b/spec/unit/indirector/file.rb
@@ -0,0 +1,160 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'puppet/indirector/file'
+
+module FileTerminusTesting
+ def setup
+ Puppet::Indirector::Terminus.stubs(:register_terminus_class)
+ @model = mock 'model'
+ @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model
+ Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection)
+
+ @file_class = Class.new(Puppet::Indirector::File) do
+ def self.to_s
+ "Testing"
+ end
+ end
+
+ @searcher = @file_class.new
+
+ @path = "/my/file"
+ @dir = "/my"
+ end
+end
+
+describe Puppet::Indirector::File, " when finding files" do
+ include FileTerminusTesting
+
+ it "should provide a method to return file contents at a specified path" do
+ @searcher.should respond_to(:find)
+ end
+
+ it "should return file contents as an instance of the model" do
+ content = "my content"
+
+ file = mock 'file'
+ @model.expects(:new).with(content).returns(file)
+
+ File.expects(:exist?).with(@path).returns(true)
+ File.expects(:read).with(@path).returns(content)
+ @searcher.find(@path)
+ end
+
+ it "should create the model instance with the content as the only argument to initialization" do
+ content = "my content"
+
+ file = mock 'file'
+ @model.expects(:new).with(content).returns(file)
+
+ File.expects(:exist?).with(@path).returns(true)
+ File.expects(:read).with(@path).returns(content)
+ @searcher.find(@path).should equal(file)
+ end
+
+ it "should return nil if no file is found" do
+ File.expects(:exist?).with(@path).returns(false)
+ @searcher.find(@path).should be_nil
+ end
+
+ it "should fail intelligently if a found file cannot be read" do
+ File.expects(:exist?).with(@path).returns(true)
+ File.expects(:read).with(@path).raises(RuntimeError)
+ proc { @searcher.find(@path) }.should raise_error(Puppet::Error)
+ end
+
+ it "should use the path() method to calculate the path if it exists" do
+ @searcher.meta_def(:path) do |name|
+ name.upcase
+ end
+
+ File.expects(:exist?).with(@path.upcase).returns(false)
+ @searcher.find(@path)
+ end
+end
+
+describe Puppet::Indirector::File, " when saving files" do
+ include FileTerminusTesting
+
+ it "should provide a method to save file contents at a specified path" do
+ filehandle = mock 'file'
+ content = "my content"
+ File.expects(:directory?).with(@dir).returns(true)
+ File.expects(:open).with(@path, "w").yields(filehandle)
+ filehandle.expects(:print).with(content)
+
+ file = stub 'file', :content => content, :path => @path, :name => @path
+
+ @searcher.save(file)
+ end
+
+ it "should fail intelligently if the file's parent directory does not exist" do
+ File.expects(:directory?).with(@dir).returns(false)
+
+ file = stub 'file', :path => @path, :name => @path
+
+ proc { @searcher.save(file) }.should raise_error(Puppet::Error)
+ end
+
+ it "should fail intelligently if a file cannot be written" do
+ filehandle = mock 'file'
+ content = "my content"
+ File.expects(:directory?).with(@dir).returns(true)
+ File.expects(:open).with(@path, "w").yields(filehandle)
+ filehandle.expects(:print).with(content).raises(ArgumentError)
+
+ file = stub 'file', :content => content, :path => @path, :name => @path
+
+ proc { @searcher.save(file) }.should raise_error(Puppet::Error)
+ end
+
+ it "should use the path() method to calculate the path if it exists" do
+ @searcher.meta_def(:path) do |name|
+ name.upcase
+ end
+
+ file = stub 'file', :name => "/yay"
+
+ File.expects(:open).with("/YAY", "w")
+ @searcher.save(file)
+ end
+end
+
+describe Puppet::Indirector::File, " when removing files" do
+ include FileTerminusTesting
+
+ it "should provide a method to remove files at a specified path" do
+ file = stub 'file', :path => @path, :name => @path
+ File.expects(:exist?).with(@path).returns(true)
+ File.expects(:unlink).with(@path)
+
+ @searcher.destroy(file)
+ end
+
+ it "should throw an exception if the file is not found" do
+ file = stub 'file', :path => @path, :name => @path
+ File.expects(:exist?).with(@path).returns(false)
+
+ proc { @searcher.destroy(file) }.should raise_error(Puppet::Error)
+ end
+
+ it "should fail intelligently if the file cannot be removed" do
+ file = stub 'file', :path => @path, :name => @path
+ File.expects(:exist?).with(@path).returns(true)
+ File.expects(:unlink).with(@path).raises(ArgumentError)
+
+ proc { @searcher.destroy(file) }.should raise_error(Puppet::Error)
+ end
+
+ it "should use the path() method to calculate the path if it exists" do
+ @searcher.meta_def(:path) do |name|
+ name.upcase
+ end
+
+ file = stub 'file', :name => "/yay"
+ File.expects(:exist?).with("/YAY").returns(true)
+ File.expects(:unlink).with("/YAY")
+
+ @searcher.destroy(file)
+ end
+end
diff --git a/spec/unit/indirector/file/checksum.rb b/spec/unit/indirector/file/checksum.rb
new file mode 100755
index 000000000..539bb973c
--- /dev/null
+++ b/spec/unit/indirector/file/checksum.rb
@@ -0,0 +1,142 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2007-9-22.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/file/checksum'
+
+module FileChecksumTesting
+ def setup
+ Puppet.settings.stubs(:use)
+ @store = Puppet::Indirector::File::Checksum.new
+
+ @value = "70924d6fa4b2d745185fa4660703a5c0"
+ @sum = stub 'sum', :name => @value
+
+ @dir = "/what/ever"
+
+ Puppet.stubs(:[]).with(:bucketdir).returns(@dir)
+
+ @path = @store.path(@value)
+ end
+end
+
+describe Puppet::Indirector::File::Checksum do
+ it "should be a subclass of the File terminus class" do
+ Puppet::Indirector::File::Checksum.superclass.should equal(Puppet::Indirector::File)
+ end
+
+ it "should have documentation" do
+ Puppet::Indirector::File::Checksum.doc.should be_instance_of(String)
+ end
+end
+
+describe Puppet::Indirector::File::Checksum, " when initializing" do
+ it "should use the filebucket settings section" do
+ Puppet.settings.expects(:use).with(:filebucket)
+ Puppet::Indirector::File::Checksum.new
+ end
+end
+
+describe Puppet::Indirector::File::Checksum, " when determining file paths" do
+ include FileChecksumTesting
+
+ # I was previously passing the object in.
+ it "should use the value passed in to path() as the checksum" do
+ @value.expects(:name).never
+ @store.path(@value)
+ end
+
+ it "should use the value of the :bucketdir setting as the root directory" do
+ @path.should =~ %r{^#{@dir}}
+ end
+
+ it "should choose a path 8 directories deep with each directory name being the respective character in the checksum" do
+ dirs = @value[0..7].split("").join(File::SEPARATOR)
+ @path.should be_include(dirs)
+ end
+
+ it "should use the full checksum as the final directory name" do
+ File.basename(File.dirname(@path)).should == @value
+ end
+
+ it "should use 'contents' as the actual file name" do
+ File.basename(@path).should == "contents"
+ end
+
+ it "should use the bucketdir, the 8 sum character directories, the full checksum, and 'contents' as the full file name" do
+ @path.should == [@dir, @value[0..7].split(""), @value, "contents"].flatten.join(File::SEPARATOR)
+ end
+end
+
+describe Puppet::Indirector::File::Checksum, " when retrieving files" do
+ include FileChecksumTesting
+
+ # The smallest test that will use the calculated path
+ it "should look for the calculated path" do
+ File.expects(:exist?).with(@path).returns(false)
+ @store.find(@value)
+ end
+
+ it "should return an instance of Puppet::Checksum created with the content if the file exists" do
+ content = "my content"
+ sum = stub 'file'
+ Puppet::Checksum.expects(:new).with(content).returns(sum)
+
+ File.expects(:exist?).with(@path).returns(true)
+ File.expects(:read).with(@path).returns(content)
+
+ @store.find(@value).should equal(sum)
+ end
+
+ it "should return nil if no file is found" do
+ File.expects(:exist?).with(@path).returns(false)
+ @store.find(@value).should be_nil
+ end
+
+ it "should fail intelligently if a found file cannot be read" do
+ File.expects(:exist?).with(@path).returns(true)
+ File.expects(:read).with(@path).raises(RuntimeError)
+ proc { @store.find(@value) }.should raise_error(Puppet::Error)
+ end
+end
+
+describe Puppet::Indirector::File::Checksum, " when saving files" do
+ include FileChecksumTesting
+
+ # LAK:FIXME I don't know how to include in the spec the fact that we're
+ # using the superclass's save() method and thus are acquiring all of
+ # it's behaviours.
+ it "should save the content to the calculated path" do
+ File.stubs(:directory?).with(File.dirname(@path)).returns(true)
+ File.expects(:open).with(@path, "w")
+
+ file = stub 'file', :name => @value
+ @store.save(file)
+ end
+
+ it "should make any directories necessary for storage" do
+ FileUtils.expects(:mkdir_p).with do |arg|
+ File.umask == 0007 and arg == File.dirname(@path)
+ end
+ File.expects(:directory?).with(File.dirname(@path)).returns(true)
+ File.expects(:open).with(@path, "w")
+
+ file = stub 'file', :name => @value
+ @store.save(file)
+ end
+end
+
+describe Puppet::Indirector::File::Checksum, " when deleting files" do
+ include FileChecksumTesting
+
+ it "should remove the file at the calculated path" do
+ File.expects(:exist?).with(@path).returns(true)
+ File.expects(:unlink).with(@path)
+
+ file = stub 'file', :name => @value
+ @store.destroy(file)
+ end
+end
diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb
new file mode 100755
index 000000000..326b6b470
--- /dev/null
+++ b/spec/unit/indirector/indirection.rb
@@ -0,0 +1,160 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/indirector'
+
+describe Puppet::Indirector::Indirection, " when initializing" do
+ it "should keep a reference to the indirecting model" do
+ model = mock 'model'
+ @indirection = Puppet::Indirector::Indirection.new(model, :myind)
+ @indirection.model.should equal(model)
+ end
+
+ it "should set the name" do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind)
+ @indirection.name.should == :myind
+ end
+
+ it "should require indirections to have unique names" do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError)
+ end
+
+ after do
+ @indirection.delete if defined? @indirection
+ end
+end
+
+describe Puppet::Indirector::Indirection, " when managing indirection instances" do
+ it "should allow an indirection to be retrieved by name" do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ Puppet::Indirector::Indirection.instance(:test).should equal(@indirection)
+ end
+
+ it "should return nil when the named indirection has not been created" do
+ Puppet::Indirector::Indirection.instance(:test).should be_nil
+ end
+
+ after do
+ @indirection.delete if defined? @indirection
+ end
+end
+
+describe Puppet::Indirector::Indirection, " when choosing terminus types" do
+ before do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ @terminus = mock 'terminus'
+ @terminus_class = stub 'terminus class', :new => @terminus
+ end
+
+ it "should follow a convention on using per-model configuration parameters to determine the terminus class" do
+ Puppet.settings.expects(:valid?).with('test_terminus').returns(true)
+ Puppet.settings.expects(:value).with('test_terminus').returns(:foo)
+ Puppet::Indirector::Terminus.expects(:terminus_class).with(:foo, :test).returns(@terminus_class)
+ @indirection.terminus.should equal(@terminus)
+ end
+
+ it "should use a default system-wide configuration parameter parameter to determine the terminus class when no
+ per-model configuration parameter is available" do
+ Puppet.settings.expects(:valid?).with('test_terminus').returns(false)
+ Puppet.settings.expects(:value).with(:default_terminus).returns(:foo)
+ Puppet::Indirector::Terminus.expects(:terminus_class).with(:foo, :test).returns(@terminus_class)
+ @indirection.terminus.should equal(@terminus)
+ end
+
+ it "should select the specified terminus class if a name is provided" do
+ Puppet::Indirector::Terminus.expects(:terminus_class).with(:foo, :test).returns(@terminus_class)
+ @indirection.terminus(:foo).should equal(@terminus)
+ end
+
+ it "should fail when the terminus class name is an empty string" do
+ proc { @indirection.terminus("") }.should raise_error(ArgumentError)
+ end
+
+ it "should fail when the terminus class name is nil" do
+ proc { @indirection.terminus(nil) }.should raise_error(ArgumentError)
+ end
+
+ it "should fail when the specified terminus class cannot be found" do
+ Puppet::Indirector::Terminus.expects(:terminus_class).with(:foo, :test).returns(nil)
+ proc { @indirection.terminus(:foo) }.should raise_error(ArgumentError)
+ end
+
+ after do
+ @indirection.delete if defined? @indirection
+ end
+end
+
+describe Puppet::Indirector::Indirection, " when managing terminus instances" do
+ before do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ @terminus = mock 'terminus'
+ @terminus_class = mock 'terminus class'
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:foo, :test).returns(@terminus_class)
+ end
+
+ it "should create an instance of the chosen terminus class" do
+ @terminus_class.stubs(:new).returns(@terminus)
+ @indirection.terminus(:foo).should equal(@terminus)
+ end
+
+ it "should allow the clearance of cached terminus instances" do
+ terminus1 = mock 'terminus1'
+ terminus2 = mock 'terminus2'
+ @terminus_class.stubs(:new).returns(terminus1, terminus2, ArgumentError)
+ @indirection.terminus(:foo).should equal(terminus1)
+ @indirection.class.clear_cache
+ @indirection.terminus(:foo).should equal(terminus2)
+ end
+
+ # Make sure it caches the terminus.
+ it "should return the same terminus instance each time for a given name" do
+ @terminus_class.stubs(:new).returns(@terminus)
+ @indirection.terminus(:foo).should equal(@terminus)
+ @indirection.terminus(:foo).should equal(@terminus)
+ end
+
+ it "should not create a terminus instance until one is actually needed" do
+ Puppet::Indirector.expects(:terminus).never
+ indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest)
+ end
+
+ after do
+ @indirection.delete
+ Puppet::Indirector::Indirection.clear_cache
+ end
+end
+
+describe Puppet::Indirector::Indirection do
+ before do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ @terminus = mock 'terminus'
+ @indirection.stubs(:terminus).returns(@terminus)
+ end
+
+ it "should handle lookups of a model instance by letting the appropriate terminus perform the lookup" do
+ @terminus.expects(:find).with(:mything).returns(:whev)
+ @indirection.find(:mything).should == :whev
+ end
+
+ it "should handle removing model instances from a terminus letting the appropriate terminus remove the instance" do
+ @terminus.expects(:destroy).with(:mything).returns(:whev)
+ @indirection.destroy(:mything).should == :whev
+ end
+
+ it "should handle searching for model instances by letting the appropriate terminus find the matching instances" do
+ @terminus.expects(:search).with(:mything).returns(:whev)
+ @indirection.search(:mything).should == :whev
+ end
+
+ it "should handle storing a model instance by letting the appropriate terminus store the instance" do
+ @terminus.expects(:save).with(:mything).returns(:whev)
+ @indirection.save(:mything).should == :whev
+ end
+
+ after do
+ @indirection.delete
+ Puppet::Indirector::Indirection.clear_cache
+ end
+end
diff --git a/spec/unit/indirector/indirector.rb b/spec/unit/indirector/indirector.rb
new file mode 100755
index 000000000..1702bf51f
--- /dev/null
+++ b/spec/unit/indirector/indirector.rb
@@ -0,0 +1,86 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/defaults'
+require 'puppet/indirector'
+
+describe Puppet::Indirector, " when available to a model" do
+ before do
+ @thingie = Class.new do
+ extend Puppet::Indirector
+ end
+ end
+
+ it "should provide a way for the model to register an indirection under a name" do
+ @thingie.should respond_to(:indirects)
+ end
+end
+
+describe Puppet::Indirector, "when registering an indirection" do
+ before do
+ @thingie = Class.new do
+ extend Puppet::Indirector
+ end
+ end
+
+ it "should require a name when registering a model" do
+ Proc.new {@thingie.send(:indirects) }.should raise_error(ArgumentError)
+ end
+
+ it "should create an indirection instance to manage each indirecting model" do
+ @indirection = @thingie.indirects(:test)
+ @indirection.should be_instance_of(Puppet::Indirector::Indirection)
+ end
+
+ it "should not allow a model to register under multiple names" do
+ # Keep track of the indirection instance so we can delete it on cleanup
+ @indirection = @thingie.indirects :first
+ Proc.new { @thingie.indirects :second }.should raise_error(ArgumentError)
+ end
+
+ it "should make the indirection available via an accessor" do
+ @indirection = @thingie.indirects :first
+ @thingie.indirection.should equal(@indirection)
+ end
+
+ after do
+ @indirection.delete if @indirection
+ end
+end
+
+describe Puppet::Indirector, " when redirecting a model" do
+ before do
+ @thingie = Class.new do
+ extend Puppet::Indirector
+ end
+ @mock_terminus = mock('Terminus')
+ @indirection = @thingie.send(:indirects, :test)
+ @thingie.expects(:indirection).returns(@mock_terminus)
+ end
+
+ it "should give the model the ability to lookup a model instance by letting the indirection perform the lookup" do
+ @mock_terminus.expects(:find)
+ @thingie.find
+ end
+
+ it "should give the model the ability to remove model instances from a terminus by letting the indirection remove the instance" do
+ @mock_terminus.expects(:destroy)
+ @thingie.destroy
+ end
+
+ it "should give the model the ability to search for model instances by letting the indirection find the matching instances" do
+ @mock_terminus.expects(:search)
+ @thingie.search
+ end
+
+ it "should give the model the ability to store a model instance by letting the indirection store the instance" do
+ thing = @thingie.new
+ @mock_terminus.expects(:save).with(thing)
+ thing.save
+ end
+
+ after do
+ @indirection.delete
+ end
+end
diff --git a/spec/unit/indirector/ldap.rb b/spec/unit/indirector/ldap.rb
new file mode 100755
index 000000000..fe9408986
--- /dev/null
+++ b/spec/unit/indirector/ldap.rb
@@ -0,0 +1,147 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/indirector/ldap'
+
+describe Puppet::Indirector::Ldap, " when searching ldap" do
+ before do
+ @indirection = mock 'indirection'
+ Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection)
+ @ldap_class = Class.new(Puppet::Indirector::Ldap) do
+ def self.to_s
+ "Testing"
+ end
+ end
+
+ @connection = mock 'ldap'
+
+ @searcher = @ldap_class.new
+
+ # Stub everything, and we can selectively replace with an expect as
+ # we need to for testing.
+ @searcher.stubs(:connection).returns(@connection)
+ @searcher.stubs(:search_filter).returns(:filter)
+ @searcher.stubs(:search_base).returns(:base)
+ @searcher.stubs(:process)
+ end
+
+ it "should call the ldapsearch method with the name being searched for" do
+ @searcher.expects(:ldapsearch).with("yay")
+ @searcher.find "yay"
+ end
+
+ it "should fail if no block is passed to the ldapsearch method" do
+ proc { @searcher.ldapsearch("blah") }.should raise_error(ArgumentError)
+ end
+
+ it "should use the results of the ldapbase method as the ldap search base" do
+ @searcher.stubs(:search_base).returns("mybase")
+ @connection.expects(:search).with do |*args|
+ args[0].should == "mybase"
+ true
+ end
+ @searcher.find "yay"
+ end
+
+ it "should default to the value of the :search_base setting as the result of the ldapbase method" do
+ Puppet.expects(:[]).with(:ldapbase).returns("myldapbase")
+ searcher = @ldap_class.new
+ searcher.search_base.should == "myldapbase"
+ end
+
+ it "should use the results of the :search_attributes method as the list of attributes to return" do
+ @searcher.stubs(:search_attributes).returns(:myattrs)
+ @connection.expects(:search).with do |*args|
+ args[3].should == :myattrs
+ true
+ end
+ @searcher.find "yay"
+ end
+
+ it "should use the results of the :search_filter method as the search filter" do
+ @searcher.stubs(:search_filter).with("yay").returns("yay's filter")
+ @connection.expects(:search).with do |*args|
+ args[2].should == "yay's filter"
+ true
+ end
+ @searcher.find "yay"
+ end
+
+ it "should use depth 2 when searching" do
+ @connection.expects(:search).with do |*args|
+ args[1].should == 2
+ true
+ end
+ @searcher.find "yay"
+ end
+
+ it "should call process() on the first found entry" do
+ @connection.expects(:search).yields("myresult")
+ @searcher.expects(:process).with("yay", "myresult")
+ @searcher.find "yay"
+ end
+
+ it "should reconnect and retry the search if there is a failure" do
+ run = false
+ @connection.stubs(:search).with do |*args|
+ if run
+ true
+ else
+ run = true
+ raise "failed"
+ end
+ end.yields("myresult")
+ @searcher.expects(:process).with("yay", "myresult")
+
+ @searcher.find "yay"
+ end
+
+ it "should not reconnect on failure more than once" do
+ count = 0
+ @connection.stubs(:search).with do |*args|
+ count += 1
+ raise ArgumentError, "yay"
+ end
+ proc { @searcher.find("whatever") }.should raise_error(Puppet::Error)
+ count.should == 2
+ end
+
+ it "should return true if an entry is found" do
+ @connection.expects(:search).yields("result")
+ @searcher.ldapsearch("whatever") { |r| }.should be_true
+ end
+end
+
+describe Puppet::Indirector::Ldap, " when connecting to ldap" do
+ confine "LDAP is not available" => Puppet.features.ldap?
+ confine "No LDAP test data for networks other than Luke's" => Facter.value(:domain) == "madstop.com"
+
+ it "should only create the ldap connection when asked for it the first time"
+
+ it "should throw an exception if it cannot connect to LDAP"
+
+ it "should use SSL when the :ldapssl setting is true"
+
+ it "should connect to the server specified in the :ldapserver setting"
+
+ it "should use the port specified in the :ldapport setting"
+
+ it "should use protocol version 3"
+
+ it "should follow referrals"
+
+ it "should use the user specified in the :ldapuser setting"
+
+ it "should use the password specified in the :ldappassord setting"
+
+ it "should have an ldap method that returns an LDAP connection object"
+
+ it "should fail when LDAP support is missing"
+end
+
+describe Puppet::Indirector::Ldap, " when reconnecting to ldap" do
+ confine "Not running on culain as root" => (Puppet::Util::SUIDManager.uid == 0 and Facter.value("hostname") == "culain")
+
+ it "should reconnect to ldap when connections are lost"
+end
diff --git a/spec/unit/indirector/ldap/node.rb b/spec/unit/indirector/ldap/node.rb
new file mode 100755
index 000000000..37953db19
--- /dev/null
+++ b/spec/unit/indirector/ldap/node.rb
@@ -0,0 +1,240 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/ldap/node'
+
+module LdapNodeSearching
+ def setup
+ @searcher = Puppet::Indirector::Ldap::Node.new
+ @entries = {}
+ entries = @entries
+
+ @connection = mock 'connection'
+ @entry = mock 'entry'
+ @connection.stubs(:search).yields(@entry)
+ @searcher.stubs(:connection).returns(@connection)
+ @searcher.stubs(:class_attributes).returns([])
+ @searcher.stubs(:parent_attribute).returns(nil)
+ @searcher.stubs(:search_base).returns(:yay)
+ @searcher.stubs(:search_filter).returns(:filter)
+
+ @node = mock 'node'
+ @node.stubs(:fact_merge)
+ @name = "mynode"
+ Puppet::Node.stubs(:new).with(@name).returns(@node)
+ end
+end
+
+describe Puppet::Indirector::Ldap::Node, " when searching for nodes" do
+ include LdapNodeSearching
+
+ it "should return nil if no results are found in ldap" do
+ @connection.stubs(:search)
+ @searcher.find("mynode").should be_nil
+ end
+
+ it "should return a node object if results are found in ldap" do
+ @entry.stubs(:to_hash).returns({})
+ @searcher.find("mynode").should equal(@node)
+ end
+
+ it "should deduplicate class values" do
+ @entry.stubs(:to_hash).returns({})
+ @searcher.stubs(:class_attributes).returns(%w{one two})
+ @entry.stubs(:vals).with("one").returns(%w{a b})
+ @entry.stubs(:vals).with("two").returns(%w{b c})
+ @node.expects(:classes=).with(%w{a b c})
+ @searcher.find("mynode")
+ end
+
+ it "should add any values stored in the class_attributes attributes to the node classes" do
+ @entry.stubs(:to_hash).returns({})
+ @searcher.stubs(:class_attributes).returns(%w{one two})
+ @entry.stubs(:vals).with("one").returns(%w{a b})
+ @entry.stubs(:vals).with("two").returns(%w{c d})
+ @node.expects(:classes=).with(%w{a b c d})
+ @searcher.find("mynode")
+ end
+
+ it "should add all entry attributes as node parameters" do
+ @entry.stubs(:to_hash).returns("one" => ["two"], "three" => ["four"])
+ @node.expects(:parameters=).with("one" => "two", "three" => "four")
+ @searcher.find("mynode")
+ end
+
+ it "should retain false parameter values" do
+ @entry.stubs(:to_hash).returns("one" => [false])
+ @node.expects(:parameters=).with("one" => false)
+ @searcher.find("mynode")
+ end
+
+ it "should turn single-value parameter value arrays into single non-arrays" do
+ @entry.stubs(:to_hash).returns("one" => ["a"])
+ @node.expects(:parameters=).with("one" => "a")
+ @searcher.find("mynode")
+ end
+
+ it "should keep multi-valued parametes as arrays" do
+ @entry.stubs(:to_hash).returns("one" => ["a", "b"])
+ @node.expects(:parameters=).with("one" => ["a", "b"])
+ @searcher.find("mynode")
+ end
+end
+
+describe Puppet::Indirector::Ldap::Node, " when a parent node exists" do
+ include LdapNodeSearching
+
+ before do
+ @parent = mock 'parent'
+ @parent_parent = mock 'parent_parent'
+
+ @searcher.meta_def(:search_filter) do |name|
+ return name
+ end
+ @connection.stubs(:search).with { |*args| args[2] == @name }.yields(@entry)
+ @connection.stubs(:search).with { |*args| args[2] == 'parent' }.yields(@parent)
+ @connection.stubs(:search).with { |*args| args[2] == 'parent_parent' }.yields(@parent_parent)
+
+ @searcher.stubs(:parent_attribute).returns(:parent)
+ end
+
+ it "should add any parent classes to the node's classes" do
+ @entry.stubs(:to_hash).returns({})
+ @entry.stubs(:vals).with(:parent).returns(%w{parent})
+ @entry.stubs(:vals).with("one").returns(%w{a b})
+
+ @parent.stubs(:to_hash).returns({})
+ @parent.stubs(:vals).with("one").returns(%w{c d})
+ @parent.stubs(:vals).with(:parent).returns(nil)
+
+ @searcher.stubs(:class_attributes).returns(%w{one})
+ @node.expects(:classes=).with(%w{a b c d})
+ @searcher.find("mynode")
+ end
+
+ it "should add any parent parameters to the node's parameters" do
+ @entry.stubs(:to_hash).returns("one" => "two")
+ @entry.stubs(:vals).with(:parent).returns(%w{parent})
+
+ @parent.stubs(:to_hash).returns("three" => "four")
+ @parent.stubs(:vals).with(:parent).returns(nil)
+
+ @node.expects(:parameters=).with("one" => "two", "three" => "four")
+ @searcher.find("mynode")
+ end
+
+ it "should prefer node parameters over parent parameters" do
+ @entry.stubs(:to_hash).returns("one" => "two")
+ @entry.stubs(:vals).with(:parent).returns(%w{parent})
+
+ @parent.stubs(:to_hash).returns("one" => "three")
+ @parent.stubs(:vals).with(:parent).returns(nil)
+
+ @node.expects(:parameters=).with("one" => "two")
+ @searcher.find("mynode")
+ end
+
+ it "should recursively look up parent information" do
+ @entry.stubs(:to_hash).returns("one" => "two")
+ @entry.stubs(:vals).with(:parent).returns(%w{parent})
+
+ @parent.stubs(:to_hash).returns("three" => "four")
+ @parent.stubs(:vals).with(:parent).returns(['parent_parent'])
+
+ @parent_parent.stubs(:to_hash).returns("five" => "six")
+ @parent_parent.stubs(:vals).with(:parent).returns(nil)
+ @parent_parent.stubs(:vals).with(:parent).returns(nil)
+
+ @node.expects(:parameters=).with("one" => "two", "three" => "four", "five" => "six")
+ @searcher.find("mynode")
+ end
+
+ it "should not allow loops in parent declarations" do
+ @entry.stubs(:to_hash).returns("one" => "two")
+ @entry.stubs(:vals).with(:parent).returns(%w{parent})
+
+ @parent.stubs(:to_hash).returns("three" => "four")
+ @parent.stubs(:vals).with(:parent).returns([@name])
+ proc { @searcher.find("mynode") }.should raise_error(ArgumentError)
+ end
+end
+
+describe Puppet::Indirector::Ldap::Node, " when developing the search query" do
+ before do
+ @searcher = Puppet::Indirector::Ldap::Node.new
+ end
+
+ it "should return the value of the :ldapclassattrs split on commas as the class attributes" do
+ Puppet.stubs(:[]).with(:ldapclassattrs).returns("one,two")
+ @searcher.class_attributes.should == %w{one two}
+ end
+
+ it "should return nil as the parent attribute if the :ldapparentattr is set to an empty string" do
+ Puppet.stubs(:[]).with(:ldapparentattr).returns("")
+ @searcher.parent_attribute.should be_nil
+ end
+
+ it "should return the value of the :ldapparentattr as the parent attribute" do
+ Puppet.stubs(:[]).with(:ldapparentattr).returns("pere")
+ @searcher.parent_attribute.should == "pere"
+ end
+
+ it "should use the value of the :ldapstring as the search filter" do
+ Puppet.stubs(:[]).with(:ldapstring).returns("mystring")
+ @searcher.search_filter("testing").should == "mystring"
+ end
+
+ it "should replace '%s' with the node name in the search filter if it is present" do
+ Puppet.stubs(:[]).with(:ldapstring).returns("my%sstring")
+ @searcher.search_filter("testing").should == "mytestingstring"
+ end
+
+ it "should not modify the global :ldapstring when replacing '%s' in the search filter" do
+ filter = mock 'filter'
+ filter.expects(:include?).with("%s").returns(true)
+ filter.expects(:gsub).with("%s", "testing").returns("mynewstring")
+ Puppet.stubs(:[]).with(:ldapstring).returns(filter)
+ @searcher.search_filter("testing").should == "mynewstring"
+ end
+end
+
+describe Puppet::Indirector::Ldap::Node, " when deciding attributes to search for" do
+ before do
+ @searcher = Puppet::Indirector::Ldap::Node.new
+ end
+
+ it "should use 'nil' if the :ldapattrs setting is 'all'" do
+ Puppet.stubs(:[]).with(:ldapattrs).returns("all")
+ @searcher.search_attributes.should be_nil
+ end
+
+ it "should split the value of :ldapattrs on commas and use the result as the attribute list" do
+ Puppet.stubs(:[]).with(:ldapattrs).returns("one,two")
+ @searcher.stubs(:class_attributes).returns([])
+ @searcher.stubs(:parent_attribute).returns(nil)
+ @searcher.search_attributes.should == %w{one two}
+ end
+
+ it "should add the class attributes to the search attributes if not returning all attributes" do
+ Puppet.stubs(:[]).with(:ldapattrs).returns("one,two")
+ @searcher.stubs(:class_attributes).returns(%w{three four})
+ @searcher.stubs(:parent_attribute).returns(nil)
+ # Sort them so i don't have to care about return order
+ @searcher.search_attributes.sort.should == %w{one two three four}.sort
+ end
+
+ it "should add the parent attribute to the search attributes if not returning all attributes" do
+ Puppet.stubs(:[]).with(:ldapattrs).returns("one,two")
+ @searcher.stubs(:class_attributes).returns([])
+ @searcher.stubs(:parent_attribute).returns("parent")
+ @searcher.search_attributes.sort.should == %w{one two parent}.sort
+ end
+
+ it "should not add nil parent attributes to the search attributes" do
+ Puppet.stubs(:[]).with(:ldapattrs).returns("one,two")
+ @searcher.stubs(:class_attributes).returns([])
+ @searcher.stubs(:parent_attribute).returns(nil)
+ @searcher.search_attributes.should == %w{one two}
+ end
+end
diff --git a/spec/unit/indirector/memory.rb b/spec/unit/indirector/memory.rb
new file mode 100755
index 000000000..ac6f055ce
--- /dev/null
+++ b/spec/unit/indirector/memory.rb
@@ -0,0 +1,53 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'puppet/indirector/memory'
+
+describe "A Memory Terminus", :shared => true do
+ it "should find no instances by default" do
+ @searcher.find(@name).should be_nil
+ end
+
+ it "should be able to find instances that were previously saved" do
+ @searcher.save(@instance)
+ @searcher.find(@name).should equal(@instance)
+ end
+
+ it "should replace existing saved instances when a new instance with the same name is saved" do
+ @searcher.save(@instance)
+ two = stub 'second', :name => @name
+ @searcher.save(two)
+ @searcher.find(@name).should equal(two)
+ end
+
+ it "should be able to remove previously saved instances" do
+ @searcher.save(@instance)
+ @searcher.destroy(@instance)
+ @searcher.find(@name).should be_nil
+ end
+
+ it "should fail when asked to destroy an instance that does not exist" do
+ proc { @searcher.destroy(@instance) }.should raise_error(ArgumentError)
+ end
+end
+
+describe Puppet::Indirector::Memory do
+ it_should_behave_like "A Memory Terminus"
+
+ before do
+ Puppet::Indirector::Terminus.stubs(:register_terminus_class)
+ @model = mock 'model'
+ @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model
+ Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection)
+
+ @memory_class = Class.new(Puppet::Indirector::Memory) do
+ def self.to_s
+ "Testing"
+ end
+ end
+
+ @searcher = @memory_class.new
+ @name = "me"
+ @instance = stub 'instance', :name => @name
+ end
+end
diff --git a/spec/unit/indirector/memory/node.rb b/spec/unit/indirector/memory/node.rb
new file mode 100755
index 000000000..cba4af53a
--- /dev/null
+++ b/spec/unit/indirector/memory/node.rb
@@ -0,0 +1,19 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/memory/node'
+
+# All of our behaviour is described here, so we always have to
+# include it.
+require 'unit/indirector/memory'
+
+describe Puppet::Indirector::Memory::Node do
+ before do
+ @name = "me"
+ @searcher = Puppet::Indirector::Memory::Node.new
+ @instance = stub 'instance', :name => @name
+ end
+
+ it_should_behave_like "A Memory Terminus"
+end
diff --git a/spec/unit/indirector/null.rb b/spec/unit/indirector/null.rb
new file mode 100755
index 000000000..9e1dcb07c
--- /dev/null
+++ b/spec/unit/indirector/null.rb
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'puppet/indirector/null'
+
+describe Puppet::Indirector::Null do
+ before do
+ Puppet::Indirector::Terminus.stubs(:register_terminus_class)
+ @model = mock 'model'
+ @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model
+ Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection)
+
+ @null_class = Class.new(Puppet::Indirector::Null) do
+ def self.to_s
+ "Testing"
+ end
+ end
+
+ @searcher = @null_class.new
+ end
+
+ it "should return return an instance of the indirected model" do
+ object = mock 'object'
+ @model.expects(:new).with("yay").returns object
+ @searcher.find("yay").should equal(object)
+ end
+end
diff --git a/spec/unit/indirector/null/node.rb b/spec/unit/indirector/null/node.rb
new file mode 100755
index 000000000..c589e5820
--- /dev/null
+++ b/spec/unit/indirector/null/node.rb
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/null/node'
+
+describe Puppet::Indirector::Null::Node do
+ before do
+ @searcher = Puppet::Indirector::Null::Node.new
+ end
+
+ it "should call node_merge() on the returned node" do
+ node = mock 'node'
+ Puppet::Node.expects(:new).with("mynode").returns(node)
+ node.expects(:fact_merge)
+ @searcher.find("mynode")
+ end
+end
diff --git a/spec/unit/indirector/terminus.rb b/spec/unit/indirector/terminus.rb
new file mode 100755
index 000000000..dc86cf315
--- /dev/null
+++ b/spec/unit/indirector/terminus.rb
@@ -0,0 +1,226 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'puppet/defaults'
+require 'puppet/indirector'
+
+describe Puppet::Indirector::Terminus do
+ before do
+ Puppet::Indirector::Terminus.stubs(:register_terminus_class)
+
+ @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil
+ Puppet::Indirector::Indirection.stubs(:instance).with(:mystuff).returns(@indirection)
+ @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do
+ def self.to_s
+ "Abstract"
+ end
+ end
+ @terminus = Class.new(@abstract_terminus) do
+ def self.to_s
+ "Terminus::Type::MyStuff"
+ end
+ end
+ end
+
+ it "should provide a method for setting terminus class documentation" do
+ @terminus.should respond_to(:desc)
+ end
+
+ it "should support a class-level name attribute" do
+ @terminus.should respond_to(:name)
+ end
+
+ it "should support a class-level indirection attribute" do
+ @terminus.should respond_to(:indirection)
+ end
+
+ it "should support a class-level terminus-type attribute" do
+ @terminus.should respond_to(:terminus_type)
+ end
+
+ it "should support a class-level model attribute" do
+ @terminus.should respond_to(:model)
+ end
+
+ it "should accept indirection instances as its indirection" do
+ indirection = stub 'indirection', :is_a? => true, :register_terminus_type => nil
+ proc { @terminus.indirection = indirection }.should_not raise_error
+ @terminus.indirection.should equal(indirection)
+ end
+
+ it "should look up indirection instances when only a name has been provided" do
+ indirection = mock 'indirection'
+ Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(indirection)
+ @terminus.indirection = :myind
+ @terminus.indirection.should equal(indirection)
+ end
+
+ it "should fail when provided a name that does not resolve to an indirection" do
+ Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(nil)
+ proc { @terminus.indirection = :myind }.should raise_error(ArgumentError)
+
+ # It shouldn't overwrite our existing one (or, more normally, it shouldn't set
+ # anything).
+ @terminus.indirection.should equal(@indirection)
+ end
+end
+
+# LAK: This could reasonably be in the Indirection instances, too. It doesn't make
+# a whole heckuva lot of difference, except that with the instance loading in
+# the Terminus base class, we have to have a check to see if we're already
+# instance-loading a given terminus class type.
+describe Puppet::Indirector::Terminus, " when managing terminus classes" do
+ it "should provide a method for registering terminus classes" do
+ Puppet::Indirector::Terminus.should respond_to(:register_terminus_class)
+ end
+
+ it "should provide a method for returning terminus classes by name and type" do
+ terminus = stub 'terminus_type', :terminus_type => :abstract, :name => :whatever
+ Puppet::Indirector::Terminus.register_terminus_class(terminus)
+ Puppet::Indirector::Terminus.terminus_class(:abstract, :whatever).should equal(terminus)
+ end
+
+ it "should set up autoloading for any terminus class types requested" do
+ Puppet::Indirector::Terminus.expects(:instance_load).with(:test2, "puppet/indirector/test2")
+ Puppet::Indirector::Terminus.terminus_class(:test2, :whatever)
+ end
+
+ it "should load terminus classes that are not found" do
+ # Set up instance loading; it would normally happen automatically
+ Puppet::Indirector::Terminus.instance_load :test1, "puppet/indirector/test1"
+ Puppet::Indirector::Terminus.instance_loader(:test1).expects(:load).with(:yay)
+ Puppet::Indirector::Terminus.terminus_class(:test1, :yay)
+ end
+
+ it "should fail when no indirection can be found" do
+ Puppet::Indirector::Indirection.expects(:instance).with(:myindirection).returns(nil)
+
+ @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do
+ def self.to_s
+ "Abstract"
+ end
+ end
+ proc {
+ @terminus = Class.new(@abstract_terminus) do
+ def self.to_s
+ "MyIndirection"
+ end
+ end
+ }.should raise_error(ArgumentError)
+ end
+
+ it "should register the terminus class with the terminus base class" do
+ Puppet::Indirector::Terminus.expects(:register_terminus_class).with do |type|
+ type.terminus_type == :abstract and type.name == :myindirection
+ end
+ @indirection = stub 'indirection', :name => :myind, :register_terminus_type => nil
+ Puppet::Indirector::Indirection.expects(:instance).with(:myindirection).returns(@indirection)
+
+ @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do
+ def self.to_s
+ "Abstract"
+ end
+ end
+
+ @terminus = Class.new(@abstract_terminus) do
+ def self.to_s
+ "MyIndirection"
+ end
+ end
+ end
+end
+
+describe Puppet::Indirector::Terminus, " when creating terminus class types" do
+ before do
+ Puppet::Indirector::Terminus.stubs(:register_terminus_class)
+ @subclass = Class.new(Puppet::Indirector::Terminus) do
+ def self.to_s
+ "Puppet::Indirector::Terminus::MyTermType"
+ end
+ end
+ end
+
+ it "should set the name of the abstract subclass to be its class constant" do
+ @subclass.name.should equal(:mytermtype)
+ end
+
+ it "should mark abstract terminus types as such" do
+ @subclass.should be_abstract_terminus
+ end
+
+ it "should not allow instances of abstract subclasses to be created" do
+ proc { @subclass.new }.should raise_error(Puppet::DevError)
+ end
+end
+
+describe Puppet::Indirector::Terminus, " when creating terminus classes" do
+ before do
+ Puppet::Indirector::Terminus.stubs(:register_terminus_class)
+
+ @indirection = stub 'indirection', :name => :myind, :register_terminus_type => nil
+ Puppet::Indirector::Indirection.expects(:instance).with(:myindirection).returns(@indirection)
+
+ @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do
+ def self.to_s
+ "Abstract"
+ end
+ end
+ @terminus = Class.new(@abstract_terminus) do
+ def self.to_s
+ "MyIndirection"
+ end
+ end
+ end
+
+ it "should associate the subclass with an indirection based on the subclass constant" do
+ @terminus.indirection.should equal(@indirection)
+ end
+
+ it "should set the subclass's type to the abstract terminus name" do
+ @terminus.terminus_type.should == :abstract
+ end
+
+ it "should set the subclass's name to the indirection name" do
+ @terminus.name.should == :myindirection
+ end
+
+ it "should set the subclass's model to the indirection model" do
+ @indirection.expects(:model).returns :yay
+ @terminus.model.should == :yay
+ end
+end
+
+describe Puppet::Indirector::Terminus, " when a terminus instance" do
+ before do
+ Puppet::Indirector::Terminus.stubs(:register_terminus_class)
+ @indirection = stub 'indirection', :name => :myyaml, :register_terminus_type => nil
+ Puppet::Indirector::Indirection.stubs(:instance).with(:mystuff).returns(@indirection)
+ @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do
+ def self.to_s
+ "Abstract"
+ end
+ end
+ @terminus_class = Class.new(@abstract_terminus) do
+ def self.to_s
+ "MyStuff"
+ end
+ end
+ @terminus_class.name = :test
+ @terminus = @terminus_class.new
+ end
+
+ it "should return the class's name as its name" do
+ @terminus.name.should == :test
+ end
+
+ it "should return the class's indirection as its indirection" do
+ @terminus.indirection.should equal(@indirection)
+ end
+
+ it "should set the instances's type to the abstract terminus type's name" do
+ @terminus.terminus_type.should == :abstract
+ end
+
+ it "should set the instances's model to the indirection's model" do
+ @indirection.expects(:model).returns :yay
+ @terminus.model.should == :yay
+ end
+end
diff --git a/spec/unit/indirector/yaml.rb b/spec/unit/indirector/yaml.rb
new file mode 100755
index 000000000..9e1d65e49
--- /dev/null
+++ b/spec/unit/indirector/yaml.rb
@@ -0,0 +1,104 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/indirector/yaml'
+
+module YamlTesting
+ def setup
+ @indirection = stub 'indirection', :name => :myyaml, :register_terminus_type => nil
+ Puppet::Indirector::Indirection.stubs(:instance).with(:myyaml).returns(@indirection)
+ @store_class = Class.new(Puppet::Indirector::Yaml) do
+ def self.to_s
+ "MyYaml"
+ end
+ end
+ @store = @store_class.new
+
+ @subject = Object.new
+ @subject.metaclass.send(:attr_accessor, :name)
+ @subject.name = :me
+
+ @dir = "/what/ever"
+ Puppet.settings.stubs(:use)
+ Puppet.settings.stubs(:value).with(:yamldir).returns(@dir)
+ end
+end
+
+describe Puppet::Indirector::Yaml, " when choosing file location" do
+ include YamlTesting
+
+ it "should store all files in a single file root set in the Puppet defaults" do
+ @store.send(:path, :me).should =~ %r{^#{@dir}}
+ end
+
+ it "should use the terminus name for choosing the subdirectory" do
+ @store.send(:path, :me).should =~ %r{^#{@dir}/myyaml}
+ end
+
+ it "should use the object's name to determine the file name" do
+ @store.send(:path, :me).should =~ %r{me.yaml$}
+ end
+end
+
+describe Puppet::Indirector::Yaml, " when storing objects as YAML" do
+ include YamlTesting
+
+ it "should only store objects that respond to :name" do
+ proc { @store.save(Object.new) }.should raise_error(ArgumentError)
+ end
+
+ it "should convert Ruby objects to YAML and write them to disk" do
+ yaml = @subject.to_yaml
+ file = mock 'file'
+ path = @store.send(:path, @subject.name)
+ FileTest.expects(:exist?).with(File.dirname(path)).returns(true)
+ File.expects(:open).with(path, "w", 0660).yields(file)
+ file.expects(:print).with(yaml)
+
+ @store.save(@subject)
+ end
+
+ it "should create the indirection subdirectory if it does not exist" do
+ yaml = @subject.to_yaml
+ file = mock 'file'
+ path = @store.send(:path, @subject.name)
+ dir = File.dirname(path)
+ FileTest.expects(:exist?).with(dir).returns(false)
+ Dir.expects(:mkdir).with(dir)
+ File.expects(:open).with(path, "w", 0660).yields(file)
+ file.expects(:print).with(yaml)
+
+ @store.save(@subject)
+ end
+end
+
+describe Puppet::Indirector::Yaml, " when retrieving YAML" do
+ include YamlTesting
+
+ it "should require the name of the object to retrieve" do
+ proc { @store.find(nil) }.should raise_error(ArgumentError)
+ end
+
+ it "should read YAML in from disk and convert it to Ruby objects" do
+ path = @store.send(:path, @subject.name)
+
+ yaml = @subject.to_yaml
+ FileTest.expects(:exist?).with(path).returns(true)
+ File.expects(:read).with(path).returns(yaml)
+
+ @store.find(@subject.name).instance_variable_get("@name").should == :me
+ end
+
+ it "should fail coherently when the stored YAML is invalid" do
+ path = @store.send(:path, @subject.name)
+
+ # Something that will fail in yaml
+ yaml = "--- !ruby/object:Hash"
+
+ FileTest.expects(:exist?).with(path).returns(true)
+ File.expects(:read).with(path).returns(yaml)
+
+ proc { @store.find(@subject.name) }.should raise_error(Puppet::Error)
+ end
+end
diff --git a/spec/unit/indirector/yaml/facts.rb b/spec/unit/indirector/yaml/facts.rb
new file mode 100755
index 000000000..f1256cfa4
--- /dev/null
+++ b/spec/unit/indirector/yaml/facts.rb
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/node/facts'
+require 'puppet/indirector/yaml/facts'
+
+describe Puppet::Indirector::Yaml::Facts do
+ it "should be a subclass of the Yaml terminus" do
+ Puppet::Indirector::Yaml::Facts.superclass.should equal(Puppet::Indirector::Yaml)
+ end
+
+
+ it "should have documentation" do
+ Puppet::Indirector::Yaml::Facts.doc.should_not be_nil
+ end
+
+ it "should be registered with the facts indirection" do
+ indirection = Puppet::Indirector::Indirection.instance(:facts)
+ Puppet::Indirector::Yaml::Facts.indirection.should equal(indirection)
+ end
+
+ it "should have its name set to :facts" do
+ Puppet::Indirector::Yaml::Facts.name.should == :facts
+ end
+end
diff --git a/spec/unit/node/configuration.rb b/spec/unit/node/configuration.rb
index 4429fe3a3..8ba55f50c 100755
--- a/spec/unit/node/configuration.rb
+++ b/spec/unit/node/configuration.rb
@@ -133,3 +133,340 @@ describe Puppet::Node::Configuration, " when extracting transobjects" do
botarray.include?(:botres).should be_true
end
end
+
+describe Puppet::Node::Configuration, " when functioning as a resource container" do
+ before do
+ @config = Puppet::Node::Configuration.new("host")
+ @one = stub 'resource1', :ref => "Me[you]", :configuration= => nil
+ @two = stub 'resource2', :ref => "Me[him]", :configuration= => nil
+ @dupe = stub 'resource3', :ref => "Me[you]", :configuration= => nil
+ end
+
+ it "should provide a method to add one or more resources" do
+ @config.add_resource @one, @two
+ @config.resource(@one.ref).should equal(@one)
+ @config.resource(@two.ref).should equal(@two)
+ end
+
+ it "should make all vertices available by resource reference" do
+ @config.add_resource(@one)
+ @config.resource(@one.ref).should equal(@one)
+ @config.vertices.find { |r| r.ref == @one.ref }.should equal(@one)
+ end
+
+ it "should not allow two resources with the same resource reference" do
+ @config.add_resource(@one)
+ proc { @config.add_resource(@dupe) }.should raise_error(ArgumentError)
+ end
+
+ it "should not store objects that do not respond to :ref" do
+ proc { @config.add_resource("thing") }.should raise_error(ArgumentError)
+ end
+
+ it "should remove all resources when asked" do
+ @config.add_resource @one
+ @config.add_resource @two
+ @one.expects :remove
+ @two.expects :remove
+ @config.clear(true)
+ end
+
+ it "should support a mechanism for finishing resources" do
+ @one.expects :finish
+ @two.expects :finish
+ @config.add_resource @one
+ @config.add_resource @two
+
+ @config.finalize
+ end
+
+ it "should optionally support an initialization block and should finalize after such blocks" do
+ @one.expects :finish
+ @two.expects :finish
+ config = Puppet::Node::Configuration.new("host") do |conf|
+ conf.add_resource @one
+ conf.add_resource @two
+ end
+ end
+
+ it "should inform the resource that it is the resource's configuration" do
+ @one.expects(:configuration=).with(@config)
+ @config.add_resource @one
+ end
+
+ it "should be able to find resources by reference" do
+ @config.add_resource @one
+ @config.resource(@one.ref).should equal(@one)
+ end
+
+ it "should be able to find resources by reference or by type/title tuple" do
+ @config.add_resource @one
+ @config.resource("me", "you").should equal(@one)
+ end
+
+ it "should have a mechanism for removing resources" do
+ @config.add_resource @one
+ @one.expects :remove
+ @config.remove_resource(@one)
+ @config.resource(@one.ref).should be_nil
+ @config.vertex?(@one).should be_false
+ end
+end
+
+module ApplyingConfigurations
+ def setup
+ @config = Puppet::Node::Configuration.new("host")
+
+ @config.retrieval_duration = Time.now
+ @transaction = mock 'transaction'
+ Puppet::Transaction.stubs(:new).returns(@transaction)
+ @transaction.stubs(:evaluate)
+ @transaction.stubs(:cleanup)
+ @transaction.stubs(:addtimes)
+ end
+end
+
+describe Puppet::Node::Configuration, " when applying" do
+ include ApplyingConfigurations
+
+ it "should create and evaluate a transaction" do
+ @transaction.expects(:evaluate)
+ @config.apply
+ end
+
+ it "should provide the configuration time to the transaction" do
+ @transaction.expects(:addtimes).with do |arg|
+ arg[:config_retrieval].should be_instance_of(Time)
+ true
+ end
+ @config.apply
+ end
+
+ it "should clean up the transaction" do
+ @transaction.expects :cleanup
+ @config.apply
+ end
+
+ it "should return the transaction" do
+ @config.apply.should equal(@transaction)
+ end
+
+ it "should yield the transaction if a block is provided" do
+ @config.apply do |trans|
+ trans.should equal(@transaction)
+ end
+ end
+
+ it "should default to not being a host configuration" do
+ @config.host_config.should be_nil
+ end
+
+ it "should pass supplied tags on to the transaction" do
+ @transaction.expects(:tags=).with(%w{one two})
+ @config.apply(:tags => %w{one two})
+ end
+
+ it "should set ignoreschedules on the transaction if specified in apply()" do
+ @transaction.expects(:ignoreschedules=).with(true)
+ @config.apply(:ignoreschedules => true)
+ end
+end
+
+describe Puppet::Node::Configuration, " when applying host configurations" do
+ include ApplyingConfigurations
+
+ # super() doesn't work in the setup method for some reason
+ before do
+ @config.host_config = true
+ end
+
+ it "should send a report if reporting is enabled" do
+ Puppet[:report] = true
+ @transaction.expects :send_report
+ @config.apply
+ end
+
+ it "should send a report if report summaries are enabled" do
+ Puppet[:summarize] = true
+ @transaction.expects :send_report
+ @config.apply
+ end
+
+ it "should initialize the state database before applying a configuration" do
+ Puppet::Util::Storage.expects(:load)
+
+ # Short-circuit the apply, so we know we're loading before the transaction
+ Puppet::Transaction.expects(:new).raises ArgumentError
+ proc { @config.apply }.should raise_error(ArgumentError)
+ end
+
+ it "should sync the state database after applying" do
+ Puppet::Util::Storage.expects(:store)
+ @config.apply
+ end
+
+ after { Puppet.settings.clear }
+end
+
+describe Puppet::Node::Configuration, " when applying non-host configurations" do
+ include ApplyingConfigurations
+
+ before do
+ @config.host_config = false
+ end
+
+ it "should never send reports" do
+ Puppet[:report] = true
+ Puppet[:summarize] = true
+ @transaction.expects(:send_report).never
+ @config.apply
+ end
+
+ it "should never modify the state database" do
+ Puppet::Util::Storage.expects(:load).never
+ Puppet::Util::Storage.expects(:store).never
+ @config.apply
+ end
+
+ after { Puppet.settings.clear }
+end
+
+describe Puppet::Node::Configuration, " when creating a relationship graph" do
+ before do
+ @config = Puppet::Node::Configuration.new("host")
+ @compone = Puppet::Type::Component.create :name => "one"
+ @comptwo = Puppet::Type::Component.create :name => "two", :require => ["class", "one"]
+ @file = Puppet::Type.type(:file)
+ @one = @file.create :path => "/one"
+ @two = @file.create :path => "/two"
+ @config.add_edge! @compone, @one
+ @config.add_edge! @comptwo, @two
+
+ @three = @file.create :path => "/three"
+ @four = @file.create :path => "/four", :require => ["file", "/three"]
+ @five = @file.create :path => "/five"
+ @config.add_resource @compone, @comptwo, @one, @two, @three, @four, @five
+ @relationships = @config.relationship_graph
+ end
+
+ it "should be able to create a relationship graph" do
+ @relationships.should be_instance_of(Puppet::Node::Configuration)
+ end
+
+ it "should copy its host_config setting to the relationship graph" do
+ config = Puppet::Node::Configuration.new
+ config.host_config = true
+ config.relationship_graph.host_config.should be_true
+ end
+
+ it "should not have any components" do
+ @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil
+ end
+
+ it "should have all non-component resources from the configuration" do
+ # The failures print out too much info, so i just do a class comparison
+ @relationships.vertex?(@five).should be_true
+ end
+
+ it "should have all resource relationships set as edges" do
+ @relationships.edge?(@three, @four).should be_true
+ end
+
+ it "should copy component relationships to all contained resources" do
+ @relationships.edge?(@one, @two).should be_true
+ end
+
+ it "should get removed when the configuration is cleaned up" do
+ @relationships.expects(:clear).with(false)
+ @config.clear
+ @config.instance_variable_get("@relationship_graph").should be_nil
+ end
+
+ it "should create a new relationship graph after clearing the old one" do
+ @relationships.expects(:clear).with(false)
+ @config.clear
+ @config.relationship_graph.should be_instance_of(Puppet::Node::Configuration)
+ end
+
+ it "should look up resources in the relationship graph if not found in the main configuration" do
+ five = stub 'five', :ref => "File[five]", :configuration= => nil
+ @relationships.add_resource five
+ @config.resource(five.ref).should equal(five)
+ end
+
+ it "should provide a method to create additional resources that also registers the resource" do
+ args = {:name => "/yay", :ensure => :file}
+ resource = stub 'file', :ref => "File[/yay]", :configuration= => @config
+ Puppet::Type.type(:file).expects(:create).with(args).returns(resource)
+ @config.create_resource :file, args
+ @config.resource("File[/yay]").should equal(resource)
+ end
+
+ it "should provide a mechanism for creating implicit resources" do
+ args = {:name => "/yay", :ensure => :file}
+ resource = stub 'file', :ref => "File[/yay]", :configuration= => @config
+ Puppet::Type.type(:file).expects(:create).with(args).returns(resource)
+ resource.expects(:implicit=).with(true)
+ @config.create_implicit_resource :file, args
+ @config.resource("File[/yay]").should equal(resource)
+ end
+
+ it "should remove resources created mid-transaction" do
+ args = {:name => "/yay", :ensure => :file}
+ resource = stub 'file', :ref => "File[/yay]", :configuration= => @config
+ @transaction = mock 'transaction'
+ Puppet::Transaction.stubs(:new).returns(@transaction)
+ @transaction.stubs(:evaluate)
+ @transaction.stubs(:cleanup)
+ @transaction.stubs(:addtimes)
+ Puppet::Type.type(:file).expects(:create).with(args).returns(resource)
+ resource.expects :remove
+ @config.apply do |trans|
+ @config.create_resource :file, args
+ @config.resource("File[/yay]").should equal(resource)
+ end
+ @config.resource("File[/yay]").should be_nil
+ end
+
+ it "should remove resources from the relationship graph if it exists" do
+ @config.remove_resource(@one)
+ @config.relationship_graph.vertex?(@one).should be_false
+ end
+
+ after do
+ Puppet::Type.allclear
+ end
+end
+
+describe Puppet::Node::Configuration, " when writing dot files" do
+ before do
+ @config = Puppet::Node::Configuration.new("host")
+ @name = :test
+ @file = File.join(Puppet[:graphdir], @name.to_s + ".dot")
+ end
+ it "should only write when it is a host configuration" do
+ File.expects(:open).with(@file).never
+ @config.host_config = false
+ Puppet[:graph] = true
+ @config.write_graph(@name)
+ end
+
+ it "should only write when graphing is enabled" do
+ File.expects(:open).with(@file).never
+ @config.host_config = true
+ Puppet[:graph] = false
+ @config.write_graph(@name)
+ end
+
+ it "should write a dot file based on the passed name" do
+ File.expects(:open).with(@file, "w").yields(stub("file", :puts => nil))
+ @config.expects(:to_dot).with("name" => @name.to_s.capitalize)
+ @config.host_config = true
+ Puppet[:graph] = true
+ @config.write_graph(@name)
+ end
+
+ after do
+ Puppet.settings.clear
+ end
+end
diff --git a/spec/unit/node/facts.rb b/spec/unit/node/facts.rb
new file mode 100755
index 000000000..61f05a2b2
--- /dev/null
+++ b/spec/unit/node/facts.rb
@@ -0,0 +1,39 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/node/facts'
+
+describe Puppet::Node::Facts, " when indirecting" do
+ before do
+ @terminus = mock 'terminus'
+ Puppet::Node::Facts.stubs(:indirection).returns(@terminus)
+
+ # We have to clear the cache so that the facts ask for our terminus stub,
+ # instead of anything that might be cached.
+ Puppet::Indirector::Indirection.clear_cache
+ @facts = Puppet::Node::Facts.new("me", "one" => "two")
+ end
+
+ it "should redirect to the specified fact store for retrieval" do
+ @terminus.expects(:find).with(:my_facts)
+ Puppet::Node::Facts.find(:my_facts)
+ end
+
+ it "should redirect to the specified fact store for storage" do
+ @terminus.expects(:save).with(@facts)
+ @facts.save
+ end
+
+ after do
+ mocha_verify
+ Puppet::Indirector::Indirection.clear_cache
+ end
+end
+
+describe Puppet::Node::Facts, " when storing and retrieving" do
+ it "should add metadata to the facts" do
+ facts = Puppet::Node::Facts.new("me", "one" => "two", "three" => "four")
+ facts.values[:_timestamp].should be_instance_of(Time)
+ end
+end
diff --git a/spec/unit/other/node.rb b/spec/unit/node/node.rb
index 66d5ba9d7..fe5d2be8b 100755
--- a/spec/unit/other/node.rb
+++ b/spec/unit/node/node.rb
@@ -11,6 +11,10 @@ describe Puppet::Node, " when initializing" do
@node.name.should == "testnode"
end
+ it "should not allow nil node names" do
+ proc { Puppet::Node.new(nil) }.should raise_error(ArgumentError)
+ end
+
it "should default to an empty parameter hash" do
@node.parameters.should == {}
end
@@ -62,7 +66,7 @@ describe Puppet::Node, " when returning the environment" do
end
it "should return the central environment if there is no environment fact nor explicit environment" do
- Puppet.config.expects(:[]).with(:environment).returns(:centralenv)
+ Puppet.settings.expects(:[]).with(:environment).returns(:centralenv)
@node.environment.should == :centralenv
end
@@ -77,7 +81,7 @@ describe Puppet::Node, " when returning the environment" do
end
it "should not use an explicit environment that is an empty string" do
- Puppet.config.expects(:[]).with(:environment).returns(nil)
+ Puppet.settings.expects(:[]).with(:environment).returns(nil)
@node.environment.should be_nil
end
end
@@ -85,17 +89,47 @@ end
describe Puppet::Node, " when merging facts" do
before do
@node = Puppet::Node.new("testnode")
+ Puppet::Node::Facts.stubs(:find).with(@node.name).returns(Puppet::Node::Facts.new(@node.name, "one" => "c", "two" => "b"))
end
it "should prefer parameters already set on the node over facts from the node" do
@node.parameters = {"one" => "a"}
- @node.fact_merge("one" => "c")
+ @node.fact_merge
@node.parameters["one"].should == "a"
end
it "should add passed parameters to the parameter list" do
@node.parameters = {"one" => "a"}
- @node.fact_merge("two" => "b")
+ @node.fact_merge
@node.parameters["two"].should == "b"
end
+
+ it "should accept arbitrary parameters to merge into its parameters" do
+ @node.parameters = {"one" => "a"}
+ @node.merge "two" => "three"
+ @node.parameters["two"].should == "three"
+ end
+end
+
+describe Puppet::Node, " when indirecting" do
+ before do
+ @terminus = mock 'terminus'
+ Puppet::Node.stubs(:indirection).returns(@terminus)
+ end
+
+ it "should redirect to the specified node source" do
+ @terminus.expects(:find).with(:my_node.to_s)
+ Puppet::Node.find(:my_node.to_s)
+ end
+
+ after do
+ Puppet::Indirector::Indirection.clear_cache
+ end
+end
+
+describe Puppet::Node do
+ # LAK:NOTE This is used to keep track of when a given node has connected,
+ # so we can report on nodes that do not appear to connecting to the
+ # central server.
+ it "should provide a method for noting that the node has connected"
end
diff --git a/spec/unit/node/searching.rb b/spec/unit/node/searching.rb
new file mode 100755
index 000000000..e747996e4
--- /dev/null
+++ b/spec/unit/node/searching.rb
@@ -0,0 +1,79 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'puppet/node/searching'
+require 'puppet/node/facts'
+
+describe Puppet::Node::Searching, " when searching for nodes" do
+ before do
+ @searcher = Object.new
+ @searcher.extend(Puppet::Node::Searching)
+ @facts = Puppet::Node::Facts.new("foo", "hostname" => "yay", "domain" => "domain.com")
+ @node = Puppet::Node.new("foo")
+ Puppet::Node::Facts.stubs(:find).with("foo").returns(@facts)
+ end
+
+ it "should search for the node by its key first" do
+ names = []
+ @searcher.expects(:find).with do |name|
+ names << name
+ names == %w{foo}
+ end.returns(@node)
+ @searcher.search("foo").should equal(@node)
+ end
+
+ it "should return the first node found using the generated list of names" do
+ names = []
+ @searcher.expects(:find).with("foo").returns(nil)
+ @searcher.expects(:find).with("yay.domain.com").returns(@node)
+ @searcher.search("foo").should equal(@node)
+ end
+
+ it "should search for the rest of the names inversely by length" do
+ names = []
+ @facts.values["fqdn"] = "longer.than.the.normal.fqdn.com"
+ @searcher.stubs(:find).with do |name|
+ names << name
+ end
+ @searcher.search("foo")
+ # Strip off the key
+ names.shift
+
+ # And the 'default'
+ names.pop
+
+ length = 100
+ names.each do |name|
+ (name.length < length).should be_true
+ length = name.length
+ end
+ end
+
+ it "should attempt to find a default node if no names are found" do
+ names = []
+ @searcher.stubs(:find).with do |name|
+ names << name
+ end.returns(nil)
+ @searcher.search("foo")
+ names[-1].should == "default"
+ end
+
+ it "should cache the nodes" do
+ @searcher.expects(:find).with("foo").returns(@node)
+ @searcher.search("foo").should equal(@node)
+ @searcher.search("foo").should equal(@node)
+ end
+
+ it "should flush the node cache using the :filetimeout parameter" do
+ node2 = Puppet::Node.new("foo2")
+ Puppet[:filetimeout] = -1
+ # I couldn't get this to work with :expects
+ @searcher.stubs(:find).returns(@node, node2).then.raises(ArgumentError)
+ @searcher.search("foo").should equal(@node)
+ @searcher.search("foo").should equal(node2)
+ end
+
+ after do
+ Puppet.settings.clear
+ end
+end
diff --git a/spec/unit/other/checksum.rb b/spec/unit/other/checksum.rb
new file mode 100755
index 000000000..6a63e833d
--- /dev/null
+++ b/spec/unit/other/checksum.rb
@@ -0,0 +1,92 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2007-9-22.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/checksum'
+
+describe Puppet::Checksum do
+ it "should have 'Checksum' and the checksum algorithm when converted to a string" do
+ inst = Puppet::Checksum.new("whatever", "md5")
+ inst.to_s.should == "Checksum<{md5}#{inst.checksum}>"
+ end
+
+ it "should convert algorithm names to symbols when they are set after checksum creation" do
+ sum = Puppet::Checksum.new("whatever")
+ sum.algorithm = "md5"
+ sum.algorithm.should == :md5
+ end
+
+ it "should return the checksum as the name" do
+ sum = Puppet::Checksum.new("whatever")
+ sum.checksum.should == sum.name
+ end
+end
+
+describe Puppet::Checksum, " when initializing" do
+ before do
+ @content = "this is some content"
+ @sum = Puppet::Checksum.new(@content)
+ end
+
+ it "should require content" do
+ proc { Puppet::Checksum.new(nil) }.should raise_error(ArgumentError)
+ end
+
+ it "should set the content appropriately" do
+ @sum.content.should == @content
+ end
+
+ it "should calculate the checksum" do
+ require 'digest/md5'
+ Digest::MD5.expects(:hexdigest).with(@content).returns(:mychecksum)
+ @sum.checksum.should == :mychecksum
+ end
+
+ it "should not calculate the checksum until it is asked for" do
+ require 'digest/md5'
+ Digest::MD5.expects(:hexdigest).never
+ sum = Puppet::Checksum.new(@content, :md5)
+ end
+
+ it "should remove the old checksum value if the algorithm is changed" do
+ Digest::MD5.expects(:hexdigest).with(@content).returns(:oldsum)
+ oldsum = @sum.checksum
+ @sum.algorithm = :sha1
+ Digest::SHA1.expects(:hexdigest).with(@content).returns(:newsum)
+ @sum.checksum.should == :newsum
+ end
+
+ it "should default to 'md5' as the checksum algorithm if the algorithm is not in the name" do
+ @sum.algorithm.should == :md5
+ end
+
+ it "should support specifying the algorithm during initialization" do
+ sum = Puppet::Checksum.new(@content, :sha1)
+ sum.algorithm.should == :sha1
+ end
+
+ it "should fail when an unsupported algorithm is used" do
+ proc { Puppet::Checksum.new(@content, :nope) }.should raise_error(ArgumentError)
+ end
+end
+
+describe Puppet::Checksum, " when using back-ends" do
+ it "should redirect using Puppet::Indirector" do
+ Puppet::Indirector::Indirection.instance(:checksum).model.should equal(Puppet::Checksum)
+ end
+
+ it "should have a :save instance method" do
+ Puppet::Checksum.new("mysum").should respond_to(:save)
+ end
+
+ it "should respond to :find" do
+ Puppet::Checksum.should respond_to(:find)
+ end
+
+ it "should respond to :destroy" do
+ Puppet::Checksum.should respond_to(:destroy)
+ end
+end
diff --git a/spec/unit/other/modules.rb b/spec/unit/other/modules.rb
index 0ab37aa9e..f53c43e89 100755
--- a/spec/unit/other/modules.rb
+++ b/spec/unit/other/modules.rb
@@ -5,10 +5,10 @@ require File.dirname(__FILE__) + '/../../spec_helper'
describe Puppet::Module, " when building its search path" do
include PuppetTest
- it "should ignore unqualified paths in the search path" do
+ it "should fully qualify unqualified paths in the search path" do
Puppet[:modulepath] = "something:/my/something"
File.stubs(:directory?).returns(true)
- Puppet::Module.modulepath.should == %w{/my/something}
+ Puppet::Module.modulepath.should == [File.join(Dir.getwd, 'something'), "/my/something"]
end
it "should ignore paths that do not exist" do
@@ -26,7 +26,7 @@ describe Puppet::Module, " when building its search path" do
end
it "should use the environment-specific search path when a node environment is provided" do
- Puppet.config.expects(:value).with(:modulepath, "myenv").returns("/mone:/mtwo")
+ Puppet.settings.expects(:value).with(:modulepath, "myenv").returns("/mone:/mtwo")
File.stubs(:directory?).returns(true)
Puppet::Module.modulepath("myenv").should == %w{/mone /mtwo}
end
@@ -82,30 +82,30 @@ describe Puppet::Module, " when searching for templates" do
end
it "should use the main templatedir if no module is found" do
- Puppet.config.expects(:value).with(:templatedir, nil).returns("/my/templates")
+ Puppet.settings.expects(:value).with(:templatedir, nil).returns("/my/templates")
Puppet::Module.expects(:find).with("mymod", nil).returns(nil)
Puppet::Module.find_template("mymod/mytemplate").should == "/my/templates/mymod/mytemplate"
end
it "should return unqualified templates directly in the template dir" do
- Puppet.config.expects(:value).with(:templatedir, nil).returns("/my/templates")
+ Puppet.settings.expects(:value).with(:templatedir, nil).returns("/my/templates")
Puppet::Module.expects(:find).never
Puppet::Module.find_template("mytemplate").should == "/my/templates/mytemplate"
end
it "should use the environment templatedir if no module is found and an environment is specified" do
- Puppet.config.expects(:value).with(:templatedir, "myenv").returns("/myenv/templates")
+ Puppet.settings.expects(:value).with(:templatedir, "myenv").returns("/myenv/templates")
Puppet::Module.expects(:find).with("mymod", "myenv").returns(nil)
Puppet::Module.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate"
end
it "should use the node environment if specified" do
- Puppet.config.expects(:value).with(:modulepath, "myenv").returns("/my/templates")
+ Puppet.settings.expects(:value).with(:modulepath, "myenv").returns("/my/templates")
File.stubs(:directory?).returns(true)
Puppet::Module.find_template("mymod/envtemplate", "myenv").should == "/my/templates/mymod/templates/envtemplate"
end
- after { Puppet.config.clear }
+ after { Puppet.settings.clear }
end
describe Puppet::Module, " when searching for manifests" do
@@ -125,27 +125,27 @@ describe Puppet::Module, " when searching for manifests" do
end
it "should use the node environment if specified" do
- Puppet.config.expects(:value).with(:modulepath, "myenv").returns("/env/modules")
+ Puppet.settings.expects(:value).with(:modulepath, "myenv").returns("/env/modules")
File.stubs(:directory?).returns(true)
Dir.expects(:glob).with("/env/modules/mymod/manifests/envmanifest.pp").returns(%w{/env/modules/mymod/manifests/envmanifest.pp})
Puppet::Module.find_manifests("mymod/envmanifest.pp", :environment => "myenv").should == ["/env/modules/mymod/manifests/envmanifest.pp"]
end
it "should return all manifests matching the glob pattern" do
- Puppet.config.expects(:value).with(:modulepath, nil).returns("/my/modules")
+ Puppet.settings.expects(:value).with(:modulepath, nil).returns("/my/modules")
File.stubs(:directory?).returns(true)
Dir.expects(:glob).with("/my/modules/mymod/manifests/yay/*.pp").returns(%w{/one /two})
Puppet::Module.find_manifests("mymod/yay/*.pp").should == %w{/one /two}
end
it "should default to the 'init.pp' file in the manifests directory" do
- Puppet.config.expects(:value).with(:modulepath, nil).returns("/my/modules")
+ Puppet.settings.expects(:value).with(:modulepath, nil).returns("/my/modules")
File.stubs(:directory?).returns(true)
Dir.expects(:glob).with("/my/modules/mymod/manifests/init.pp").returns(%w{my manifest})
Puppet::Module.find_manifests("mymod").should == %w{my manifest}
end
- after { Puppet.config.clear }
+ after { Puppet.settings.clear }
end
describe Puppet::Module, " when returning files" do
diff --git a/spec/unit/other/pgraph.rb b/spec/unit/other/pgraph.rb
new file mode 100755
index 000000000..19809ac1e
--- /dev/null
+++ b/spec/unit/other/pgraph.rb
@@ -0,0 +1,285 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2007-9-12.
+# Copyright (c) 2006. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/util/graph'
+
+class Container
+ include Puppet::Util::Graph
+ include Enumerable
+ attr_accessor :name
+ def each
+ @children.each do |c| yield c end
+ end
+
+ def initialize(name, ary)
+ @name = name
+ @children = ary
+ end
+
+ def push(*ary)
+ ary.each { |c| @children.push(c)}
+ end
+
+ def to_s
+ @name
+ end
+end
+
+describe Puppet::PGraph do
+ before do
+ @graph = Puppet::PGraph.new
+ end
+
+ it "should correctly clear vertices and edges when asked" do
+ @graph.add_edge!("a", "b")
+ @graph.add_vertex! "c"
+ @graph.clear
+ @graph.vertices.should be_empty
+ @graph.edges.should be_empty
+ end
+end
+
+describe Puppet::PGraph, " when matching edges" do
+ before do
+ @graph = Puppet::PGraph.new
+ @event = Puppet::Event.new(:source => "a", :event => :yay)
+ @none = Puppet::Event.new(:source => "a", :event => :NONE)
+
+ @edges = {}
+ @edges["a/b"] = Puppet::Relationship["a", "b", {:event => :yay, :callback => :refresh}]
+ @edges["a/c"] = Puppet::Relationship["a", "c", {:event => :yay, :callback => :refresh}]
+ @graph.add_edge!(@edges["a/b"])
+ end
+
+ it "should match edges whose source matches the source of the event" do
+ @graph.matching_edges([@event]).should == [@edges["a/b"]]
+ end
+
+ it "should match always match nothing when the event is :NONE" do
+ @graph.matching_edges([@none]).should be_empty
+ end
+
+ it "should match multiple edges" do
+ @graph.add_edge!(@edges["a/c"])
+ @graph.matching_edges([@event]).sort.should == [@edges["a/b"], @edges["a/c"]].sort
+ end
+end
+
+describe Puppet::PGraph, " when determining dependencies" do
+ before do
+ @graph = Puppet::PGraph.new
+
+ @graph.add_edge!("a", "b")
+ @graph.add_edge!("a", "c")
+ @graph.add_edge!("b", "d")
+ end
+
+ it "should find all dependents when they are on multiple levels" do
+ @graph.dependents("a").sort.should == %w{b c d}.sort
+ end
+
+ it "should find single dependents" do
+ @graph.dependents("b").sort.should == %w{d}.sort
+ end
+
+ it "should return an empty array when there are no dependents" do
+ @graph.dependents("c").sort.should == [].sort
+ end
+
+ it "should find all dependencies when they are on multiple levels" do
+ @graph.dependencies("d").sort.should == %w{a b}
+ end
+
+ it "should find single dependencies" do
+ @graph.dependencies("c").sort.should == %w{a}
+ end
+
+ it "should return an empty array when there are no dependencies" do
+ @graph.dependencies("a").sort.should == []
+ end
+end
+
+describe Puppet::PGraph, " when splicing the relationship graph" do
+ def container_graph
+ @one = Container.new("one", %w{a b})
+ @two = Container.new("two", ["c", "d"])
+ @three = Container.new("three", ["i", "j"])
+ @middle = Container.new("middle", ["e", "f", @two])
+ @top = Container.new("top", ["g", "h", @middle, @one, @three])
+ @empty = Container.new("empty", [])
+
+ @contgraph = @top.to_graph
+
+ # We have to add the container to the main graph, else it won't
+ # be spliced in the dependency graph.
+ @contgraph.add_vertex!(@empty)
+ end
+
+ def dependency_graph
+ @depgraph = Puppet::PGraph.new
+ @contgraph.vertices.each do |v|
+ @depgraph.add_vertex(v)
+ end
+
+ # We have to specify a relationship to our empty container, else it
+ # never makes it into the dep graph in the first place.
+ {@one => @two, "f" => "c", "h" => @middle, "c" => @empty}.each do |source, target|
+ @depgraph.add_edge!(source, target, :callback => :refresh)
+ end
+ end
+
+ def splice
+ @depgraph.splice!(@contgraph, Container)
+ end
+
+ before do
+ container_graph
+ dependency_graph
+ splice
+ end
+
+ it "should not create a cyclic graph" do
+ @depgraph.should_not be_cyclic
+ end
+
+ # This is the real heart of splicing -- replacing all containers in
+ # our relationship and exploding their relationships so that each
+ # relationship to a container gets copied to all of its children.
+ it "should remove all Container objects from the dependency graph" do
+ @depgraph.vertices.find_all { |v| v.is_a?(Container) }.should be_empty
+ end
+
+ it "should add container relationships to contained objects" do
+ @contgraph.leaves(@middle).each do |leaf|
+ @depgraph.should be_edge("h", leaf)
+ end
+ end
+
+ it "should explode container-to-container relationships, making edges between all respective contained objects" do
+ @one.each do |oobj|
+ @two.each do |tobj|
+ @depgraph.should be_edge(oobj, tobj)
+ end
+ end
+ end
+
+ it "should no longer contain anything but the non-container objects" do
+ @depgraph.vertices.find_all { |v| ! v.is_a?(String) }.should be_empty
+ end
+
+ it "should copy labels" do
+ @depgraph.edges.each do |edge|
+ edge.label.should == {:callback => :refresh}
+ end
+ end
+
+ it "should not add labels to edges that have none" do
+ @depgraph.add_edge!(@two, @three)
+ splice
+ @depgraph.edge_label("c", "i").should == {}
+ end
+
+ it "should copy labels over edges that have none" do
+ @depgraph.add_edge!("c", @three, {:callback => :refresh})
+ splice
+ # And make sure the label got copied.
+ @depgraph.edge_label("c", "i").should == {:callback => :refresh}
+ end
+
+ it "should not replace a label with a nil label" do
+ # Lastly, add some new label-less edges and make sure the label stays.
+ @depgraph.add_edge!(@middle, @three)
+ @depgraph.add_edge!("c", @three, {:callback => :refresh})
+ splice
+ @depgraph.edge_label("c", "i").should == {:callback => :refresh}
+ end
+
+ it "should copy labels to all created edges" do
+ @depgraph.add_edge!(@middle, @three)
+ @depgraph.add_edge!("c", @three, {:callback => :refresh})
+ splice
+ @three.each do |child|
+ edge = @depgraph.edge_class.new("c", child)
+ @depgraph.should be_edge(edge)
+ @depgraph[edge].should == {:callback => :refresh}
+ end
+ end
+end
+
+# Labels in this graph are used for managing relationships,
+# including callbacks, so they're quite important.
+describe Puppet::PGraph, " when managing labels" do
+ before do
+ @graph = Puppet::PGraph.new
+ @label = {:callback => :yay}
+ end
+
+ it "should return nil for edges with no label" do
+ @graph.add_edge!(:a, :b)
+ @graph.edge_label(:a, :b).should be_nil
+ end
+
+ it "should just return empty label hashes" do
+ @graph.add_edge!(:a, :b, {})
+ @graph.edge_label(:a, :b).should == {}
+ end
+
+ it "should consider empty label hashes to be nil when copying" do
+ @graph.add_edge!(:a, :b)
+ @graph.copy_label(:a, :b, {})
+ @graph.edge_label(:a, :b).should be_nil
+ end
+
+ it "should return label hashes" do
+ @graph.add_edge!(:a, :b, @label)
+ @graph.edge_label(:a, :b).should == @label
+ end
+
+ it "should replace nil labels with real labels" do
+ @graph.add_edge!(:a, :b)
+ @graph.copy_label(:a, :b, @label)
+ @graph.edge_label(:a, :b).should == @label
+ end
+
+ it "should not replace labels with nil labels" do
+ @graph.add_edge!(:a, :b, @label)
+ @graph.copy_label(:a, :b, {})
+ @graph.edge_label(:a, :b).should == @label
+ end
+end
+
+describe Puppet::PGraph, " when sorting the graph" do
+ before do
+ @graph = Puppet::PGraph.new
+ end
+
+ def add_edges(hash)
+ hash.each do |a,b|
+ @graph.add_edge!(a, b)
+ end
+ end
+
+ it "should fail on two-vertex loops" do
+ add_edges :a => :b, :b => :a
+ proc { @graph.topsort }.should raise_error(Puppet::Error)
+ end
+
+ it "should fail on multi-vertex loops" do
+ add_edges :a => :b, :b => :c, :c => :a
+ proc { @graph.topsort }.should raise_error(Puppet::Error)
+ end
+
+ it "should fail when a larger tree contains a small cycle" do
+ add_edges :a => :b, :b => :a, :c => :a, :d => :c
+ proc { @graph.topsort }.should raise_error(Puppet::Error)
+ end
+
+ it "should succeed on trees with no cycles" do
+ add_edges :a => :b, :b => :e, :c => :a, :d => :c
+ proc { @graph.topsort }.should_not raise_error
+ end
+end
diff --git a/spec/unit/other/transaction.rb b/spec/unit/other/transaction.rb
new file mode 100755
index 000000000..7990d2eef
--- /dev/null
+++ b/spec/unit/other/transaction.rb
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Transaction, " when determining tags" do
+ before do
+ @config = Puppet::Node::Configuration.new
+ @transaction = Puppet::Transaction.new(@config)
+ end
+
+ it "should default to the tags specified in the :tags setting" do
+ Puppet.expects(:[]).with(:tags).returns("one")
+ @transaction.tags.should == %w{one}
+ end
+
+ it "should split tags based on ','" do
+ Puppet.expects(:[]).with(:tags).returns("one,two")
+ @transaction.tags.should == %w{one two}
+ end
+
+ it "should use any tags set after creation" do
+ Puppet.expects(:[]).with(:tags).never
+ @transaction.tags = %w{one two}
+ @transaction.tags.should == %w{one two}
+ end
+end
diff --git a/spec/unit/other/transbucket.rb b/spec/unit/other/transbucket.rb
new file mode 100755
index 000000000..8cb9abaa4
--- /dev/null
+++ b/spec/unit/other/transbucket.rb
@@ -0,0 +1,133 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::TransBucket do
+ before do
+ @bucket = Puppet::TransBucket.new
+ end
+
+ it "should be able to produce a RAL component" do
+ @bucket.name = "luke"
+ @bucket.type = "user"
+
+ resource = nil
+ proc { resource = @bucket.to_type }.should_not raise_error
+ resource.should be_instance_of(Puppet::Type::Component)
+ resource.title.should == "user[luke]"
+ end
+
+ it "should accept TransObjects into its children list" do
+ object = Puppet::TransObject.new("luke", "user")
+ proc { @bucket.push(object) }.should_not raise_error
+ @bucket.each do |o|
+ o.should equal(object)
+ end
+ end
+
+ it "should accept TransBuckets into its children list" do
+ object = Puppet::TransBucket.new()
+ proc { @bucket.push(object) }.should_not raise_error
+ @bucket.each do |o|
+ o.should equal(object)
+ end
+ end
+
+ it "should refuse to accept any children that are not TransObjects or TransBuckets" do
+ proc { @bucket.push "a test" }.should raise_error
+ end
+
+ it "should return nil as its reference when type or name is missing" do
+ @bucket.to_ref.should be_nil
+ end
+
+ it "should return the title as its reference" do
+ @bucket.name = "luke"
+ @bucket.type = "user"
+ @bucket.to_ref.should == "user[luke]"
+ end
+end
+
+describe Puppet::TransBucket, " when generating a configuration" do
+ before do
+ @bottom = Puppet::TransBucket.new
+ @bottom.type = "fake"
+ @bottom.name = "bottom"
+ @bottomobj = Puppet::TransObject.new("bottom", "user")
+ @bottom.push @bottomobj
+
+ @middle = Puppet::TransBucket.new
+ @middle.type = "fake"
+ @middle.name = "middle"
+ @middleobj = Puppet::TransObject.new("middle", "user")
+ @middle.push(@middleobj)
+ @middle.push(@bottom)
+
+ @top = Puppet::TransBucket.new
+ @top.type = "fake"
+ @top.name = "top"
+ @topobj = Puppet::TransObject.new("top", "user")
+ @top.push(@topobj)
+ @top.push(@middle)
+
+ @config = @top.to_configuration
+
+ @users = %w{top middle bottom}
+ @fakes = %w{fake[bottom] fake[middle] fake[top]}
+ end
+
+ it "should convert all transportable objects to RAL resources" do
+ @users.each do |name|
+ @config.vertices.find { |r| r.class.name == :user and r.title == name }.should be_instance_of(Puppet::Type.type(:user))
+ end
+ end
+
+ it "should convert all transportable buckets to RAL components" do
+ @fakes.each do |name|
+ @config.vertices.find { |r| r.class.name == :component and r.title == name }.should be_instance_of(Puppet::Type.type(:component))
+ end
+ end
+
+ it "should add all resources to the graph's resource table" do
+ @config.resource("fake[top]").should equal(@top)
+ end
+
+ it "should finalize all resources" do
+ @config.vertices.each do |vertex| vertex.should be_finalized end
+ end
+
+ it "should only call to_type on each resource once" do
+ @topobj.expects(:to_type)
+ @bottomobj.expects(:to_type)
+ @top.to_configuration
+ end
+
+ after do
+ Puppet::Type.allclear
+ end
+end
+
+describe Puppet::TransBucket, " when serializing" do
+ before do
+ @bucket = Puppet::TransBucket.new(%w{one two})
+ @bucket.name = "one"
+ @bucket.type = "two"
+ end
+
+ it "should be able to be dumped to yaml" do
+ proc { YAML.dump(@bucket) }.should_not raise_error
+ end
+
+ it "should dump YAML that produces an equivalent object" do
+ result = YAML.dump(@bucket)
+
+ newobj = YAML.load(result)
+ newobj.name.should == "one"
+ newobj.type.should == "two"
+ children = []
+ newobj.each do |o|
+ children << o
+ end
+ children.should == %w{one two}
+ end
+end
diff --git a/spec/unit/other/transobject.rb b/spec/unit/other/transobject.rb
new file mode 100755
index 000000000..07c9dc761
--- /dev/null
+++ b/spec/unit/other/transobject.rb
@@ -0,0 +1,116 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::TransObject, " when building its search path" do
+end
+
+describe Puppet::TransObject, " when building its search path" do
+end
+#!/usr/bin/env ruby
+
+$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
+
+require 'puppet'
+require 'puppet/transportable'
+require 'puppettest'
+require 'puppettest/parsertesting'
+require 'yaml'
+
+class TestTransportable < Test::Unit::TestCase
+ include PuppetTest::ParserTesting
+
+ def test_yamldumpobject
+ obj = mk_transobject
+ obj.to_yaml_properties
+ str = nil
+ assert_nothing_raised {
+ str = YAML.dump(obj)
+ }
+
+ newobj = nil
+ assert_nothing_raised {
+ newobj = YAML.load(str)
+ }
+
+ assert(newobj.name, "Object has no name")
+ assert(newobj.type, "Object has no type")
+ end
+
+ def test_yamldumpbucket
+ objects = %w{/etc/passwd /etc /tmp /var /dev}.collect { |d|
+ mk_transobject(d)
+ }
+ bucket = mk_transbucket(*objects)
+ str = nil
+ assert_nothing_raised {
+ str = YAML.dump(bucket)
+ }
+
+ newobj = nil
+ assert_nothing_raised {
+ newobj = YAML.load(str)
+ }
+
+ assert(newobj.name, "Bucket has no name")
+ assert(newobj.type, "Bucket has no type")
+ end
+
+ # Verify that we correctly strip out collectable objects, since they should
+ # not be sent to the client.
+ def test_collectstrip
+ top = mk_transtree do |object, depth, width|
+ if width % 2 == 1
+ object.collectable = true
+ end
+ end
+
+ assert(top.flatten.find_all { |o| o.collectable }.length > 0,
+ "Could not find any collectable objects")
+
+ # Now strip out the collectable objects
+ top.collectstrip!
+
+ # And make sure they're actually gone
+ assert_equal(0, top.flatten.find_all { |o| o.collectable }.length,
+ "Still found collectable objects")
+ end
+
+ # Make sure our 'delve' command is working
+ def test_delve
+ top = mk_transtree do |object, depth, width|
+ if width % 2 == 1
+ object.collectable = true
+ end
+ end
+
+ objects = []
+ buckets = []
+ collectable = []
+
+ count = 0
+ assert_nothing_raised {
+ top.delve do |object|
+ count += 1
+ if object.is_a? Puppet::TransBucket
+ buckets << object
+ else
+ objects << object
+ if object.collectable
+ collectable << object
+ end
+ end
+ end
+ }
+
+ top.flatten.each do |obj|
+ assert(objects.include?(obj), "Missing obj %s[%s]" % [obj.type, obj.name])
+ end
+
+ assert_equal(collectable.length,
+ top.flatten.find_all { |o| o.collectable }.length,
+ "Found incorrect number of collectable objects")
+ end
+end
+
+# $Id$
diff --git a/spec/unit/parser/interpreter.rb b/spec/unit/parser/interpreter.rb
index c0f9d54b3..a79267b52 100755
--- a/spec/unit/parser/interpreter.rb
+++ b/spec/unit/parser/interpreter.rb
@@ -77,8 +77,8 @@ describe Puppet::Parser::Interpreter, " when creating parser instances" do
file = mock 'file'
file.stubs(:changed?).returns(true)
file.stubs(:file).returns("/whatever")
- Puppet.config.stubs(:read_file).with(file).returns(text)
- Puppet.config.parse(file)
+ Puppet.settings.stubs(:read_file).with(file).returns(text)
+ Puppet.settings.parse(file)
parser1 = mock 'parser1'
Puppet::Parser::Parser.expects(:new).with(:environment => :env1).returns(parser1)
diff --git a/spec/unit/ral/type.rb b/spec/unit/ral/type.rb
new file mode 100755
index 000000000..c8bf8c9b4
--- /dev/null
+++ b/spec/unit/ral/type.rb
@@ -0,0 +1,25 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Type, " when in a configuration" do
+ before do
+ @configuration = Puppet::Node::Configuration.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")
+ @configuration.add_resource @container
+ @configuration.add_resource @one
+ @configuration.add_resource @two
+ @configuration.add_edge! @container, @one
+ @configuration.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 equal(@container.ref)
+ end
+end
diff --git a/spec/unit/util/config.rb b/spec/unit/util/config.rb
deleted file mode 100755
index 348a54893..000000000
--- a/spec/unit/util/config.rb
+++ /dev/null
@@ -1,408 +0,0 @@
-#!/usr/bin/env ruby
-
-require File.dirname(__FILE__) + '/../../spec_helper'
-
-describe Puppet::Util::Config, " when specifying defaults" do
- before do
- @config = Puppet::Util::Config.new
- end
-
- it "should start with no defined parameters" do
- @config.params.length.should == 0
- end
-
- it "should allow specification of default values associated with a section as an array" do
- @config.setdefaults(:section, :myvalue => ["defaultval", "my description"])
- end
-
- it "should not allow duplicate parameter specifications" do
- @config.setdefaults(:section, :myvalue => ["a", "b"])
- lambda { @config.setdefaults(:section, :myvalue => ["c", "d"]) }.should raise_error(ArgumentError)
- end
-
- it "should allow specification of default values associated with a section as a hash" do
- @config.setdefaults(:section, :myvalue => {:default => "defaultval", :desc => "my description"})
- end
-
- it "should consider defined parameters to be valid" do
- @config.setdefaults(:section, :myvalue => ["defaultval", "my description"])
- @config.valid?(:myvalue).should be_true
- end
-
- it "should require a description when defaults are specified with an array" do
- lambda { @config.setdefaults(:section, :myvalue => ["a value"]) }.should raise_error(ArgumentError)
- end
-
- it "should require a description when defaults are specified with a hash" do
- lambda { @config.setdefaults(:section, :myvalue => {:default => "a value"}) }.should raise_error(ArgumentError)
- end
-
- it "should support specifying owner, group, and mode when specifying files" do
- @config.setdefaults(:section, :myvalue => {:default => "/some/file", :owner => "blah", :mode => "boo", :group => "yay", :desc => "whatever"})
- end
-
- it "should support specifying a short name" do
- @config.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"})
- end
-
- it "should fail when short names conflict" do
- @config.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"})
- lambda { @config.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) }.should raise_error(ArgumentError)
- end
-end
-
-describe Puppet::Util::Config, " when setting values" do
- before do
- @config = Puppet::Util::Config.new
- @config.setdefaults :main, :myval => ["val", "desc"]
- @config.setdefaults :main, :bool => [true, "desc"]
- end
-
- it "should provide a method for setting values from other objects" do
- @config[:myval] = "something else"
- @config[:myval].should == "something else"
- end
-
- it "should support a getopt-specific mechanism for setting values" do
- @config.handlearg("--myval", "newval")
- @config[:myval].should == "newval"
- end
-
- it "should support a getopt-specific mechanism for turning booleans off" do
- @config.handlearg("--no-bool")
- @config[:bool].should == false
- end
-
- it "should support a getopt-specific mechanism for turning booleans on" do
- # Turn it off first
- @config[:bool] = false
- @config.handlearg("--bool")
- @config[:bool].should == true
- end
-
- it "should clear the cache when setting getopt-specific values" do
- @config.setdefaults :mysection, :one => ["whah", "yay"], :two => ["$one yay", "bah"]
- @config[:two].should == "whah yay"
- @config.handlearg("--one", "else")
- @config[:two].should == "else yay"
- end
-
- it "should not clear other values when setting getopt-specific values" do
- @config[:myval] = "yay"
- @config.handlearg("--no-bool")
- @config[:myval].should == "yay"
- end
-
- it "should call passed blocks when values are set" do
- values = []
- @config.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }})
- values.should == []
-
- @config[:hooker] = "something"
- values.should == %w{something}
- end
-
- it "should munge values using the element-specific methods" do
- @config[:bool] = "false"
- @config[:bool].should == false
- end
-
- it "should prefer cli values to values set in Ruby code" do
- @config.handlearg("--myval", "cliarg")
- @config[:myval] = "memarg"
- @config[:myval].should == "cliarg"
- end
-end
-
-describe Puppet::Util::Config, " when returning values" do
- before do
- @config = Puppet::Util::Config.new
- @config.setdefaults :section, :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"], :four => ["$two $three FOUR", "d"]
- end
-
- it "should provide a mechanism for returning set values" do
- @config[:one] = "other"
- @config[:one].should == "other"
- end
-
- it "should interpolate default values for other parameters into returned parameter values" do
- @config[:one].should == "ONE"
- @config[:two].should == "ONE TWO"
- @config[:three].should == "ONE ONE TWO THREE"
- end
-
- it "should interpolate default values that themselves need to be interpolated" do
- @config[:four].should == "ONE TWO ONE ONE TWO THREE FOUR"
- end
-
- it "should interpolate set values for other parameters into returned parameter values" do
- @config[:one] = "on3"
- @config[:two] = "$one tw0"
- @config[:three] = "$one $two thr33"
- @config[:four] = "$one $two $three f0ur"
- @config[:one].should == "on3"
- @config[:two].should == "on3 tw0"
- @config[:three].should == "on3 on3 tw0 thr33"
- @config[:four].should == "on3 on3 tw0 on3 on3 tw0 thr33 f0ur"
- end
-
- it "should not cache interpolated values such that stale information is returned" do
- @config[:two].should == "ONE TWO"
- @config[:one] = "one"
- @config[:two].should == "one TWO"
- end
-
- it "should not cache values such that information from one environment is returned for another environment" do
- text = "[env1]\none = oneval\n[env2]\none = twoval\n"
- file = mock 'file'
- file.stubs(:changed?).returns(true)
- file.stubs(:file).returns("/whatever")
- @config.stubs(:read_file).with(file).returns(text)
- @config.parse(file)
-
- @config.value(:one, "env1").should == "oneval"
- @config.value(:one, "env2").should == "twoval"
- end
-
- it "should have a name determined by the 'name' parameter" do
- @config.setdefaults(:whatever, :name => ["something", "yayness"])
- @config.name.should == :something
- @config[:name] = :other
- @config.name.should == :other
- end
-end
-
-describe Puppet::Util::Config, " when choosing which value to return" do
- before do
- @config = Puppet::Util::Config.new
- @config.setdefaults :section,
- :one => ["ONE", "a"],
- :name => ["myname", "w"]
- end
-
- it "should return default values if no values have been set" do
- @config[:one].should == "ONE"
- end
-
- it "should return values set on the cli before values set in the configuration file" do
- text = "[main]\none = fileval\n"
- file = mock 'file'
- file.stubs(:changed?).returns(true)
- file.stubs(:file).returns("/whatever")
- @config.stubs(:parse_file).returns(text)
- @config.handlearg("--one", "clival")
- @config.parse(file)
-
- @config[:one].should == "clival"
- end
-
- it "should return values set on the cli before values set in Ruby" do
- @config[:one] = "rubyval"
- @config.handlearg("--one", "clival")
- @config[:one].should == "clival"
- end
-
- it "should return values set in the executable-specific section before values set in the main section" do
- text = "[main]\none = mainval\n[myname]\none = nameval\n"
- file = mock 'file'
- file.stubs(:changed?).returns(true)
- file.stubs(:file).returns("/whatever")
- @config.stubs(:read_file).with(file).returns(text)
- @config.parse(file)
-
- @config[:one].should == "nameval"
- end
-
- it "should not return values outside of its search path" do
- text = "[other]\none = oval\n"
- file = "/some/file"
- file = mock 'file'
- file.stubs(:changed?).returns(true)
- file.stubs(:file).returns("/whatever")
- @config.stubs(:read_file).with(file).returns(text)
- @config.parse(file)
- @config[:one].should == "ONE"
- end
-
- it "should return values in a specified environment" do
- text = "[env]\none = envval\n"
- file = "/some/file"
- file = mock 'file'
- file.stubs(:changed?).returns(true)
- file.stubs(:file).returns("/whatever")
- @config.stubs(:read_file).with(file).returns(text)
- @config.parse(file)
- @config.value(:one, "env").should == "envval"
- 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"
- file = mock 'file'
- file.stubs(:changed?).returns(true)
- file.stubs(:file).returns("/whatever")
- @config.stubs(:read_file).with(file).returns(text)
- @config.parse(file)
- @config.value(:one, "env").should == "envval"
- end
-end
-
-describe Puppet::Util::Config, " when parsing its configuration" do
- before do
- @config = Puppet::Util::Config.new
- @config.setdefaults :section, :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"]
- end
-
- it "should return values set in the configuration file" do
- text = "[main]
- one = fileval
- "
- file = "/some/file"
- @config.expects(:read_file).with(file).returns(text)
- @config.parse(file)
- @config[:one].should == "fileval"
- end
-
- #484 - this should probably be in the regression area
- it "should not throw an exception on unknown parameters" do
- text = "[main]\nnosuchparam = mval\n"
- file = "/some/file"
- @config.expects(:read_file).with(file).returns(text)
- lambda { @config.parse(file) }.should_not raise_error
- end
-
- it "should support an old parse method when per-executable configuration files still exist" do
- # I'm not going to bother testing this method.
- @config.should respond_to(:old_parse)
- end
-
- it "should convert booleans in the configuration file into Ruby booleans" do
- text = "[main]
- one = true
- two = false
- "
- file = "/some/file"
- @config.expects(:read_file).with(file).returns(text)
- @config.parse(file)
- @config[:one].should == true
- @config[:two].should == false
- end
-
- it "should convert integers in the configuration file into Ruby Integers" do
- text = "[main]
- one = 65
- "
- file = "/some/file"
- @config.expects(:read_file).with(file).returns(text)
- @config.parse(file)
- @config[:one].should == 65
- end
-
- it "should support specifying file all metadata (owner, group, mode) in the configuration file" do
- @config.setdefaults :section, :myfile => ["/my/file", "a"]
-
- text = "[main]
- myfile = /other/file {owner = luke, group = luke, mode = 644}
- "
- file = "/some/file"
- @config.expects(:read_file).with(file).returns(text)
- @config.parse(file)
- @config[:myfile].should == "/other/file"
- @config.metadata(:myfile).should == {:owner => "luke", :group => "luke", :mode => "644"}
- end
-
- it "should support specifying file a single piece of metadata (owner, group, or mode) in the configuration file" do
- @config.setdefaults :section, :myfile => ["/my/file", "a"]
-
- text = "[main]
- myfile = /other/file {owner = luke}
- "
- file = "/some/file"
- @config.expects(:read_file).with(file).returns(text)
- @config.parse(file)
- @config[:myfile].should == "/other/file"
- @config.metadata(:myfile).should == {:owner => "luke"}
- end
-end
-
-describe Puppet::Util::Config, " when reparsing its configuration" do
- before do
- @config = Puppet::Util::Config.new
- @config.setdefaults :section, :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"]
- end
-
- it "should replace in-memory values with on-file values" do
- # Init the value
- text = "[main]\none = disk-init\n"
- file = mock 'file'
- file.stubs(:changed?).returns(true)
- file.stubs(:file).returns("/test/file")
- @config[:one] = "init"
- @config.file = file
-
- # Now replace the value
- text = "[main]\none = disk-replace\n"
-
- # This is kinda ridiculous - the reason it parses twice is that
- # it goes to parse again when we ask for the value, because the
- # mock always says it should get reparsed.
- @config.expects(:read_file).with(file).returns(text).times(2)
- @config.reparse
- @config[:one].should == "disk-replace"
- end
-
- it "should retain parameters set by cli when configuration files are reparsed" do
- @config.handlearg("--one", "clival")
-
- text = "[main]\none = on-disk\n"
- file = mock 'file'
- file.stubs(:file).returns("/test/file")
- @config.stubs(:read_file).with(file).returns(text)
- @config.parse(file)
-
- @config[:one].should == "clival"
- end
-
- it "should remove in-memory values that are no longer set in the file" do
- # Init the value
- text = "[main]\none = disk-init\n"
- file = mock 'file'
- file.stubs(:changed?).returns(true)
- file.stubs(:file).returns("/test/file")
- @config.expects(:read_file).with(file).returns(text)
- @config.parse(file)
- @config[:one].should == "disk-init"
-
- # Now replace the value
- text = "[main]\ntwo = disk-replace\n"
- @config.expects(:read_file).with(file).returns(text)
- @config.parse(file)
- #@config.reparse
-
- # The originally-overridden value should be replaced with the default
- @config[:one].should == "ONE"
-
- # and we should now have the new value in memory
- @config[:two].should == "disk-replace"
- end
-end
-
-#describe Puppet::Util::Config, " when being used to manage the host machine" do
-# it "should provide a method that writes files with the correct modes"
-#
-# it "should provide a method that creates directories with the correct modes"
-#
-# it "should provide a method to declare what directories should exist"
-#
-# it "should provide a method to trigger enforcing of file modes on existing files and directories"
-#
-# it "should provide a method to convert the file mode enforcement into a Puppet manifest"
-#
-# it "should provide an option to create needed users and groups"
-#
-# it "should provide a method to print out the current configuration"
-#
-# it "should be able to provide all of its parameters in a format compatible with GetOpt::Long"
-#
-# it "should not attempt to manage files within /dev"
-#end
diff --git a/spec/unit/util/settings.rb b/spec/unit/util/settings.rb
new file mode 100755
index 000000000..8d11737b3
--- /dev/null
+++ b/spec/unit/util/settings.rb
@@ -0,0 +1,535 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Util::Settings, " when specifying defaults" do
+ before do
+ @settings = Puppet::Util::Settings.new
+ end
+
+ it "should start with no defined parameters" do
+ @settings.params.length.should == 0
+ end
+
+ it "should allow specification of default values associated with a section as an array" do
+ @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"])
+ end
+
+ it "should not allow duplicate parameter specifications" do
+ @settings.setdefaults(:section, :myvalue => ["a", "b"])
+ lambda { @settings.setdefaults(:section, :myvalue => ["c", "d"]) }.should raise_error(ArgumentError)
+ end
+
+ it "should allow specification of default values associated with a section as a hash" do
+ @settings.setdefaults(:section, :myvalue => {:default => "defaultval", :desc => "my description"})
+ end
+
+ it "should consider defined parameters to be valid" do
+ @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"])
+ @settings.valid?(:myvalue).should be_true
+ end
+
+ it "should require a description when defaults are specified with an array" do
+ lambda { @settings.setdefaults(:section, :myvalue => ["a value"]) }.should raise_error(ArgumentError)
+ end
+
+ it "should require a description when defaults are specified with a hash" do
+ lambda { @settings.setdefaults(:section, :myvalue => {:default => "a value"}) }.should raise_error(ArgumentError)
+ end
+
+ it "should support specifying owner, group, and mode when specifying files" do
+ @settings.setdefaults(:section, :myvalue => {:default => "/some/file", :owner => "blah", :mode => "boo", :group => "yay", :desc => "whatever"})
+ end
+
+ it "should support specifying a short name" do
+ @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"})
+ end
+
+ it "should fail when short names conflict" do
+ @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"})
+ lambda { @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) }.should raise_error(ArgumentError)
+ end
+end
+
+describe Puppet::Util::Settings, " when setting values" do
+ before do
+ @settings = Puppet::Util::Settings.new
+ @settings.setdefaults :main, :myval => ["val", "desc"]
+ @settings.setdefaults :main, :bool => [true, "desc"]
+ end
+
+ it "should provide a method for setting values from other objects" do
+ @settings[:myval] = "something else"
+ @settings[:myval].should == "something else"
+ end
+
+ it "should support a getopt-specific mechanism for setting values" do
+ @settings.handlearg("--myval", "newval")
+ @settings[:myval].should == "newval"
+ end
+
+ it "should support a getopt-specific mechanism for turning booleans off" do
+ @settings.handlearg("--no-bool")
+ @settings[:bool].should == false
+ end
+
+ it "should support a getopt-specific mechanism for turning booleans on" do
+ # Turn it off first
+ @settings[:bool] = false
+ @settings.handlearg("--bool")
+ @settings[:bool].should == true
+ end
+
+ it "should clear the cache when setting getopt-specific values" do
+ @settings.setdefaults :mysection, :one => ["whah", "yay"], :two => ["$one yay", "bah"]
+ @settings[:two].should == "whah yay"
+ @settings.handlearg("--one", "else")
+ @settings[:two].should == "else yay"
+ end
+
+ it "should not clear other values when setting getopt-specific values" do
+ @settings[:myval] = "yay"
+ @settings.handlearg("--no-bool")
+ @settings[:myval].should == "yay"
+ end
+
+ it "should call passed blocks when values are set" do
+ values = []
+ @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }})
+ values.should == []
+
+ @settings[:hooker] = "something"
+ values.should == %w{something}
+ end
+
+ it "should munge values using the element-specific methods" do
+ @settings[:bool] = "false"
+ @settings[:bool].should == false
+ end
+
+ it "should prefer cli values to values set in Ruby code" do
+ @settings.handlearg("--myval", "cliarg")
+ @settings[:myval] = "memarg"
+ @settings[:myval].should == "cliarg"
+ end
+end
+
+describe Puppet::Util::Settings, " when returning values" do
+ before do
+ @settings = Puppet::Util::Settings.new
+ @settings.setdefaults :section, :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"], :four => ["$two $three FOUR", "d"]
+ end
+
+ it "should provide a mechanism for returning set values" do
+ @settings[:one] = "other"
+ @settings[:one].should == "other"
+ end
+
+ it "should interpolate default values for other parameters into returned parameter values" do
+ @settings[:one].should == "ONE"
+ @settings[:two].should == "ONE TWO"
+ @settings[:three].should == "ONE ONE TWO THREE"
+ end
+
+ it "should interpolate default values that themselves need to be interpolated" do
+ @settings[:four].should == "ONE TWO ONE ONE TWO THREE FOUR"
+ end
+
+ it "should interpolate set values for other parameters into returned parameter values" do
+ @settings[:one] = "on3"
+ @settings[:two] = "$one tw0"
+ @settings[:three] = "$one $two thr33"
+ @settings[:four] = "$one $two $three f0ur"
+ @settings[:one].should == "on3"
+ @settings[:two].should == "on3 tw0"
+ @settings[:three].should == "on3 on3 tw0 thr33"
+ @settings[:four].should == "on3 on3 tw0 on3 on3 tw0 thr33 f0ur"
+ end
+
+ it "should not cache interpolated values such that stale information is returned" do
+ @settings[:two].should == "ONE TWO"
+ @settings[:one] = "one"
+ @settings[:two].should == "one TWO"
+ end
+
+ it "should not cache values such that information from one environment is returned for another environment" do
+ text = "[env1]\none = oneval\n[env2]\none = twoval\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @settings.stubs(:read_file).with(file).returns(text)
+ @settings.parse(file)
+
+ @settings.value(:one, "env1").should == "oneval"
+ @settings.value(:one, "env2").should == "twoval"
+ end
+
+ it "should have a name determined by the 'name' parameter" do
+ @settings.setdefaults(:whatever, :name => ["something", "yayness"])
+ @settings.name.should == :something
+ @settings[:name] = :other
+ @settings.name.should == :other
+ end
+end
+
+describe Puppet::Util::Settings, " when choosing which value to return" do
+ before do
+ @settings = Puppet::Util::Settings.new
+ @settings.setdefaults :section,
+ :one => ["ONE", "a"],
+ :name => ["myname", "w"]
+ end
+
+ it "should return default values if no values have been set" do
+ @settings[:one].should == "ONE"
+ end
+
+ it "should return values set on the cli before values set in the configuration file" do
+ text = "[main]\none = fileval\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @settings.stubs(:parse_file).returns(text)
+ @settings.handlearg("--one", "clival")
+ @settings.parse(file)
+
+ @settings[:one].should == "clival"
+ end
+
+ it "should return values set on the cli before values set in Ruby" do
+ @settings[:one] = "rubyval"
+ @settings.handlearg("--one", "clival")
+ @settings[:one].should == "clival"
+ end
+
+ it "should return values set in the executable-specific section before values set in the main section" do
+ text = "[main]\none = mainval\n[myname]\none = nameval\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @settings.stubs(:read_file).with(file).returns(text)
+ @settings.parse(file)
+
+ @settings[:one].should == "nameval"
+ end
+
+ it "should not return values outside of its search path" do
+ text = "[other]\none = oval\n"
+ file = "/some/file"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @settings.stubs(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ @settings[:one].should == "ONE"
+ end
+
+ it "should return values in a specified environment" do
+ text = "[env]\none = envval\n"
+ file = "/some/file"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @settings.stubs(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ @settings.value(:one, "env").should == "envval"
+ 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"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @settings.stubs(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ @settings.value(:one, "env").should == "envval"
+ end
+end
+
+describe Puppet::Util::Settings, " when parsing its configuration" do
+ before do
+ @settings = Puppet::Util::Settings.new
+ @settings.setdefaults :section, :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"]
+ end
+
+ it "should return values set in the configuration file" do
+ text = "[main]
+ one = fileval
+ "
+ file = "/some/file"
+ @settings.expects(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ @settings[:one].should == "fileval"
+ end
+
+ #484 - this should probably be in the regression area
+ it "should not throw an exception on unknown parameters" do
+ text = "[main]\nnosuchparam = mval\n"
+ file = "/some/file"
+ @settings.expects(:read_file).with(file).returns(text)
+ lambda { @settings.parse(file) }.should_not raise_error
+ end
+
+ it "should support an old parse method when per-executable configuration files still exist" do
+ # I'm not going to bother testing this method.
+ @settings.should respond_to(:old_parse)
+ end
+
+ it "should convert booleans in the configuration file into Ruby booleans" do
+ text = "[main]
+ one = true
+ two = false
+ "
+ file = "/some/file"
+ @settings.expects(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ @settings[:one].should == true
+ @settings[:two].should == false
+ end
+
+ it "should convert integers in the configuration file into Ruby Integers" do
+ text = "[main]
+ one = 65
+ "
+ file = "/some/file"
+ @settings.expects(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ @settings[:one].should == 65
+ end
+
+ it "should support specifying file all metadata (owner, group, mode) in the configuration file" do
+ @settings.setdefaults :section, :myfile => ["/myfile", "a"]
+
+ text = "[main]
+ myfile = /other/file {owner = luke, group = luke, mode = 644}
+ "
+ file = "/some/file"
+ @settings.expects(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ @settings[:myfile].should == "/other/file"
+ @settings.metadata(:myfile).should == {:owner => "luke", :group => "luke", :mode => "644"}
+ end
+
+ it "should support specifying file a single piece of metadata (owner, group, or mode) in the configuration file" do
+ @settings.setdefaults :section, :myfile => ["/myfile", "a"]
+
+ text = "[main]
+ myfile = /other/file {owner = luke}
+ "
+ file = "/some/file"
+ @settings.expects(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ @settings[:myfile].should == "/other/file"
+ @settings.metadata(:myfile).should == {:owner => "luke"}
+ end
+end
+
+describe Puppet::Util::Settings, " when reparsing its configuration" do
+ before do
+ @settings = Puppet::Util::Settings.new
+ @settings.setdefaults :section, :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"]
+ end
+
+ it "should replace in-memory values with on-file values" do
+ # Init the value
+ text = "[main]\none = disk-init\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/test/file")
+ @settings[:one] = "init"
+ @settings.file = file
+
+ # Now replace the value
+ text = "[main]\none = disk-replace\n"
+
+ # This is kinda ridiculous - the reason it parses twice is that
+ # it goes to parse again when we ask for the value, because the
+ # mock always says it should get reparsed.
+ @settings.expects(:read_file).with(file).returns(text).times(2)
+ @settings.reparse
+ @settings[:one].should == "disk-replace"
+ end
+
+ it "should retain parameters set by cli when configuration files are reparsed" do
+ @settings.handlearg("--one", "clival")
+
+ text = "[main]\none = on-disk\n"
+ file = mock 'file'
+ file.stubs(:file).returns("/test/file")
+ @settings.stubs(:read_file).with(file).returns(text)
+ @settings.parse(file)
+
+ @settings[:one].should == "clival"
+ end
+
+ it "should remove in-memory values that are no longer set in the file" do
+ # Init the value
+ text = "[main]\none = disk-init\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/test/file")
+ @settings.expects(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ @settings[:one].should == "disk-init"
+
+ # Now replace the value
+ text = "[main]\ntwo = disk-replace\n"
+ @settings.expects(:read_file).with(file).returns(text)
+ @settings.parse(file)
+ #@settings.reparse
+
+ # The originally-overridden value should be replaced with the default
+ @settings[:one].should == "ONE"
+
+ # and we should now have the new value in memory
+ @settings[:two].should == "disk-replace"
+ end
+end
+
+describe Puppet::Util::Settings, " when being used to manage the host machine" do
+ before do
+ @settings = Puppet::Util::Settings.new
+ @settings.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"]
+ @settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "luke", :group => "johnny", :mode => 0755}
+ @settings.setdefaults :files, :myfile => {:default => "/myfile", :desc => "a", :mode => 0755}
+ end
+
+ it "should provide a method that writes files with the correct modes" do
+ pending "Not converted from test/unit yet"
+ end
+
+ it "should provide a method that creates directories with the correct modes" do
+ Puppet::Util::SUIDManager.expects(:asuser).with("luke", "johnny").yields
+ Dir.expects(:mkdir).with("/otherdir", 0755)
+ @settings.mkdir(:otherdir)
+ end
+
+ it "should be able to create needed directories in a single section" do
+ Dir.expects(:mkdir).with("/maindir")
+ Dir.expects(:mkdir).with("/seconddir")
+ @settings.use(:main)
+ end
+
+ it "should be able to create needed directories in multiple sections" do
+ Dir.expects(:mkdir).with("/maindir")
+ Dir.expects(:mkdir).with("/otherdir", 0755)
+ Dir.expects(:mkdir).with("/seconddir")
+ @settings.use(:main, :other)
+ end
+
+ it "should provide a method to trigger enforcing of file modes on existing files and directories" do
+ pending "Not converted from test/unit yet"
+ end
+
+ it "should provide a method to convert the file mode enforcement into a Puppet manifest" do
+ pending "Not converted from test/unit yet"
+ end
+
+ it "should create files when configured to do so with the :create parameter"
+
+ it "should provide a method to convert the file mode enforcement into transportable resources" do
+ # Make it think we're root so it tries to manage user and group.
+ Puppet.features.stubs(:root?).returns(true)
+ File.stubs(:exist?).with("/myfile").returns(true)
+ trans = nil
+ trans = @settings.to_transportable
+ resources = []
+ trans.delve { |obj| resources << obj if obj.is_a? Puppet::TransObject }
+ %w{/maindir /seconddir /otherdir /myfile}.each do |path|
+ obj = resources.find { |r| r.type == "file" and r.name == path }
+ if path.include?("dir")
+ obj[:ensure].should == :directory
+ else
+ # Do not create the file, just manage mode
+ obj[:ensure].should be_nil
+ end
+ obj.should be_instance_of(Puppet::TransObject)
+ case path
+ when "/otherdir":
+ obj[:owner].should == "luke"
+ obj[:group].should == "johnny"
+ obj[:mode].should == 0755
+ when "/myfile":
+ obj[:mode].should == 0755
+ end
+ end
+ end
+
+ it "should not try to manage user or group when not running as root" do
+ Puppet.features.stubs(:root?).returns(false)
+ trans = nil
+ trans = @settings.to_transportable(:other)
+ trans.delve do |obj|
+ next unless obj.is_a?(Puppet::TransObject)
+ obj[:owner].should be_nil
+ obj[:group].should be_nil
+ end
+ end
+
+ it "should add needed users and groups to the manifest when asked" do
+ # This is how we enable user/group management
+ @settings.setdefaults :main, :mkusers => [true, "w"]
+ Puppet.features.stubs(:root?).returns(false)
+ trans = nil
+ trans = @settings.to_transportable(:other)
+ resources = []
+ trans.delve { |obj| resources << obj if obj.is_a? Puppet::TransObject and obj.type != "file" }
+
+ user = resources.find { |r| r.type == "user" }
+ user.should be_instance_of(Puppet::TransObject)
+ user.name.should == "luke"
+ user[:ensure].should == :present
+
+ # This should maybe be a separate test, but...
+ group = resources.find { |r| r.type == "group" }
+ group.should be_instance_of(Puppet::TransObject)
+ group.name.should == "johnny"
+ group[:ensure].should == :present
+ end
+
+ it "should ignore tags and schedules when creating files and directories"
+
+ it "should apply all resources in debug mode to reduce logging"
+
+ it "should not try to manage absent files" do
+ # Make it think we're root so it tries to manage user and group.
+ Puppet.features.stubs(:root?).returns(true)
+ trans = nil
+ trans = @settings.to_transportable
+ file = nil
+ trans.delve { |obj| file = obj if obj.name == "/myfile" }
+ file.should be_nil
+ end
+
+ it "should be able to turn the current configuration into a parseable manifest"
+
+ it "should convert octal numbers correctly when producing a manifest"
+
+ it "should be able to provide all of its parameters in a format compatible with GetOpt::Long" do
+ pending "Not converted from test/unit yet"
+ end
+
+ it "should not attempt to manage files within /dev" do
+ pending "Not converted from test/unit yet"
+ end
+
+ it "should not modify the stored state database when managing resources" do
+ Puppet::Util::Storage.expects(:store).never
+ Puppet::Util::Storage.expects(:load).never
+ Dir.expects(:mkdir).with("/maindir")
+ @settings.use(:main)
+ end
+
+ it "should convert all relative paths to fully-qualified paths (#795)" do
+ @settings[:myfile] = "unqualified"
+ dir = Dir.getwd
+ @settings[:myfile].should == File.join(dir, "unqualified")
+ end
+
+ it "should support a method for re-using all currently used sections" do
+ Dir.expects(:mkdir).with(@settings[:otherdir], 0755).times(2)
+ @settings.use(:other)
+ @settings.reuse
+ end
+end