summaryrefslogtreecommitdiffstats
path: root/spec/unit/resource
diff options
context:
space:
mode:
authorLuke Kanies <luke@reductivelabs.com>2010-01-07 17:23:31 -0800
committertest branch <puppet-dev@googlegroups.com>2010-02-17 06:50:53 -0800
commitd0389f4d16efbeccf47d6cd2f1b0854ccb1c88d5 (patch)
tree3a3060ac94be20b25742f5ec956e95c8ff3633d8 /spec/unit/resource
parent67ef78d9f231661d0fdd6260d470cf0d06f1bac2 (diff)
downloadpuppet-d0389f4d16efbeccf47d6cd2f1b0854ccb1c88d5.tar.gz
puppet-d0389f4d16efbeccf47d6cd2f1b0854ccb1c88d5.tar.xz
puppet-d0389f4d16efbeccf47d6cd2f1b0854ccb1c88d5.zip
Renaming Parser::ResourceType to Resource::Type
Basically, these classes (ResourceType and ResourceTypeCollection) don't really belong in Parser, so I'm moving them to the Resource namespace. This will be where anything RAL-related goes from now on, and as we migrate functionality out of Puppet::Type, it should go here. Signed-off-by: Luke Kanies <luke@reductivelabs.com>
Diffstat (limited to 'spec/unit/resource')
-rwxr-xr-xspec/unit/resource/type.rb533
-rw-r--r--spec/unit/resource/type_collection.rb340
-rw-r--r--spec/unit/resource/type_collection_helper.rb25
3 files changed, 898 insertions, 0 deletions
diff --git a/spec/unit/resource/type.rb b/spec/unit/resource/type.rb
new file mode 100755
index 000000000..14503b7cc
--- /dev/null
+++ b/spec/unit/resource/type.rb
@@ -0,0 +1,533 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/resource/type'
+
+describe Puppet::Resource::Type do
+ it "should have a 'name' attribute" do
+ Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo"
+ end
+
+ [:code, :doc, :line, :file, :code_collection].each do |attr|
+ it "should have a '#{attr}' attribute" do
+ type = Puppet::Resource::Type.new(:hostclass, "foo")
+ type.send(attr.to_s + "=", "yay")
+ type.send(attr).should == "yay"
+ end
+ end
+
+ describe "when a node" do
+ it "should allow a regex as its name" do
+ lambda { Puppet::Resource::Type.new(:node, /foo/) }.should_not raise_error
+ end
+
+ it "should allow a AST::HostName instance as its name" do
+ regex = Puppet::Parser::AST::Regex.new(:value => /foo/)
+ name = Puppet::Parser::AST::HostName.new(:value => regex)
+ lambda { Puppet::Resource::Type.new(:node, name) }.should_not raise_error
+ end
+
+ it "should match against the regexp in the AST::HostName when a HostName instance is provided" do
+ regex = Puppet::Parser::AST::Regex.new(:value => /\w/)
+ name = Puppet::Parser::AST::HostName.new(:value => regex)
+ node = Puppet::Resource::Type.new(:node, name)
+
+ node.match("foo").should be_true
+ end
+
+ it "should return the value of the hostname if provided a string-form AST::HostName instance as the name" do
+ name = Puppet::Parser::AST::HostName.new(:value => "foo")
+ node = Puppet::Resource::Type.new(:node, name)
+
+ node.name.should == "foo"
+ end
+
+ describe "and the name is a regex" do
+ it "should have a method that indicates that this is the case" do
+ Puppet::Resource::Type.new(:node, /w/).should be_name_is_regex
+ end
+
+ it "should set its namespace to ''" do
+ Puppet::Resource::Type.new(:node, /w/).namespace.should == ""
+ end
+
+ it "should return the regex converted to a string when asked for its name" do
+ Puppet::Resource::Type.new(:node, /ww/).name.should == "ww"
+ end
+
+ it "should downcase the regex when returning the name as a string" do
+ Puppet::Resource::Type.new(:node, /W/).name.should == "w"
+ end
+
+ it "should remove non-alpha characters when returning the name as a string" do
+ Puppet::Resource::Type.new(:node, /w*w/).name.should_not include("*")
+ end
+
+ it "should remove leading dots when returning the name as a string" do
+ Puppet::Resource::Type.new(:node, /.ww/).name.should_not =~ /^\./
+ end
+
+ it "should have a method for matching its regex name against a provided name" do
+ Puppet::Resource::Type.new(:node, /.ww/).should respond_to(:match)
+ end
+
+ it "should return true when its regex matches the provided name" do
+ Puppet::Resource::Type.new(:node, /\w/).match("foo").should be_true
+ end
+
+ it "should return false when its regex does not match the provided name" do
+ (!!Puppet::Resource::Type.new(:node, /\d/).match("foo")).should be_false
+ end
+
+ it "should return true when its name, as a string, is matched against an equal string" do
+ Puppet::Resource::Type.new(:node, "foo").match("foo").should be_true
+ end
+
+ it "should return false when its name is matched against an unequal string" do
+ Puppet::Resource::Type.new(:node, "foo").match("bar").should be_false
+ end
+
+ it "should match names insensitive to case" do
+ Puppet::Resource::Type.new(:node, "fOo").match("foO").should be_true
+ end
+ end
+
+ it "should return the name converted to a string when the name is not a regex" do
+ pending "Need to define ResourceTypeCollection behaviour first"
+ name = Puppet::Parser::AST::HostName.new(:value => "foo")
+ Puppet::Resource::Type.new(:node, name).name.should == "foo"
+ end
+
+ it "should return the name converted to a string when the name is a regex" do
+ pending "Need to define ResourceTypeCollection behaviour first"
+ name = Puppet::Parser::AST::HostName.new(:value => /regex/)
+ Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s
+ end
+
+ it "should mark any created scopes as a node scope" do
+ pending "Need to define ResourceTypeCollection behaviour first"
+ name = Puppet::Parser::AST::HostName.new(:value => /regex/)
+ Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s
+ end
+ end
+
+ describe "when initializing" do
+ it "should require a resource super type" do
+ Puppet::Resource::Type.new(:hostclass, "foo").type.should == :hostclass
+ end
+
+ it "should fail if provided an invalid resource super type" do
+ lambda { Puppet::Resource::Type.new(:nope, "foo") }.should raise_error(ArgumentError)
+ end
+
+ it "should set its name to the downcased, stringified provided name" do
+ Puppet::Resource::Type.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar"
+ end
+
+ it "should set its namespace to the downcased, stringified qualified portion of the name" do
+ Puppet::Resource::Type.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar"
+ end
+
+ %w{code line file doc}.each do |arg|
+ it "should set #{arg} if provided" do
+ type = Puppet::Resource::Type.new(:hostclass, "foo", arg.to_sym => "something")
+ type.send(arg).should == "something"
+ end
+ end
+
+ it "should set any provided arguments with the keys as symbols" do
+ type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"})
+ type.should be_validattr("foo")
+ type.should be_validattr("baz")
+ end
+
+ it "should set any provided arguments with they keys as strings" do
+ type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"})
+ type.should be_validattr(:foo)
+ type.should be_validattr(:baz)
+ end
+
+ it "should function if provided no arguments" do
+ type = Puppet::Resource::Type.new(:hostclass, "foo")
+ type.should_not be_validattr(:foo)
+ end
+ end
+
+ describe "when testing the validity of an attribute" do
+ it "should return true if the parameter was typed at initialization" do
+ Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_validattr("foo")
+ end
+
+ it "should return true if it is a metaparam" do
+ Puppet::Resource::Type.new(:hostclass, "foo").should be_validattr("require")
+ end
+
+ it "should return true if the parameter is named 'name'" do
+ Puppet::Resource::Type.new(:hostclass, "foo").should be_validattr("name")
+ end
+
+ it "should return false if it is not a metaparam and was not provided at initialization" do
+ Puppet::Resource::Type.new(:hostclass, "foo").should_not be_validattr("yayness")
+ end
+ end
+
+ describe "when creating a subscope" do
+ before do
+ @scope = stub 'scope', :newscope => nil
+ @resource = stub 'resource'
+ @type = Puppet::Resource::Type.new(:hostclass, "foo")
+ end
+
+ it "should return a new scope created with the provided scope as the parent" do
+ @scope.expects(:newscope).returns "foo"
+ @type.subscope(@scope, @resource).should == "foo"
+ end
+
+ it "should set the source as itself" do
+ @scope.expects(:newscope).with { |args| args[:source] == @type }
+ @type.subscope(@scope, @resource)
+ end
+
+ it "should set the scope's namespace to its namespace" do
+ @type.expects(:namespace).returns "yayness"
+ @scope.expects(:newscope).with { |args| args[:namespace] == "yayness" }
+ @type.subscope(@scope, @resource)
+ end
+
+ it "should set the scope's resource to the provided resource" do
+ @scope.expects(:newscope).with { |args| args[:resource] == @resource }
+ @type.subscope(@scope, @resource)
+ end
+ end
+
+ describe "when setting its parameters in the scope" do
+ before do
+ @scope = stub 'scope', :newscope => nil, :setvar => nil
+ @resource = stub 'resource', :title => "yay", :name => "yea", :ref => "Foo[bar]"
+ @type = Puppet::Resource::Type.new(:hostclass, "foo")
+ end
+
+ it "should set each of the resource's parameters as variables in the scope" do
+ @type.set_arguments :foo => nil, :boo => nil
+ @resource.expects(:to_hash).returns(:foo => "bar", :boo => "baz")
+
+ @scope.expects(:setvar).with("foo", "bar")
+ @scope.expects(:setvar).with("boo", "baz")
+ @scope.stubs(:class_set).with("foo",@scope)
+
+ @type.set_resource_parameters(@resource, @scope)
+ end
+
+ it "should set the variables as strings" do
+ @type.set_arguments :foo => nil
+ @resource.expects(:to_hash).returns(:foo => "bar")
+ @scope.expects(:setvar).with("foo", "bar")
+ @scope.stubs(:class_set).with("foo",@scope)
+
+ @type.set_resource_parameters(@resource, @scope)
+ end
+
+ it "should fail if any of the resource's parameters are not valid attributes" do
+ @type.set_arguments :foo => nil
+ @resource.expects(:to_hash).returns(:boo => "baz")
+
+ lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError)
+ end
+
+ it "should evaluate and set its default values as variables for parameters not provided by the resource" do
+ @type.set_arguments :foo => stub("value", :safeevaluate => "something")
+ @resource.expects(:to_hash).returns({})
+
+ @scope.expects(:setvar).with("foo", "something")
+ @scope.stubs(:class_set).with("foo",@scope)
+
+ @type.set_resource_parameters(@resource, @scope)
+ end
+
+ it "should fail if the resource does not provide a value for a required argument" do
+ @type.set_arguments :foo => nil
+ @resource.expects(:to_hash).returns({})
+
+ lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError)
+ end
+
+ it "should set the resource's title as a variable if not otherwise provided" do
+ @resource.expects(:to_hash).returns({})
+
+ @resource.expects(:title).returns 'teetle'
+ @scope.expects(:setvar).with("title", "teetle")
+ @scope.stubs(:class_set).with("foo",@scope)
+
+ @type.set_resource_parameters(@resource, @scope)
+ end
+
+ it "should set the resource's name as a variable if not otherwise provided" do
+ @resource.expects(:to_hash).returns({})
+
+ @resource.expects(:name).returns 'nombre'
+ @scope.expects(:setvar).with("name", "nombre")
+ @scope.stubs(:class_set).with("foo",@scope)
+
+ @type.set_resource_parameters(@resource, @scope)
+ end
+ end
+
+ describe "when describing and managing parent classes" do
+ before do
+ @code = Puppet::Resource::TypeCollection.new("env")
+ @parent = Puppet::Resource::Type.new(:hostclass, "bar")
+ @code.add @parent
+
+ @child = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar")
+ @code.add @child
+ end
+
+ it "should be able to define a parent" do
+ Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar")
+ end
+
+ it "should use the code collection to find the parent resource type" do
+ @child.parent_type.should equal(@parent)
+ end
+
+ it "should be able to find parent nodes" do
+ parent = Puppet::Resource::Type.new(:node, "bar")
+ @code.add parent
+ child = Puppet::Resource::Type.new(:node, "foo", :parent => "bar")
+ @code.add child
+
+ child.parent_type.should equal(parent)
+ end
+
+ it "should cache a reference to the parent type" do
+ @code.expects(:hostclass).once.with("bar").returns @parent
+ @child.parent_type
+ @child.parent_type
+ end
+
+ it "should correctly state when it is another type's child" do
+ @child.should be_child_of(@parent)
+ end
+
+ it "should be considered the child of a parent's parent" do
+ @grandchild = Puppet::Resource::Type.new(:hostclass, "baz", :parent => "foo")
+ @code.add @grandchild
+
+ @grandchild.should be_child_of(@parent)
+ end
+
+ it "should correctly state when it is not another type's child" do
+ @notchild = Puppet::Resource::Type.new(:hostclass, "baz")
+ @code.add @notchild
+
+ @notchild.should_not be_child_of(@parent)
+ end
+ end
+
+ describe "when evaluating its code" do
+ before do
+ @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode"))
+ @scope = Puppet::Parser::Scope.new :compiler => @compiler
+ @resource = stub 'resource', :title => "yay", :name => "yea", :ref => "Foo[bar]", :scope => @scope
+ @type = Puppet::Resource::Type.new(:hostclass, "foo")
+ @type.stubs(:set_resource_parameters)
+ end
+
+ it "should set all of its parameters in a subscope" do
+ subscope = stub 'subscope', :compiler => @compiler
+ @type.expects(:subscope).with(@scope, @resource).returns subscope
+ @type.expects(:set_resource_parameters).with(@resource, subscope)
+
+ @type.evaluate_code(@resource)
+ end
+
+ it "should store the class scope" do
+ subscope = stub 'subscope', :compiler => @compiler
+ @type.expects(:subscope).with(@scope, @resource).returns subscope
+
+ @type.evaluate_code(@resource)
+ @compiler.class_scope(@type).should equal(subscope)
+ end
+
+ it "should evaluate the code if any is provided" do
+ code = stub 'code'
+ @type.stubs(:code).returns code
+ @type.stubs(:subscope).returns stub("subscope", :compiler => @compiler)
+ code.expects(:safeevaluate).with @type.subscope
+
+ @type.evaluate_code(@resource)
+ end
+
+ it "should noop if there is no code" do
+ @type.expects(:code).returns nil
+
+ @type.evaluate_code(@resource)
+ end
+ end
+
+ describe "when creating a resource" do
+ before do
+ @node = Puppet::Node.new("foo")
+ @compiler = Puppet::Parser::Compiler.new(@node)
+ @scope = Puppet::Parser::Scope.new(:compiler => @compiler)
+
+ @top = Puppet::Resource::Type.new :hostclass, "top"
+ @middle = Puppet::Resource::Type.new :hostclass, "middle", :parent => "top"
+
+ @code = Puppet::Resource::TypeCollection.new("env")
+ @code.add @top
+ @code.add @middle
+ end
+
+ it "should create a resource instance" do
+ @top.mk_plain_resource(@scope).should be_instance_of(Puppet::Parser::Resource)
+ end
+
+ it "should set its resource type to 'class' when it is a hostclass" do
+ Puppet::Resource::Type.new(:hostclass, "top").mk_plain_resource(@scope).type.should == "Class"
+ end
+
+ it "should set its resource type to 'node' when it is a node" do
+ Puppet::Resource::Type.new(:node, "top").mk_plain_resource(@scope).type.should == "Node"
+ end
+
+ it "should fail when it is a definition" do
+ lambda { Puppet::Resource::Type.new(:definition, "top").mk_plain_resource(@scope) }.should raise_error(ArgumentError)
+ end
+
+ it "should add the created resource to the scope's catalog" do
+ @top.mk_plain_resource(@scope)
+
+ @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
+ end
+
+ it "should evaluate the parent class if one exists" do
+ @middle.mk_plain_resource(@scope)
+
+ @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
+ end
+
+ it "should fail to evaluate if a parent class is defined but cannot be found" do
+ othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay"
+ @code.add othertop
+ lambda { othertop.mk_plain_resource(@scope) }.should raise_error(Puppet::ParseError)
+ end
+
+ it "should not create a new resource if one already exists" do
+ @compiler.catalog.expects(:resource).with(:class, "top").returns("something")
+ @compiler.catalog.expects(:add_resource).never
+ @top.mk_plain_resource(@scope)
+ end
+
+ it "should return the existing resource when not creating a new one" do
+ @compiler.catalog.expects(:resource).with(:class, "top").returns("something")
+ @compiler.catalog.expects(:add_resource).never
+ @top.mk_plain_resource(@scope).should == "something"
+ end
+
+ it "should not create a new parent resource if one already exists and it has a parent class" do
+ @top.mk_plain_resource(@scope)
+
+ top_resource = @compiler.catalog.resource(:class, "top")
+
+ @middle.mk_plain_resource(@scope)
+
+ @compiler.catalog.resource(:class, "top").should equal(top_resource)
+ end
+
+ # #795 - tag before evaluation.
+ it "should tag the catalog with the resource tags when it is evaluated" do
+ @middle.mk_plain_resource(@scope)
+
+ @compiler.catalog.should be_tagged("middle")
+ end
+
+ it "should tag the catalog with the parent class tags when it is evaluated" do
+ @middle.mk_plain_resource(@scope)
+
+ @compiler.catalog.should be_tagged("top")
+ end
+ end
+
+ describe "when merging code from another instance" do
+ def code(str)
+ Puppet::Parser::AST::Leaf.new :value => str
+ end
+
+ it "should fail unless it is a class" do
+ lambda { Puppet::Resource::Type.new(:node, "bar").merge("foo") }.should raise_error(ArgumentError)
+ end
+
+ it "should fail unless the source instance is a class" do
+ dest = Puppet::Resource::Type.new(:hostclass, "bar")
+ source = Puppet::Resource::Type.new(:node, "foo")
+ lambda { dest.merge(source) }.should raise_error(ArgumentError)
+ end
+
+ it "should fail if both classes have different parent classes" do
+ code = Puppet::Resource::TypeCollection.new("env")
+ {"a" => "b", "c" => "d"}.each do |parent, child|
+ code.add Puppet::Resource::Type.new(:hostclass, parent)
+ code.add Puppet::Resource::Type.new(:hostclass, child, :parent => parent)
+ end
+ lambda { code.hostclass("b").merge(code.hostclass("d")) }.should raise_error(ArgumentError)
+ end
+
+ it "should copy the other class's parent if it has not parent" do
+ dest = Puppet::Resource::Type.new(:hostclass, "bar")
+
+ parent = Puppet::Resource::Type.new(:hostclass, "parent")
+ source = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "parent")
+ dest.merge(source)
+
+ dest.parent.should == "parent"
+ end
+
+ it "should copy the other class's documentation as its docs if it has no docs" do
+ dest = Puppet::Resource::Type.new(:hostclass, "bar")
+ source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness")
+ dest.merge(source)
+
+ dest.doc.should == "yayness"
+ end
+
+ it "should append the other class's docs to its docs if it has any" do
+ dest = Puppet::Resource::Type.new(:hostclass, "bar", :doc => "fooness")
+ source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness")
+ dest.merge(source)
+
+ dest.doc.should == "foonessyayness"
+ end
+
+ it "should turn its code into an ASTArray if necessary" do
+ dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => code("foo"))
+ source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar"))
+
+ dest.merge(source)
+
+ dest.code.should be_instance_of(Puppet::Parser::AST::ASTArray)
+ end
+
+ it "should set the other class's code as its code if it has none" do
+ dest = Puppet::Resource::Type.new(:hostclass, "bar")
+ source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar"))
+
+ dest.merge(source)
+
+ dest.code.value.should == "bar"
+ end
+
+ it "should append the other class's code to its code if it has any" do
+ dcode = Puppet::Parser::AST::ASTArray.new :children => [code("dest")]
+ dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => dcode)
+
+ scode = Puppet::Parser::AST::ASTArray.new :children => [code("source")]
+ source = Puppet::Resource::Type.new(:hostclass, "foo", :code => scode)
+
+ dest.merge(source)
+
+ dest.code.children.collect { |l| l.value }.should == %w{dest source}
+ end
+ end
+end
diff --git a/spec/unit/resource/type_collection.rb b/spec/unit/resource/type_collection.rb
new file mode 100644
index 000000000..2fc364d6b
--- /dev/null
+++ b/spec/unit/resource/type_collection.rb
@@ -0,0 +1,340 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/resource/type_collection'
+require 'puppet/resource/type'
+
+describe Puppet::Resource::TypeCollection do
+ before do
+ @instance = Puppet::Resource::Type.new(:hostclass, "foo")
+ @code = Puppet::Resource::TypeCollection.new("env")
+ end
+
+ it "should require an environment at initialization" do
+ env = Puppet::Node::Environment.new("testing")
+ Puppet::Resource::TypeCollection.new(env).environment.should equal(env)
+ end
+
+ it "should convert the environment into an environment instance if a string is provided" do
+ env = Puppet::Node::Environment.new("testing")
+ Puppet::Resource::TypeCollection.new("testing").environment.should equal(env)
+ end
+
+ it "should be able to add a resource type" do
+ Puppet::Resource::TypeCollection.new("env").should respond_to(:add)
+ end
+
+ it "should consider '<<' to be an alias to 'add' but should return self" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ loader.expects(:add).with "foo"
+ loader.expects(:add).with "bar"
+ loader << "foo" << "bar"
+ end
+
+ it "should set itself as the code collection for added resource types" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+
+ node = Puppet::Resource::Type.new(:node, "foo")
+
+ @code.add(node)
+ @code.node("foo").should equal(node)
+
+ node.code_collection.should equal(@code)
+ end
+
+ it "should store node resource types as nodes" do
+ node = Puppet::Resource::Type.new(:node, "foo")
+
+ @code.add(node)
+ @code.node("foo").should equal(node)
+ end
+
+ it "should store hostclasses as hostclasses" do
+ klass = Puppet::Resource::Type.new(:hostclass, "foo")
+
+ @code.add(klass)
+ @code.hostclass("foo").should equal(klass)
+ end
+
+ it "should store definitions as definitions" do
+ define = Puppet::Resource::Type.new(:definition, "foo")
+
+ @code.add(define)
+ @code.definition("foo").should equal(define)
+ end
+
+ %w{hostclass node definition}.each do |data|
+ it "should have a method for adding a #{data}" do
+ Puppet::Resource::TypeCollection.new("env").should respond_to("add_" + data)
+ end
+
+ it "should use the name of the instance to add it" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ loader.send("add_#{data}", @instance)
+ loader.send(data, @instance.name).should equal(@instance)
+ end
+
+ it "should fail to add a #{data} when one already exists" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ loader.add @instance
+ lambda { loader.add(@instance) }.should raise_error(Puppet::ParseError)
+ end
+
+ it "should return the added #{data}" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+
+ loader.add(@instance).should equal(@instance)
+ end
+
+ it "should be able to retrieve #{data} by name" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(data, "bar")
+ loader.add instance
+ loader.send(data, "bar").should equal(instance)
+ end
+
+ it "should retrieve #{data} insensitive to case" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(data, "Bar")
+ loader.add instance
+ loader.send(data, "bAr").should equal(instance)
+ end
+
+ it "should return nil when asked for a #{data} that has not been added" do
+ Puppet::Resource::TypeCollection.new("env").send(data, "foo").should be_nil
+ end
+
+ it "should be able to retrieve all #{data}s" do
+ plurals = { "hostclass" => "hostclasses", "node" => "nodes", "definition" => "definitions" }
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(data, "foo")
+ loader.add instance
+ loader.send(plurals[data]).should == { "foo" => instance }
+ end
+ end
+
+ describe "when finding a qualified instance" do
+ it "should return any found instance if the instance name is fully qualified" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(:hostclass, "foo::bar")
+ loader.add instance
+ loader.find("namespace", "::foo::bar", :hostclass).should equal(instance)
+ end
+
+ it "should return nil if the instance name is fully qualified and no such instance exists" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ loader.find("namespace", "::foo::bar", :hostclass).should be_nil
+ end
+
+ it "should return the partially qualified object if it exists in the provided namespace" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz")
+ loader.add instance
+ loader.find("foo", "bar::baz", :hostclass).should equal(instance)
+ end
+
+ it "should return the unqualified object if it exists in the provided namespace" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(:hostclass, "foo::bar")
+ loader.add instance
+ loader.find("foo", "bar", :hostclass).should equal(instance)
+ end
+
+ it "should return the unqualified object if it exists in the parent namespace" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(:hostclass, "foo::bar")
+ loader.add instance
+ loader.find("foo::bar::baz", "bar", :hostclass).should equal(instance)
+ end
+
+ it "should should return the partially qualified object if it exists in the parent namespace" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz")
+ loader.add instance
+ loader.find("foo::bar", "bar::baz", :hostclass).should equal(instance)
+ end
+
+ it "should return the qualified object if it exists in the root namespace" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz")
+ loader.add instance
+ loader.find("foo::bar", "foo::bar::baz", :hostclass).should equal(instance)
+ end
+
+ it "should return nil if the object cannot be found" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz")
+ loader.add instance
+ loader.find("foo::bar", "eh", :hostclass).should be_nil
+ end
+ end
+
+ it "should use the generic 'find' method with an empty namespace to find nodes" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ loader.expects(:find).with("", "bar", :node)
+ loader.find_node("bar")
+ end
+
+ it "should use the generic 'find' method to find hostclasses" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ loader.expects(:find).with("foo", "bar", :hostclass)
+ loader.find_hostclass("foo", "bar")
+ end
+
+ it "should use the generic 'find' method to find definitions" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ loader.expects(:find).with("foo", "bar", :definition)
+ loader.find_definition("foo", "bar")
+ end
+
+ it "should indicate whether any nodes are defined" do
+ loader = Puppet::Resource::TypeCollection.new("env")
+ loader.add_node(Puppet::Resource::Type.new(:node, "foo"))
+ loader.should be_nodes
+ end
+
+ it "should indicate whether no nodes are defined" do
+ Puppet::Resource::TypeCollection.new("env").should_not be_nodes
+ end
+
+ describe "when finding nodes" do
+ before :each do
+ @loader = Puppet::Resource::TypeCollection.new("env")
+ end
+
+ it "should return any node whose name exactly matches the provided node name" do
+ node = Puppet::Resource::Type.new(:node, "foo")
+ @loader << node
+
+ @loader.node("foo").should equal(node)
+ end
+
+ it "should return the first regex node whose regex matches the provided node name" do
+ node1 = Puppet::Resource::Type.new(:node, /\w/)
+ node2 = Puppet::Resource::Type.new(:node, /\d/)
+ @loader << node1 << node2
+
+ @loader.node("foo10").should equal(node1)
+ end
+
+ it "should preferentially return a node whose name is string-equal over returning a node whose regex matches a provided name" do
+ node1 = Puppet::Resource::Type.new(:node, /\w/)
+ node2 = Puppet::Resource::Type.new(:node, "foo")
+ @loader << node1 << node2
+
+ @loader.node("foo").should equal(node2)
+ end
+ end
+
+ describe "when managing files" do
+ before do
+ @loader = Puppet::Resource::TypeCollection.new("env")
+ Puppet::Util::LoadedFile.stubs(:new).returns stub("watched_file")
+ end
+
+ it "should have a method for specifying a file should be watched" do
+ @loader.should respond_to(:watch_file)
+ end
+
+ it "should have a method for determining if a file is being watched" do
+ @loader.watch_file("/foo/bar")
+ @loader.should be_watching_file("/foo/bar")
+ end
+
+ it "should use LoadedFile to watch files" do
+ Puppet::Util::LoadedFile.expects(:new).with("/foo/bar").returns stub("watched_file")
+ @loader.watch_file("/foo/bar")
+ end
+
+ it "should be considered stale if any files have changed" do
+ file1 = stub 'file1', :changed? => false
+ file2 = stub 'file2', :changed? => true
+ Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2)
+ @loader.watch_file("/foo/bar")
+ @loader.watch_file("/other/bar")
+
+ @loader.should be_stale
+ end
+
+ it "should not be considered stable if no files have changed" do
+ file1 = stub 'file1', :changed? => false
+ file2 = stub 'file2', :changed? => false
+ Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2)
+ @loader.watch_file("/foo/bar")
+ @loader.watch_file("/other/bar")
+
+ @loader.should_not be_stale
+ end
+ end
+
+ describe "when performing initial import" do
+ before do
+ @parser = stub 'parser', :file= => nil, :string => nil, :parse => nil
+ Puppet::Parser::Parser.stubs(:new).returns @parser
+ @code = Puppet::Resource::TypeCollection.new("env")
+ end
+
+ it "should create a new parser instance" do
+ Puppet::Parser::Parser.expects(:new).returns @parser
+ @code.perform_initial_import
+ end
+
+ it "should set the parser's string to the 'code' setting and parse if code is available" do
+ Puppet.settings[:code] = "my code"
+ @parser.expects(:string=).with "my code"
+ @parser.expects(:parse)
+ @code.perform_initial_import
+ end
+
+ it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do
+ File.expects(:exist?).with("/my/file").returns true
+ Puppet.settings[:manifest] = "/my/file"
+ @parser.expects(:file=).with "/my/file"
+ @parser.expects(:parse)
+ @code.perform_initial_import
+ end
+
+ it "should not attempt to load a manifest if none is present" do
+ File.expects(:exist?).with("/my/file").returns false
+ Puppet.settings[:manifest] = "/my/file"
+ @parser.expects(:file=).never
+ @parser.expects(:parse).never
+ @code.perform_initial_import
+ end
+
+ it "should fail helpfully if there is an error importing" do
+ File.stubs(:exist?).returns true
+ @parser.expects(:parse).raises ArgumentError
+ lambda { @code.perform_initial_import }.should raise_error(Puppet::Error)
+ end
+ end
+
+ describe "when determining the configuration version" do
+ before do
+ @code = Puppet::Resource::TypeCollection.new("env")
+ end
+
+ it "should default to the current time" do
+ time = Time.now
+
+ Time.stubs(:now).returns time
+ @code.version.should == time.to_i
+ end
+
+ it "should use the output of the environment's config_version setting if one is provided" do
+ @code.environment.stubs(:[]).with(:config_version).returns("/my/foo")
+
+ Puppet::Util.expects(:execute).with(["/my/foo"]).returns "output\n"
+ @code.version.should == "output"
+ end
+
+ it "should raise a puppet parser error if executing config_version fails" do
+ @code.environment.stubs(:[]).with(:config_version).returns("test")
+ Puppet::Util.expects(:execute).raises(Puppet::ExecutionFailure.new("msg"))
+
+ lambda { @code.version }.should raise_error(Puppet::ParseError)
+ end
+
+ end
+end
diff --git a/spec/unit/resource/type_collection_helper.rb b/spec/unit/resource/type_collection_helper.rb
new file mode 100644
index 000000000..e390ff952
--- /dev/null
+++ b/spec/unit/resource/type_collection_helper.rb
@@ -0,0 +1,25 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/resource/type_collection_helper'
+
+class RTCHelperTester
+ include Puppet::Resource::TypeCollectionHelper
+end
+
+describe Puppet::Resource::TypeCollectionHelper do
+ before do
+ @helper = RTCHelperTester.new
+ end
+
+ it "should use its current environment to retrieve the known resource type collection" do
+ env = stub 'environment'
+ @helper.expects(:environment).returns env
+
+ rtc = stub 'known_resource_types'
+ env.expects(:known_resource_types).returns rtc
+
+ @helper.known_resource_types.should equal(rtc)
+ end
+end