diff options
| author | Luke Kanies <luke@reductivelabs.com> | 2010-01-07 17:23:31 -0800 |
|---|---|---|
| committer | test branch <puppet-dev@googlegroups.com> | 2010-02-17 06:50:53 -0800 |
| commit | d0389f4d16efbeccf47d6cd2f1b0854ccb1c88d5 (patch) | |
| tree | 3a3060ac94be20b25742f5ec956e95c8ff3633d8 /spec/unit/resource | |
| parent | 67ef78d9f231661d0fdd6260d470cf0d06f1bac2 (diff) | |
| download | puppet-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-x | spec/unit/resource/type.rb | 533 | ||||
| -rw-r--r-- | spec/unit/resource/type_collection.rb | 340 | ||||
| -rw-r--r-- | spec/unit/resource/type_collection_helper.rb | 25 |
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 |
