diff options
Diffstat (limited to 'test')
| -rwxr-xr-x | test/language/configuration.rb | 627 |
1 files changed, 606 insertions, 21 deletions
diff --git a/test/language/configuration.rb b/test/language/configuration.rb index 4cbba8063..aafdf8356 100755 --- a/test/language/configuration.rb +++ b/test/language/configuration.rb @@ -14,35 +14,40 @@ class TestConfiguration < Test::Unit::TestCase Config = Puppet::Parser::Configuration Scope = Puppet::Parser::Scope + Node = Puppet::Network::Handler.handler(:node) + SimpleNode = Node::SimpleNode - def mkconfig - Config.new(:host => "foo", :interpreter => "interp") + def mknode(name = "foo") + @node = SimpleNode.new(name) end - def test_initialize - # Make sure we get an error if we don't send an interpreter - assert_raise(ArgumentError, "Did not fail when missing host") do - Config.new(:interpreter => "yay" ) - end - assert_raise(ArgumentError, "Did not fail when missing interp") do - Config.new(:host => "foo") - end + def mkparser + # This should mock an interpreter + @parser = mock 'parser' + end + + def mkconfig(options = {}) + @config = Config.new(mknode, mkparser, options) + end - # Now check the defaults + def test_initialize config = nil assert_nothing_raised("Could not init config with all required options") do - config = Config.new(:host => "foo", :interpreter => "interp") + config = Config.new("foo", "parser") end - assert_equal("foo", config.host, "Did not set host correctly") - assert_equal("interp", config.interpreter, "Did not set interpreter correctly") - assert_equal({}, config.facts, "Did not set default facts") + assert_equal("foo", config.node, "Did not set node correctly") + assert_equal("parser", config.parser, "Did not set parser correctly") - # Now make a new one with facts, to make sure the facts get set appropriately - assert_nothing_raised("Could not init config with all required options") do - config = Config.new(:host => "foo", :interpreter => "interp", :facts => {"a" => "b"}) + # We're not testing here whether we call initvars, because it's too difficult to + # mock. + + # Now try it with some options + assert_nothing_raised("Could not init config with extra options") do + config = Config.new("foo", "parser", :ast_nodes => false) end - assert_equal({"a" => "b"}, config.facts, "Did not set facts") + + assert_equal(false, config.ast_nodes?, "Did not set ast_nodes? correctly") end def test_initvars @@ -50,14 +55,19 @@ class TestConfiguration < Test::Unit::TestCase [:class_scopes, :resource_table, :exported_resources, :resource_overrides].each do |table| assert_instance_of(Hash, config.send(:instance_variable_get, "@#{table}"), "Did not set %s table correctly" % table) end + assert_instance_of(Scope, config.topscope, "Did not create a topscope") + graph = config.instance_variable_get("@scope_graph") + assert_instance_of(GRATR::Digraph, graph, "Did not create scope graph") + assert(graph.vertex?(config.topscope), "Did not add top scope as a vertex in the graph") end # Make sure we store and can retrieve references to classes and their scopes. def test_class_set_and_class_scope - klass = Object.new + klass = mock 'ast_class' klass.expects(:classname).returns("myname") config = mkconfig + config.expects(:tag).with("myname") assert_nothing_raised("Could not set class") do config.class_set "myname", "myscope" @@ -92,7 +102,7 @@ class TestConfiguration < Test::Unit::TestCase # Make sure we create a graph of scopes. def test_newscope config = mkconfig - graph = config.instance_variable_get("@graph") + graph = config.instance_variable_get("@scope_graph") assert_instance_of(Scope, config.topscope, "Did not create top scope") assert_instance_of(GRATR::Digraph, graph, "Did not create graph") @@ -128,4 +138,579 @@ class TestConfiguration < Test::Unit::TestCase assert_equal([another.object_id, subscope.object_id, config.topscope.object_id], another.scope_path.collect { |p| p.object_id }, "Did not get correct scope path") end + + # The heart of the action. + def test_compile + config = mkconfig + [:set_node_parameters, :evaluate_main, :evaluate_ast_nodes, :evaluate_classes, :evaluate_generators, :fail_on_unevaluated, :finish].each do |method| + config.expects(method) + end + config.expects(:extract).returns(:config) + assert_equal(:config, config.compile, "Did not return the results of the extraction") + end + + # Test setting the node's parameters into the top scope. + def test_set_node_parameters + config = mkconfig + @node.parameters = {"a" => "b", "c" => "d"} + scope = config.topscope + @node.parameters.each do |param, value| + scope.expects(:setvar).with(param, value) + end + + assert_nothing_raised("Could not call 'set_node_parameters'") do + config.send(:set_node_parameters) + end + end + + # Test that we can evaluate the main class, which is the one named "" in namespace + # "". + def test_evaluate_main + config = mkconfig + main = mock 'main_class' + config.topscope.expects(:source=).with(main) + main.expects(:safeevaluate).with(:scope => config.topscope, :nosubscope => true) + @parser.expects(:findclass).with("", "").returns(main) + + assert_nothing_raised("Could not call evaluate_main") do + config.send(:evaluate_main) + end + end + + # Make sure we either don't look for nodes, or that we find and evaluate the right object. + def test_evaluate_ast_node + # First try it with ast_nodes disabled + config = mkconfig :ast_nodes => false + config.expects(:ast_nodes?).returns(false) + config.parser.expects(:nodes).never + + assert_nothing_raised("Could not call evaluate_ast_node when ast nodes are disabled") do + config.send(:evaluate_ast_node) + end + + # Now try it with them enabled, but no node found. + nodes = mock 'node_hash' + config = mkconfig :ast_nodes => true + config.expects(:ast_nodes?).returns(true) + config.parser.expects(:nodes).returns(nodes).times(4) + + # Set some names for our test + @node.names = %w{a b c} + nodes.expects(:[]).with("a").returns(nil) + nodes.expects(:[]).with("b").returns(nil) + nodes.expects(:[]).with("c").returns(nil) + + # It should check this last, of course. + nodes.expects(:[]).with("default").returns(nil) + + # And make sure the lack of a node throws an exception + assert_raise(Puppet::ParseError, "Did not fail when we couldn't find an ast node") do + config.send(:evaluate_ast_node) + end + + # Finally, make sure it works dandily when we have a node + nodes = mock 'hash' + config = mkconfig :ast_nodes => true + config.expects(:ast_nodes?).returns(true) + config.parser.expects(:nodes).returns(nodes).times(3) + + node = mock 'node' + node.expects(:safeevaluate).with(:scope => config.topscope) + # Set some names for our test + @node.names = %w{a b c} + nodes.expects(:[]).with("a").returns(nil) + nodes.expects(:[]).with("b").returns(nil) + nodes.expects(:[]).with("c").returns(node) + nodes.expects(:[]).with("default").never + + # And make sure the lack of a node throws an exception + assert_nothing_raised("Failed when a node was found") do + config.send(:evaluate_ast_node) + end + + # Lastly, check when we actually find the default. + nodes = mock 'hash' + config = mkconfig :ast_nodes => true + config.expects(:ast_nodes?).returns(true) + config.parser.expects(:nodes).returns(nodes).times(4) + + node = mock 'node' + node.expects(:safeevaluate).with(:scope => config.topscope) + # Set some names for our test + @node.names = %w{a b c} + nodes.expects(:[]).with("a").returns(nil) + nodes.expects(:[]).with("b").returns(nil) + nodes.expects(:[]).with("c").returns(nil) + nodes.expects(:[]).with("default").returns(node) + + # And make sure the lack of a node throws an exception + assert_nothing_raised("Failed when a node was found") do + config.send(:evaluate_ast_node) + end + end + + # Make sure our config object handles tags appropriately. + def test_tags + config = mkconfig + config.send(:tag, "one") + assert_equal(%w{one}, config.send(:tags), "Did not add tag") + + config.send(:tag, "two", "three") + assert_equal(%w{one two three}, config.send(:tags), "Did not add new tags") + + config.send(:tag, "two") + assert_equal(%w{one two three}, config.send(:tags), "Allowed duplicate tag") + end + + def test_evaluate_classes + config = mkconfig + classes = { + "one" => mock('class one'), + "three" => mock('class three') + } + + classes.each do |name, obj| + config.parser.expects(:findclass).with("", name).returns(obj) + obj.expects(:safeevaluate).with(:scope => config.topscope) + end + %w{two four}.each do |name| + config.parser.expects(:findclass).with("", name).returns(nil) + end + + config.expects(:tag).with("two") + config.expects(:tag).with("four") + + @node.classes = %w{one two three four} + assert_nothing_raised("could not call evaluate_classes") do + config.send(:evaluate_classes) + end + end + + def test_evaluate_collections + config = mkconfig + + colls = [] + + # Make sure we return false when there's nothing there. + assert(! config.send(:evaluate_collections), "Returned true when there were no collections") + + # And when the collections fail to evaluate. + colls << mock("coll1-false") + colls << mock("coll2-false") + colls.each { |c| c.expects(:evaluate).returns(false) } + + config.instance_variable_set("@collections", colls) + assert(! config.send(:evaluate_collections), "Returned true when collections both evaluated nothing") + + # Now have one of the colls evaluate + colls.clear + colls << mock("coll1-one-true") + colls << mock("coll2-one-true") + colls[0].expects(:evaluate).returns(true) + colls[1].expects(:evaluate).returns(false) + assert(config.send(:evaluate_collections), "Did not return true when one collection evaluated true") + + # And have them both eval true + colls.clear + colls << mock("coll1-both-true") + colls << mock("coll2-both-true") + colls[0].expects(:evaluate).returns(true) + colls[1].expects(:evaluate).returns(true) + assert(config.send(:evaluate_collections), "Did not return true when both collections evaluated true") + end + + def test_unevaluated_resources + config = mkconfig + resources = {} + config.instance_variable_set("@resource_table", resources) + + # First test it when the table is empty + assert_nil(config.send(:unevaluated_resources), "Somehow found unevaluated resources in an empty table") + + # Then add a builtin resources + resources["one"] = mock("builtin only") + resources["one"].expects(:builtin?).returns(true) + assert_nil(config.send(:unevaluated_resources), "Considered a builtin resource unevaluated") + + # And do both builtin and non-builtin but already evaluated + resources.clear + resources["one"] = mock("builtin (with eval)") + resources["one"].expects(:builtin?).returns(true) + resources["two"] = mock("evaled (with builtin)") + resources["two"].expects(:builtin?).returns(false) + resources["two"].expects(:evaluated?).returns(true) + assert_nil(config.send(:unevaluated_resources), "Considered either a builtin or evaluated resource unevaluated") + + # Now a single unevaluated resource. + resources.clear + resources["one"] = mock("unevaluated") + resources["one"].expects(:builtin?).returns(false) + resources["one"].expects(:evaluated?).returns(false) + assert_equal([resources["one"]], config.send(:unevaluated_resources), "Did not find unevaluated resource") + + # With two uneval'ed resources, and an eval'ed one thrown in + resources.clear + resources["one"] = mock("unevaluated one") + resources["one"].expects(:builtin?).returns(false) + resources["one"].expects(:evaluated?).returns(false) + resources["two"] = mock("unevaluated two") + resources["two"].expects(:builtin?).returns(false) + resources["two"].expects(:evaluated?).returns(false) + resources["three"] = mock("evaluated") + resources["three"].expects(:builtin?).returns(false) + resources["three"].expects(:evaluated?).returns(true) + + result = config.send(:unevaluated_resources) + %w{one two}.each do |name| + assert(result.include?(resources[name]), "Did not find %s in the unevaluated list" % name) + end + end + + def test_evaluate_definitions + # First try the case where there's nothing to return + config = mkconfig + config.expects(:unevaluated_resources).returns(nil) + + assert_nothing_raised("Could not test for unevaluated resources") do + assert(! config.send(:evaluate_definitions), "evaluate_definitions returned true when no resources were evaluated") + end + + # Now try it with resources left to evaluate + resources = [] + res1 = mock("resource1") + res1.expects(:evaluate) + res2 = mock("resource2") + res2.expects(:evaluate) + resources << res1 << res2 + config = mkconfig + config.expects(:unevaluated_resources).returns(resources) + + assert_nothing_raised("Could not test for unevaluated resources") do + assert(config.send(:evaluate_definitions), "evaluate_definitions returned false when resources were evaluated") + end + end + + def test_evaluate_generators + # First try the case where we have nothing to do + config = mkconfig + config.expects(:evaluate_definitions).returns(false) + config.expects(:evaluate_collections).returns(false) + + assert_nothing_raised("Could not call :eval_iterate") do + config.send(:evaluate_generators) + end + + # FIXME I could not get this test to work, but the code is short + # enough that I'm ok with it. + # It's important that collections are evaluated before definitions, + # so make sure that's the case by verifying that collections get tested + # twice but definitions only once. + #config = mkconfig + #config.expects(:evaluate_collections).returns(true).returns(false) + #config.expects(:evaluate_definitions).returns(false) + #config.send(:eval_iterate) + end + + def test_store + config = mkconfig + Puppet.features.expects(:rails?).returns(true) + Puppet::Rails.expects(:connect) + + args = {:name => "yay"} + config.expects(:store_to_active_record).with(args) + config.send(:store, args) + end + + def test_store_to_active_record + config = mkconfig + args = {:name => "yay"} + Puppet::Rails::Host.stubs(:transaction).yields + Puppet::Rails::Host.expects(:store).with(args) + config.send(:store_to_active_record, args) + end + + # Make sure that 'finish' gets called on all of our resources. + def test_finish + config = mkconfig + table = config.instance_variable_get("@resource_table") + + # Add a resource that does respond to :finish + yep = mock("finisher") + yep.expects(:respond_to?).with(:finish).returns(true) + yep.expects(:finish) + table["yep"] = yep + + # And one that does not + dnf = mock("dnf") + dnf.expects(:respond_to?).with(:finish).returns(false) + table["dnf"] = dnf + + config.send(:finish) + end + + def test_extract + config = mkconfig + config.expects(:extraction_format).returns(:whatever) + config.expects(:extract_to_whatever).returns(:result) + assert_equal(:result, config.send(:extract), "Did not return extraction result as the method result") + end + + # We want to make sure that the scope and resource graphs translate correctly + def test_extract_to_transportable_simple + # Start with a really simple graph -- one scope, one resource. + config = mkconfig + resources = config.instance_variable_get("@resource_graph") + scopes = config.instance_variable_get("@scope_graph") + + # Get rid of the topscope + scopes.vertices.each { |v| scopes.remove_vertex!(v) } + + scope = mock("scope") + scope.expects(:to_trans).returns([]) + scopes.add_vertex! scope + + # The topscope is the key to picking out the top of the graph. + config.instance_variable_set("@topscope", scope) + + resource = mock "resource" + resource.expects(:to_trans).returns(:resource) + resources.add_edge! scope, resource + + result = nil + assert_nothing_raised("Could not extract transportable configuration") do + result = config.send :extract_to_transportable + end + assert_equal([:resource], result, "Did not translate simple configuration correctly") + end + + def test_extract_to_transportable_complex + # Now try it with a more complicated graph -- a three tier graph, each tier + # having a scope and a resource. + config = mkconfig + resources = config.instance_variable_get("@resource_graph") + scopes = config.instance_variable_get("@scope_graph") + + # Get rid of the topscope + scopes.vertices.each { |v| scopes.remove_vertex!(v) } + + fakebucket = Class.new(Array) do + attr_accessor :name + def initialize(n) + @name = n + end + end + + # Create our scopes. + top = mock("top") + top.expects(:to_trans).returns(fakebucket.new("top")) + # The topscope is the key to picking out the top of the graph. + config.instance_variable_set("@topscope", top) + middle = mock("middle") + middle.expects(:to_trans).returns(fakebucket.new("middle")) + scopes.add_edge! top, middle + bottom = mock("bottom") + bottom.expects(:to_trans).returns(fakebucket.new("bottom")) + scopes.add_edge! middle, bottom + + topres = mock "topres" + topres.expects(:to_trans).returns(:topres) + resources.add_edge! top, topres + + midres = mock "midres" + midres.expects(:to_trans).returns(:midres) + resources.add_edge! middle, midres + + botres = mock "botres" + botres.expects(:to_trans).returns(:botres) + resources.add_edge! bottom, botres + + result = nil + assert_nothing_raised("Could not extract transportable configuration") do + result = config.send :extract_to_transportable + end + assert_equal([[[:botres], :midres], :topres], result, "Did not translate medium configuration correctly") + end + + def test_verify_uniqueness + config = mkconfig + + resources = config.instance_variable_get("@resource_table") + resource = mock("noconflict") + resource.expects(:ref).returns("File[yay]") + assert_nothing_raised("Raised an exception when there should have been no conflict") do + config.send(:verify_uniqueness, resource) + end + + # Now try the case where our type is isomorphic + resources["thing"] = true + + isoconflict = mock("isoconflict") + isoconflict.expects(:ref).returns("thing") + isoconflict.expects(:type).returns("testtype") + faketype = mock("faketype") + faketype.expects(:isomorphic?).returns(false) + faketype.expects(:name).returns("whatever") + Puppet::Type.expects(:type).with("testtype").returns(faketype) + assert_nothing_raised("Raised an exception when was a conflict in non-isomorphic types") do + config.send(:verify_uniqueness, isoconflict) + end + + # Now test for when we actually have an exception + initial = mock("initial") + resources["thing"] = initial + initial.expects(:file).returns(false) + + conflict = mock("conflict") + conflict.expects(:ref).returns("thing").times(2) + conflict.expects(:type).returns("conflict") + conflict.expects(:file).returns(false) + conflict.expects(:line).returns(false) + + faketype = mock("faketype") + faketype.expects(:isomorphic?).returns(true) + Puppet::Type.expects(:type).with("conflict").returns(faketype) + assert_raise(Puppet::ParseError, "Did not fail when two isomorphic resources conflicted") do + config.send(:verify_uniqueness, conflict) + end + end + + def test_store_resource + # Run once when there's no conflict + config = mkconfig + table = config.instance_variable_get("@resource_table") + resource = mock("resource") + resource.expects(:ref).returns("yay") + config.expects(:verify_uniqueness).with(resource) + scope = mock("scope") + + graph = config.instance_variable_get("@resource_graph") + graph.expects(:add_edge!).with(scope, resource) + + assert_nothing_raised("Could not store resource") do + config.store_resource(scope, resource) + end + assert_equal(resource, table["yay"], "Did not store resource in table") + + # Now for conflicts + config = mkconfig + table = config.instance_variable_get("@resource_table") + resource = mock("resource") + config.expects(:verify_uniqueness).with(resource).raises(ArgumentError) + + assert_raise(ArgumentError, "Did not raise uniqueness exception") do + config.store_resource(scope, resource) + end + assert(table.empty?, "Conflicting resource was stored in table") + end + + def test_fail_on_unevaluated + config = mkconfig + config.expects(:fail_on_unevaluated_overrides) + config.expects(:fail_on_unevaluated_resource_collections) + config.send :fail_on_unevaluated + end + + def test_store_override + # First test the case when the resource is not present. + config = mkconfig + overrides = config.instance_variable_get("@resource_overrides") + override = Object.new + override.expects(:ref).returns(:myref).times(2) + override.expects(:override=).with(true) + + assert_nothing_raised("Could not call store_override") do + config.store_override(override) + end + assert_instance_of(Array, overrides[:myref], "Overrides table is not a hash of arrays") + assert_equal(override, overrides[:myref][0], "Did not store override in appropriately named array") + + # And when the resource already exists. + resource = mock 'resource' + resources = config.instance_variable_get("@resource_table") + resources[:resref] = resource + + override = mock 'override' + resource.expects(:merge).with(override) + override.expects(:override=).with(true) + override.expects(:ref).returns(:resref) + assert_nothing_raised("Could not call store_override when the resource already exists.") do + config.store_override(override) + end + end + + def test_resource_overrides + config = mkconfig + overrides = config.instance_variable_get("@resource_overrides") + overrides[:test] = :yay + resource = mock 'resource' + resource.expects(:ref).returns(:test) + + assert_equal(:yay, config.resource_overrides(resource), "Did not return overrides from table") + end + + def test_fail_on_unevaluated_resource_collections + config = mkconfig + collections = config.instance_variable_get("@collections") + + # Make sure we're fine when the list is empty + assert_nothing_raised("Failed when no collections were present") do + config.send :fail_on_unevaluated_resource_collections + end + + # And that we're fine when we've got collections but with no resources + collections << mock('coll') + collections[0].expects(:resources).returns(nil) + assert_nothing_raised("Failed when no resource collections were present") do + config.send :fail_on_unevaluated_resource_collections + end + + # But that we do fail when we've got resource collections left. + collections.clear + + # return both an array and a string, because that's tested internally + collections << mock('coll returns one') + collections[0].expects(:resources).returns(:something) + + collections << mock('coll returns many') + collections[1].expects(:resources).returns([:one, :two]) + + assert_raise(Puppet::ParseError, "Did not fail on unevaluated resource collections") do + config.send :fail_on_unevaluated_resource_collections + end + end + + def test_fail_on_unevaluated_overrides + config = mkconfig + overrides = config.instance_variable_get("@resource_overrides") + + # Make sure we're fine when the list is empty + assert_nothing_raised("Failed when no collections were present") do + config.send :fail_on_unevaluated_overrides + end + + # But that we fail if there are any overrides left in the table. + overrides[:yay] = [] + overrides[:foo] = [] + overrides[:bar] = [mock("override")] + overrides[:bar][0].expects(:ref).returns("yay") + assert_raise(Puppet::ParseError, "Failed to fail when overrides remain") do + config.send :fail_on_unevaluated_overrides + end + end + + def test_find_resource + config = mkconfig + resources = config.instance_variable_get("@resource_table") + + assert_nothing_raised("Could not call findresource when the resource table was empty") do + assert_nil(config.findresource("yay", "foo"), "Returned a non-existent resource") + assert_nil(config.findresource("yay[foo]"), "Returned a non-existent resource") + end + + resources["Foo[bar]"] = :yay + assert_nothing_raised("Could not call findresource when the resource table was not empty") do + assert_equal(:yay, config.findresource("foo", "bar"), "Returned a non-existent resource") + assert_equal(:yay, config.findresource("Foo[bar]"), "Returned a non-existent resource") + end + end end |
