diff options
| author | Josh Cooper <josh@puppetlabs.com> | 2011-04-21 14:37:50 -0700 |
|---|---|---|
| committer | Josh Cooper <josh@puppetlabs.com> | 2011-04-21 14:37:50 -0700 |
| commit | 01f610bb223b435dc52f491260af3ea002930102 (patch) | |
| tree | 93565136d5ef2b4156fdd64476792e441bcfbb4e /spec | |
| parent | ac428b9557e2da251e4b51e48de844833ca0aa2a (diff) | |
| parent | fc66e98b84b9a16728af054485883334a5887cca (diff) | |
Merge branch 'next'
Diffstat (limited to 'spec')
47 files changed, 2085 insertions, 377 deletions
diff --git a/spec/integration/indirector/catalog/queue_spec.rb b/spec/integration/indirector/catalog/queue_spec.rb index 569f096bf..940c8bab8 100755 --- a/spec/integration/indirector/catalog/queue_spec.rb +++ b/spec/integration/indirector/catalog/queue_spec.rb @@ -19,13 +19,13 @@ describe "Puppet::Resource::Catalog::Queue", :if => Puppet.features.pson? do after { Puppet.settings.clear } - it "should render catalogs to pson and send them via the queue client when catalogs are saved" do + it "should render catalogs to pson and publish them via the queue client when catalogs are saved" do terminus = Puppet::Resource::Catalog.indirection.terminus(:queue) client = mock 'client' terminus.stubs(:client).returns client - client.expects(:send_message).with(:catalog, @catalog.to_pson) + client.expects(:publish_message).with(:catalog, @catalog.to_pson) request = Puppet::Indirector::Request.new(:catalog, :save, "foo", :instance => @catalog) diff --git a/spec/integration/transaction_spec.rb b/spec/integration/transaction_spec.rb index 41a1ebac3..78d62fc51 100755 --- a/spec/integration/transaction_spec.rb +++ b/spec/integration/transaction_spec.rb @@ -75,6 +75,62 @@ describe Puppet::Transaction do transaction.evaluate end + it "should not apply device resources on normal host" do + catalog = Puppet::Resource::Catalog.new + resource = Puppet::Type.type(:interface).new :name => "FastEthernet 0/1" + catalog.add_resource resource + + transaction = Puppet::Transaction.new(catalog) + transaction.for_network_device = false + + transaction.expects(:apply).never.with(resource, nil) + + transaction.evaluate + transaction.resource_status(resource).should be_skipped + end + + it "should not apply host resources on device" do + catalog = Puppet::Resource::Catalog.new + resource = Puppet::Type.type(:file).new :path => "/foo/bar", :backup => false + catalog.add_resource resource + + transaction = Puppet::Transaction.new(catalog) + transaction.for_network_device = true + + transaction.expects(:apply).never.with(resource, nil) + + transaction.evaluate + transaction.resource_status(resource).should be_skipped + end + + it "should apply device resources on device" do + catalog = Puppet::Resource::Catalog.new + resource = Puppet::Type.type(:interface).new :name => "FastEthernet 0/1" + catalog.add_resource resource + + transaction = Puppet::Transaction.new(catalog) + transaction.for_network_device = true + + transaction.expects(:apply).with(resource, nil) + + transaction.evaluate + transaction.resource_status(resource).should_not be_skipped + end + + it "should apply resources appliable on host and device on a device" do + catalog = Puppet::Resource::Catalog.new + resource = Puppet::Type.type(:schedule).new :name => "test" + catalog.add_resource resource + + transaction = Puppet::Transaction.new(catalog) + transaction.for_network_device = true + + transaction.expects(:apply).with(resource, nil) + + transaction.evaluate + transaction.resource_status(resource).should_not be_skipped + end + # Verify that one component requiring another causes the contained # resources in the requiring component to get refreshed. it "should propagate events from a contained resource through its container to its dependent container's contained resources" do diff --git a/spec/lib/matchers/json.rb b/spec/lib/matchers/json.rb new file mode 100644 index 000000000..798e4cd21 --- /dev/null +++ b/spec/lib/matchers/json.rb @@ -0,0 +1,111 @@ +RSpec::Matchers.define :set_json_attribute do |*attributes| + def format + @format ||= Puppet::Network::FormatHandler.format('pson') + end + + chain :to do |value| + @value = value + end + + def json(instance) + PSON.parse(instance.to_pson) + end + + def attr_value(attrs, instance) + attrs = attrs.dup + hash = json(instance)['data'] + while attrs.length > 0 + name = attrs.shift + hash = hash[name] + end + hash + end + + match do |instance| + result = attr_value(attributes, instance) + if @value + result == @value + else + ! result.nil? + end + end + + failure_message_for_should do |instance| + if @value + "expected #{instance.inspect} to set #{attributes.inspect} to #{@value.inspect}; got #{attr_value(attributes, instance).inspect}" + else + "expected #{instance.inspect} to set #{attributes.inspect} but was nil" + end + end + + failure_message_for_should_not do |instance| + if @value + "expected #{instance.inspect} not to set #{attributes.inspect} to #{@value.inspect}" + else + "expected #{instance.inspect} not to set #{attributes.inspect} to nil" + end + end +end + +RSpec::Matchers.define :set_json_document_type_to do |type| + def format + @format ||= Puppet::Network::FormatHandler.format('pson') + end + + match do |instance| + json(instance)['document_type'] == type + end + + def json(instance) + PSON.parse(instance.to_pson) + end + + failure_message_for_should do |instance| + "expected #{instance.inspect} to set document_type to #{type.inspect}; got #{json(instance)['document_type'].inspect}" + end + + failure_message_for_should_not do |instance| + "expected #{instance.inspect} not to set document_type to #{type.inspect}" + end +end + +RSpec::Matchers.define :read_json_attribute do |attribute| + def format + @format ||= Puppet::Network::FormatHandler.format('pson') + end + + chain :from do |value| + @json = value + end + + chain :as do |as| + @value = as + end + + match do |klass| + raise "Must specify json with 'from'" unless @json + + @instance = format.intern(klass, @json) + if @value + @instance.send(attribute) == @value + else + ! @instance.send(attribute).nil? + end + end + + failure_message_for_should do |klass| + if @value + "expected #{klass} to read #{attribute} from #{@json} as #{@value.inspect}; got #{@instance.send(attribute).inspect}" + else + "expected #{klass} to read #{attribute} from #{@json} but was nil" + end + end + + failure_message_for_should_not do |klass| + if @value + "expected #{klass} not to set #{attribute} to #{@value}" + else + "expected #{klass} not to set #{attribute} to nil" + end + end +end diff --git a/spec/lib/puppet/face/basetest.rb b/spec/lib/puppet/face/basetest.rb index 00616f74f..e935161ae 100755 --- a/spec/lib/puppet/face/basetest.rb +++ b/spec/lib/puppet/face/basetest.rb @@ -1 +1,2 @@ +require 'puppet/face' Puppet::Face.define(:basetest, '0.0.1') diff --git a/spec/shared_behaviours/documentation_on_faces.rb b/spec/shared_behaviours/documentation_on_faces.rb new file mode 100644 index 000000000..41b4015c9 --- /dev/null +++ b/spec/shared_behaviours/documentation_on_faces.rb @@ -0,0 +1,35 @@ +# encoding: UTF-8 +shared_examples_for "documentation on faces" do + context "description" do + describe "#summary" do + it "should accept a summary" do + text = "this is my summary" + expect { subject.summary = text }.to_not raise_error + subject.summary.should == text + end + + it "should accept a long, long, long summary" do + text = "I never know when to stop with the word banana" + ("na" * 1000) + expect { subject.summary = text }.to_not raise_error + subject.summary.should == text + end + + it "should reject a summary with a newline" do + expect { subject.summary = "with\nnewlines" }. + to raise_error ArgumentError, /summary should be a single line/ + end + end + + describe "#description" do + it "should accept a description" do + subject.description = "hello" + subject.description.should == "hello" + end + + it "should accept a description with a newline" do + subject.description = "hello \n my \n fine \n friend" + subject.description.should == "hello \n my \n fine \n friend" + end + end + end +end diff --git a/spec/shared_behaviours/things_that_declare_options.rb b/spec/shared_behaviours/things_that_declare_options.rb index ac358eacd..5300a159a 100755 --- a/spec/shared_behaviours/things_that_declare_options.rb +++ b/spec/shared_behaviours/things_that_declare_options.rb @@ -1,45 +1,45 @@ # encoding: UTF-8 shared_examples_for "things that declare options" do it "should support options without arguments" do - subject = add_options_to { option "--bar" } - subject.should be_option :bar + thing = add_options_to { option "--bar" } + thing.should be_option :bar end it "should support options with an empty block" do - subject = add_options_to do + thing = add_options_to do option "--foo" do # this section deliberately left blank end end - subject.should be - subject.should be_option :foo + thing.should be + thing.should be_option :foo end { "--foo=" => :foo }.each do |input, option| it "should accept #{name.inspect}" do - subject = add_options_to { option input } - subject.should be_option option + thing = add_options_to { option input } + thing.should be_option option end end it "should support option documentation" do text = "Sturm und Drang (German pronunciation: [ˈʃtʊʁm ʊnt ˈdʁaŋ]) …" - subject = add_options_to do + thing = add_options_to do option "--foo" do desc text end end - subject.get_option(:foo).desc.should == text + thing.get_option(:foo).desc.should == text end it "should list all the options" do - subject = add_options_to do + thing = add_options_to do option "--foo" option "--bar" end - subject.options.should =~ [:foo, :bar] + thing.options.should =~ [:foo, :bar] end it "should detect conflicts in long options" do @@ -95,22 +95,24 @@ shared_examples_for "things that declare options" do should raise_error ArgumentError, /inconsistent about taking an argument/ end - it "should accept optional arguments" do - subject = add_options_to do option "--foo=[baz]", "--bar=[baz]" end - [:foo, :bar].each do |name| - subject.should be_option name - end + it "should not accept optional arguments" do + expect do + thing = add_options_to do option "--foo=[baz]", "--bar=[baz]" end + [:foo, :bar].each do |name| + thing.should be_option name + end + end.to raise_error(ArgumentError, /optional arguments are not supported/) end describe "#takes_argument?" do it "should detect an argument being absent" do - subject = add_options_to do option "--foo" end - subject.get_option(:foo).should_not be_takes_argument + thing = add_options_to do option "--foo" end + thing.get_option(:foo).should_not be_takes_argument end - ["=FOO", " FOO", "=[FOO]", " [FOO]"].each do |input| + ["=FOO", " FOO"].each do |input| it "should detect an argument given #{input.inspect}" do - subject = add_options_to do option "--foo#{input}" end - subject.get_option(:foo).should be_takes_argument + thing = add_options_to do option "--foo#{input}" end + thing.get_option(:foo).should be_takes_argument end end end @@ -131,10 +133,12 @@ shared_examples_for "things that declare options" do end ["=[FOO]", " [FOO]"].each do |input| - it "should be true if the argument is optional (like #{input.inspect})" do - option = add_options_to do option "--foo#{input}" end.get_option(:foo) - option.should be_takes_argument - option.should be_optional_argument + it "should fail if the argument is optional (like #{input.inspect})" do + expect do + option = add_options_to do option "--foo#{input}" end.get_option(:foo) + option.should be_takes_argument + option.should be_optional_argument + end.to raise_error(ArgumentError, /optional arguments are not supported/) end end end diff --git a/spec/unit/application/device_spec.rb b/spec/unit/application/device_spec.rb new file mode 100644 index 000000000..086e321e9 --- /dev/null +++ b/spec/unit/application/device_spec.rb @@ -0,0 +1,349 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/device' +require 'puppet/util/network_device/config' +require 'ostruct' +require 'puppet/configurer' + +describe Puppet::Application::Device do + before :each do + @device = Puppet::Application[:device] +# @device.stubs(:puts) + @device.preinit + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:level=) + + Puppet::Node.indirection.stubs(:terminus_class=) + Puppet::Node.indirection.stubs(:cache_class=) + Puppet::Node::Facts.indirection.stubs(:terminus_class=) + end + + it "should operate in agent run_mode" do + @device.class.run_mode.name.should == :agent + end + + it "should ask Puppet::Application to parse Puppet configuration file" do + @device.should_parse_config?.should be_true + end + + it "should declare a main command" do + @device.should respond_to(:main) + end + + it "should declare a preinit block" do + @device.should respond_to(:preinit) + end + + describe "in preinit" do + before :each do + @device.stubs(:trap) + end + + it "should catch INT" do + @device.expects(:trap).with { |arg,block| arg == :INT } + + @device.preinit + end + end + + describe "when handling options" do + before do + @device.command_line.stubs(:args).returns([]) + end + + [:centrallogging, :debug, :verbose,].each do |option| + it "should declare handle_#{option} method" do + @device.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @device.options.expects(:[]=).with(option, 'arg') + @device.send("handle_#{option}".to_sym, 'arg') + end + end + + it "should set waitforcert to 0 with --onetime and if --waitforcert wasn't given" do + Puppet[:onetime] = true + Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(0) + @device.setup_host + end + + it "should use supplied waitforcert when --onetime is specified" do + Puppet[:onetime] = true + @device.handle_waitforcert(60) + Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(60) + @device.setup_host + end + + it "should use a default value for waitforcert when --onetime and --waitforcert are not specified" do + Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(120) + @device.setup_host + end + + it "should set the log destination with --logdest" do + @device.options.stubs(:[]=).with { |opt,val| opt == :setdest } + Puppet::Log.expects(:newdestination).with("console") + + @device.handle_logdest("console") + end + + it "should put the setdest options to true" do + @device.options.expects(:[]=).with(:setdest,true) + + @device.handle_logdest("console") + end + + it "should parse the log destination from the command line" do + @device.command_line.stubs(:args).returns(%w{--logdest /my/file}) + + Puppet::Util::Log.expects(:newdestination).with("/my/file") + + @device.parse_options + end + + it "should store the waitforcert options with --waitforcert" do + @device.options.expects(:[]=).with(:waitforcert,42) + + @device.handle_waitforcert("42") + end + + it "should set args[:Port] with --port" do + @device.handle_port("42") + @device.args[:Port].should == "42" + end + + end + + describe "during setup" do + before :each do + @device.options.stubs(:[]) + Puppet.stubs(:info) + FileTest.stubs(:exists?).returns(true) + Puppet[:libdir] = "/dev/null/lib" + Puppet::SSL::Host.stubs(:ca_location=) + Puppet::Transaction::Report.indirection.stubs(:terminus_class=) + Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) + Puppet::Resource::Catalog.indirection.stubs(:cache_class=) + Puppet::Node::Facts.indirection.stubs(:terminus_class=) + @host = stub_everything 'host' + Puppet::SSL::Host.stubs(:new).returns(@host) + Puppet.stubs(:settraps) + end + + it "should call setup_logs" do + @device.expects(:setup_logs) + @device.setup + end + + describe "when setting up logs" do + before :each do + Puppet::Util::Log.stubs(:newdestination) + end + + it "should set log level to debug if --debug was passed" do + @device.options.stubs(:[]).with(:debug).returns(true) + + Puppet::Util::Log.expects(:level=).with(:debug) + + @device.setup_logs + end + + it "should set log level to info if --verbose was passed" do + @device.options.stubs(:[]).with(:verbose).returns(true) + + Puppet::Util::Log.expects(:level=).with(:info) + + @device.setup_logs + end + + [:verbose, :debug].each do |level| + it "should set console as the log destination with level #{level}" do + @device.options.stubs(:[]).with(level).returns(true) + + Puppet::Util::Log.expects(:newdestination).with(:console) + + @device.setup_logs + end + end + + it "should set syslog as the log destination if no --logdest" do + @device.options.stubs(:[]).with(:setdest).returns(false) + + Puppet::Util::Log.expects(:newdestination).with(:syslog) + + @device.setup_logs + end + + end + + it "should set a central log destination with --centrallogs" do + @device.options.stubs(:[]).with(:centrallogs).returns(true) + Puppet[:server] = "puppet.reductivelabs.com" + Puppet::Util::Log.stubs(:newdestination).with(:syslog) + + Puppet::Util::Log.expects(:newdestination).with("puppet.reductivelabs.com") + + @device.setup + end + + it "should use :main, :agent, :device and :ssl config" do + Puppet.settings.expects(:use).with(:main, :agent, :device, :ssl) + + @device.setup + end + + it "should install a remote ca location" do + Puppet::SSL::Host.expects(:ca_location=).with(:remote) + + @device.setup + end + + it "should tell the report handler to use REST" do + Puppet::Transaction::Report.indirection.expects(:terminus_class=).with(:rest) + + @device.setup + end + + it "should change the catalog_terminus setting to 'rest'" do + Puppet[:catalog_terminus] = :foo + @device.setup + Puppet[:catalog_terminus].should == :rest + end + + it "should tell the catalog handler to use cache" do + Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:yaml) + + @device.setup + end + + it "should change the facts_terminus setting to 'network_device'" do + Puppet[:facts_terminus] = :foo + + @device.setup + Puppet[:facts_terminus].should == :network_device + end + end + + describe "when initializing each devices SSL" do + before(:each) do + @host = stub_everything 'host' + Puppet::SSL::Host.stubs(:new).returns(@host) + end + + it "should create a new ssl host" do + Puppet::SSL::Host.expects(:new).returns(@host) + @device.setup_host + end + + it "should wait for a certificate" do + @device.options.stubs(:[]).with(:waitforcert).returns(123) + @host.expects(:wait_for_cert).with(123) + + @device.setup_host + end + end + + + describe "when running" do + before :each do + @device.options.stubs(:[]).with(:fingerprint).returns(false) + Puppet.stubs(:notice) + @device.options.stubs(:[]).with(:client) + Puppet::Util::NetworkDevice::Config.stubs(:devices).returns({}) + end + + it "should dispatch to main" do + @device.stubs(:main) + @device.run_command + end + + it "should get the device list" do + device_hash = stub_everything 'device hash' + Puppet::Util::NetworkDevice::Config.expects(:devices).returns(device_hash) + @device.main + end + + it "should exit if the device list is empty" do + @device.expects(:exit).with(1) + @device.main + end + + describe "for each device" do + before(:each) do + Puppet[:vardir] = "/dummy" + Puppet[:confdir] = "/dummy" + Puppet[:certname] = "certname" + @device_hash = { + "device1" => OpenStruct.new(:name => "device1", :url => "url", :provider => "cisco"), + "device2" => OpenStruct.new(:name => "device2", :url => "url", :provider => "cisco"), + } + Puppet::Util::NetworkDevice::Config.stubs(:devices).returns(@device_hash) + Puppet.settings.stubs(:set_value) + Puppet.settings.stubs(:use) + @device.stubs(:setup_host) + Puppet::Util::NetworkDevice.stubs(:init) + @configurer = stub_everything 'configurer' + Puppet::Configurer.stubs(:new).returns(@configurer) + end + + it "should set vardir to the device vardir" do + Puppet.settings.expects(:set_value).with(:vardir, "/dummy/devices/device1", :cli) + @device.main + end + + it "should set confdir to the device confdir" do + Puppet.settings.expects(:set_value).with(:confdir, "/dummy/devices/device1", :cli) + @device.main + end + + it "should set certname to the device certname" do + Puppet.settings.expects(:set_value).with(:certname, "device1", :cli) + Puppet.settings.expects(:set_value).with(:certname, "device2", :cli) + @device.main + end + + it "should make sure all the required folders and files are created" do + Puppet.settings.expects(:use).with(:main, :agent, :ssl).twice + @device.main + end + + it "should initialize the device singleton" do + Puppet::Util::NetworkDevice.expects(:init).with(@device_hash["device1"]).then.with(@device_hash["device2"]) + @device.main + end + + it "should setup the SSL context" do + @device.expects(:setup_host).twice + @device.main + end + + it "should launch a configurer for this device" do + @configurer.expects(:run).twice + @device.main + end + + [:vardir, :confdir].each do |setting| + it "should cleanup the #{setting} setting after the run" do + configurer = states('configurer').starts_as('notrun') + Puppet.settings.expects(:set_value).with(setting, "/dummy/devices/device1", :cli).when(configurer.is('notrun')) + @configurer.expects(:run).twice.then(configurer.is('run')) + Puppet.settings.expects(:set_value).with(setting, "/dummy", :cli).when(configurer.is('run')) + + @device.main + end + end + + it "should cleanup the certname setting after the run" do + configurer = states('configurer').starts_as('notrun') + Puppet.settings.expects(:set_value).with(:certname, "device1", :cli).when(configurer.is('notrun')) + @configurer.expects(:run).twice.then(configurer.is('run')) + Puppet.settings.expects(:set_value).with(:certname, "certname", :cli).when(configurer.is('run')) + + @device.main + end + + end + end +end diff --git a/spec/unit/application/face_base_spec.rb b/spec/unit/application/face_base_spec.rb index 939712ef8..5403608cf 100755 --- a/spec/unit/application/face_base_spec.rb +++ b/spec/unit/application/face_base_spec.rb @@ -11,7 +11,6 @@ describe Puppet::Application::FaceBase do Puppet::Face.define(:basetest, '0.0.1') do option("--[no-]boolean") option("--mandatory MANDATORY") - option("--optional [OPTIONAL]") action :foo do option("--action") @@ -60,19 +59,39 @@ describe Puppet::Application::FaceBase do app.face.name.should == :basetest end - it "should set the format based on the face default" do - app.format.should == :pson - end - it "should find the action" do app.action.should be app.action.name.should == :foo end end - it "should fail if no action is given" do - expect { app.preinit; app.parse_options }. - to raise_error OptionParser::MissingArgument, /No action given/ + it "should use the default action if not given any arguments" do + app.command_line.stubs(:args).returns [] + action = stub(:options => []) + Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(action) + app.stubs(:main) + app.run + app.action.should == action + app.arguments.should == [ { } ] + end + + it "should use the default action if not given a valid one" do + app.command_line.stubs(:args).returns %w{bar} + action = stub(:options => []) + Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(action) + app.stubs(:main) + app.run + app.action.should == action + app.arguments.should == [ 'bar', { } ] + end + + it "should have no action if not given a valid one and there is no default action" do + app.command_line.stubs(:args).returns %w{bar} + Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(nil) + app.stubs(:main) + app.run + app.action.should be_nil + app.arguments.should == [ 'bar', { } ] end it "should report a sensible error when options with = fail" do @@ -169,7 +188,6 @@ describe Puppet::Application::FaceBase do app.face = Puppet::Face[:basetest, '0.0.1'] app.action = app.face.get_action(:foo) - app.format = :pson app.arguments = ["myname", "myarg"] end @@ -178,10 +196,79 @@ describe Puppet::Application::FaceBase do app.main end + it "should lookup help when it cannot do anything else" do + app.action = nil + Puppet::Face[:help, :current].expects(:help).with(:basetest, *app.arguments) + app.stubs(:puts) # meh. Don't print nil, thanks. --daniel 2011-04-12 + app.main + end + it "should use its render method to render any result" do app.expects(:render).with(app.arguments.length + 1) app.stubs(:puts) # meh. Don't print nil, thanks. --daniel 2011-04-12 app.main end end + + describe "#render" do + before :each do + app.face = Puppet::Face[:basetest, '0.0.1'] + app.action = app.face.get_action(:foo) + end + + ["hello", 1, 1.0].each do |input| + it "should just return a #{input.class.name}" do + app.render(input).should == input + end + end + + [[1, 2], ["one"], [{ 1 => 1 }]].each do |input| + it "should render #{input.class} using the 'pp' library" do + app.render(input).should == input.pretty_inspect + end + end + + it "should render a non-trivially-keyed Hash with the 'pp' library" do + hash = { [1,2] => 3, [2,3] => 5, [3,4] => 7 } + app.render(hash).should == hash.pretty_inspect + end + + it "should render a {String,Numeric}-keyed Hash into a table" do + object = Object.new + hash = { "one" => 1, "two" => [], "three" => {}, "four" => object, + 5 => 5, 6.0 => 6 } + + # Gotta love ASCII-betical sort order. Hope your objects are better + # structured for display than my test one is. --daniel 2011-04-18 + app.render(hash).should == <<EOT +5 5 +6.0 6 +four #{object.pretty_inspect.chomp} +one 1 +three {} +two [] +EOT + end + + it "should render a hash nicely with a multi-line value" do + hash = { + "number" => { "1" => '1' * 40, "2" => '2' * 40, '3' => '3' * 40 }, + "text" => { "a" => 'a' * 40, 'b' => 'b' * 40, 'c' => 'c' * 40 } + } + app.render(hash).should == <<EOT +number {"1"=>"1111111111111111111111111111111111111111", + "2"=>"2222222222222222222222222222222222222222", + "3"=>"3333333333333333333333333333333333333333"} +text {"a"=>"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "b"=>"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "c"=>"cccccccccccccccccccccccccccccccccccccccc"} +EOT + end + + it "should invoke the action rendering hook while rendering" do + app.action.set_rendering_method_for(:for_humans, proc { |value| "bi-winning!" }) + app.action.render_as = :for_humans + app.render("bi-polar?").should == "bi-winning!" + end + end end diff --git a/spec/unit/application/indirection_base_spec.rb b/spec/unit/application/indirection_base_spec.rb index 63ab11eed..57740384a 100755 --- a/spec/unit/application/indirection_base_spec.rb +++ b/spec/unit/application/indirection_base_spec.rb @@ -23,12 +23,11 @@ describe Puppet::Application::IndirectionBase do it "should accept a terminus command line option" do # It would be nice not to have to stub this, but whatever... writing an # entire indirection stack would cause us more grief. --daniel 2011-03-31 - terminus = mock("test indirection terminus") + terminus = stub_everything("test indirection terminus") Puppet::Indirector::Indirection.expects(:instance). - with(:testindirection).twice.returns() + with(:testindirection).returns(terminus) - subject.command_line. - instance_variable_set('@args', %w{--terminus foo save}) + subject.command_line.instance_variable_set('@args', %w{--terminus foo save}) # Not a very nice thing. :( $stderr.stubs(:puts) diff --git a/spec/unit/face/certificate_spec.rb b/spec/unit/face/certificate_spec.rb index dbcc888ad..b0bbf1af6 100755 --- a/spec/unit/face/certificate_spec.rb +++ b/spec/unit/face/certificate_spec.rb @@ -6,9 +6,14 @@ describe Puppet::Face[:certificate, '0.0.1'] do end it "should set the ca location when invoked" do - pending "#6983: This is broken in the actual face..." Puppet::SSL::Host.expects(:ca_location=).with(:foo) Puppet::SSL::Host.indirection.expects(:save) - subject.sign :ca_location => :foo + subject.sign "hello, friend", :ca_location => :foo + end + + it "(#7059) should set the ca location when an inherited action is invoked" do + Puppet::SSL::Host.expects(:ca_location=).with(:foo) + subject.indirection.expects(:find) + subject.find "hello, friend", :ca_location => :foo end end diff --git a/spec/unit/face/facts_spec.rb b/spec/unit/face/facts_spec.rb index e6411f836..6ab6ad5be 100755 --- a/spec/unit/face/facts_spec.rb +++ b/spec/unit/face/facts_spec.rb @@ -2,14 +2,10 @@ require 'spec_helper' describe Puppet::Face[:facts, '0.0.1'] do - it "should define an 'upload' fact" do + it "should define an 'upload' action" do subject.should be_action(:upload) end - it "should set its default format to :yaml" do - subject.default_format.should == :yaml - end - describe "when uploading" do it "should set the terminus_class to :facter" diff --git a/spec/unit/face/help_spec.rb b/spec/unit/face/help_spec.rb index e67f29e07..faa5f9617 100755 --- a/spec/unit/face/help_spec.rb +++ b/spec/unit/face/help_spec.rb @@ -7,7 +7,7 @@ describe Puppet::Face[:help, '0.0.1'] do end it "should have a default action of help" do - pending "REVISIT: we don't support default actions yet" + subject.get_action('help').should be_default end it "should accept a call with no arguments" do @@ -55,19 +55,23 @@ describe Puppet::Face[:help, '0.0.1'] do end end - Puppet::Face.faces.each do |name| - face = Puppet::Face[name, :current] - summary = face.summary + it "should list all faces" do + Puppet::Face.faces.each do |name| + face = Puppet::Face[name, :current] + summary = face.summary - it { should =~ %r{ #{name} } } - it { should =~ %r{ #{name} +#{summary}} } if summary + subject.should =~ %r{ #{name} } + summary and subject.should =~ %r{ #{name} +#{summary}} + end end - Puppet::Face[:help, :current].legacy_applications.each do |appname| - it { should =~ %r{ #{appname} } } + it "should list all legacy applications" do + Puppet::Face[:help, :current].legacy_applications.each do |appname| + subject.should =~ %r{ #{appname} } - summary = Puppet::Face[:help, :current].horribly_extract_summary_from(appname) - summary and it { should =~ %r{ #{summary}\b} } + summary = Puppet::Face[:help, :current].horribly_extract_summary_from(appname) + summary and subject.should =~ %r{ #{summary}\b} + end end end diff --git a/spec/unit/face/node_spec.rb b/spec/unit/face/node_spec.rb index 90d258db9..d19312c58 100755 --- a/spec/unit/face/node_spec.rb +++ b/spec/unit/face/node_spec.rb @@ -2,7 +2,5 @@ require 'spec_helper' describe Puppet::Face[:node, '0.0.1'] do - it "should set its default format to :yaml" do - subject.default_format.should == :yaml - end + it "REVISIT: really should have some tests" end diff --git a/spec/unit/indirector/facts/network_device_spec.rb b/spec/unit/indirector/facts/network_device_spec.rb new file mode 100644 index 000000000..302a810e8 --- /dev/null +++ b/spec/unit/indirector/facts/network_device_spec.rb @@ -0,0 +1,89 @@ +#!/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/facts/network_device' + +describe Puppet::Node::Facts::NetworkDevice do + it "should be a subclass of the Code terminus" do + Puppet::Node::Facts::NetworkDevice.superclass.should equal(Puppet::Indirector::Code) + end + + it "should have documentation" do + Puppet::Node::Facts::NetworkDevice.doc.should_not be_nil + end + + it "should be registered with the configuration store indirection" do + indirection = Puppet::Indirector::Indirection.instance(:facts) + Puppet::Node::Facts::NetworkDevice.indirection.should equal(indirection) + end + + it "should have its name set to :facter" do + Puppet::Node::Facts::NetworkDevice.name.should == :network_device + end +end + +describe Puppet::Node::Facts::NetworkDevice do + before :each do + @remote_device = stub 'remote_device', :facts => {} + Puppet::Util::NetworkDevice.stubs(:current).returns(@remote_device) + @device = Puppet::Node::Facts::NetworkDevice.new + @name = "me" + @request = stub 'request', :key => @name + end + + describe Puppet::Node::Facts::NetworkDevice, " when finding facts" do + it "should return a Facts instance" do + @device.find(@request).should be_instance_of(Puppet::Node::Facts) + end + + it "should return a Facts instance with the provided key as the name" do + @device.find(@request).name.should == @name + end + + it "should return the device facts as the values in the Facts instance" do + @remote_device.expects(:facts).returns("one" => "two") + facts = @device.find(@request) + facts.values["one"].should == "two" + end + + it "should add local facts" do + facts = Puppet::Node::Facts.new("foo") + Puppet::Node::Facts.expects(:new).returns facts + facts.expects(:add_local_facts) + + @device.find(@request) + end + + it "should convert all facts into strings" do + facts = Puppet::Node::Facts.new("foo") + Puppet::Node::Facts.expects(:new).returns facts + facts.expects(:stringify) + + @device.find(@request) + end + + it "should call the downcase hook" do + facts = Puppet::Node::Facts.new("foo") + Puppet::Node::Facts.expects(:new).returns facts + facts.expects(:downcase_if_necessary) + + @device.find(@request) + end + end + + describe Puppet::Node::Facts::NetworkDevice, " when saving facts" do + it "should fail" do + proc { @device.save(@facts) }.should raise_error(Puppet::DevError) + end + end + + describe Puppet::Node::Facts::NetworkDevice, " when destroying facts" do + it "should fail" do + proc { @device.destroy(@facts) }.should raise_error(Puppet::DevError) + end + end +end diff --git a/spec/unit/indirector/queue_spec.rb b/spec/unit/indirector/queue_spec.rb index b84ed2aea..eba136bbc 100755 --- a/spec/unit/indirector/queue_spec.rb +++ b/spec/unit/indirector/queue_spec.rb @@ -60,20 +60,20 @@ describe Puppet::Indirector::Queue, :if => Puppet.features.pson? do describe "when saving" do it 'should render the instance using pson' do @subject.expects(:render).with(:pson) - @store.client.stubs(:send_message) + @store.client.stubs(:publish_message) @store.save(@request) end - it "should send the rendered message to the appropriate queue on the client" do + it "should publish the rendered message to the appropriate queue on the client" do @subject.expects(:render).returns "mypson" - @store.client.expects(:send_message).with(:my_queue, "mypson") + @store.client.expects(:publish_message).with(:my_queue, "mypson") @store.save(@request) end it "should catch any exceptions raised" do - @store.client.expects(:send_message).raises ArgumentError + @store.client.expects(:publish_message).raises ArgumentError lambda { @store.save(@request) }.should raise_error(Puppet::Error) end diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb index 965d54188..ba7dc815e 100755 --- a/spec/unit/indirector/request_spec.rb +++ b/spec/unit/indirector/request_spec.rb @@ -1,5 +1,6 @@ #!/usr/bin/env rspec require 'spec_helper' +require 'matchers/json' require 'puppet/indirector/request' describe Puppet::Indirector::Request do @@ -300,4 +301,99 @@ describe Puppet::Indirector::Request do lambda { @request.query_string }.should raise_error(ArgumentError) end end + + describe "when converting to json" do + before do + @request = Puppet::Indirector::Request.new(:facts, :find, "foo") + end + + it "should produce a hash with the document_type set to 'request'" do + @request.should set_json_document_type_to("Puppet::Indirector::Request") + end + + it "should set the 'key'" do + @request.should set_json_attribute("key").to("foo") + end + + it "should include an attribute for its indirection name" do + @request.should set_json_attribute("type").to("facts") + end + + it "should include a 'method' attribute set to its method" do + @request.should set_json_attribute("method").to("find") + end + + it "should add all attributes under the 'attributes' attribute" do + @request.ip = "127.0.0.1" + @request.should set_json_attribute("attributes", "ip").to("127.0.0.1") + end + + it "should add all options under the 'attributes' attribute" do + @request.options["opt"] = "value" + PSON.parse(@request.to_pson)["data"]['attributes']['opt'].should == "value" + end + + it "should include the instance if provided" do + facts = Puppet::Node::Facts.new("foo") + @request.instance = facts + PSON.parse(@request.to_pson)["data"]['instance'].should be_instance_of(Puppet::Node::Facts) + end + end + + describe "when converting from json" do + before do + @request = Puppet::Indirector::Request.new(:facts, :find, "foo") + @klass = Puppet::Indirector::Request + @format = Puppet::Network::FormatHandler.format('pson') + end + + def from_json(json) + @format.intern(Puppet::Indirector::Request, json) + end + + it "should set the 'key'" do + from_json(@request.to_pson).key.should == "foo" + end + + it "should fail if no key is provided" do + json = PSON.parse(@request.to_pson) + json['data'].delete("key") + lambda { from_json(json.to_pson) }.should raise_error(ArgumentError) + end + + it "should set its indirector name" do + from_json(@request.to_pson).indirection_name.should == :facts + end + + it "should fail if no type is provided" do + json = PSON.parse(@request.to_pson) + json['data'].delete("type") + lambda { from_json(json.to_pson) }.should raise_error(ArgumentError) + end + + it "should set its method" do + from_json(@request.to_pson).method.should == "find" + end + + it "should fail if no method is provided" do + json = PSON.parse(@request.to_pson) + json['data'].delete("method") + lambda { from_json(json.to_pson) }.should raise_error(ArgumentError) + end + + it "should initialize with all attributes and options" do + @request.ip = "127.0.0.1" + @request.options["opt"] = "value" + result = from_json(@request.to_pson) + result.options[:opt].should == "value" + result.ip.should == "127.0.0.1" + end + + it "should set its instance as an instance if one is provided" do + facts = Puppet::Node::Facts.new("foo") + @request.instance = facts + result = from_json(@request.to_pson) + result.instance.should be_instance_of(Puppet::Node::Facts) + end + end end diff --git a/spec/unit/indirector/rest_spec.rb b/spec/unit/indirector/rest_spec.rb index 513eb8352..5637080e8 100755 --- a/spec/unit/indirector/rest_spec.rb +++ b/spec/unit/indirector/rest_spec.rb @@ -233,6 +233,11 @@ describe Puppet::Indirector::REST do params[s] = 'foo' end + # The request special-cases this parameter, and it + # won't be passed on to the server, so we remove it here + # to avoid a failure. + params.delete('ip') + @request = Puppet::Indirector::Request.new(:foo, :find, "foo bar", params.merge(:environment => "myenv")) @connection.expects(:post).with do |uri, body| diff --git a/spec/unit/interface/action_builder_spec.rb b/spec/unit/interface/action_builder_spec.rb index 5b04df900..bf7afa74e 100755 --- a/spec/unit/interface/action_builder_spec.rb +++ b/spec/unit/interface/action_builder_spec.rb @@ -1,68 +1,192 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/interface/action_builder' +require 'puppet/network/format_handler' describe Puppet::Interface::ActionBuilder do - describe "::build" do - it "should build an action" do - action = Puppet::Interface::ActionBuilder.build(nil, :foo) do + let :face do Puppet::Interface.new(:puppet_interface_actionbuilder, '0.0.1') end + + it "should build an action" do + action = Puppet::Interface::ActionBuilder.build(nil, :foo) do + end + action.should be_a(Puppet::Interface::Action) + action.name.should == :foo + end + + it "should define a method on the face which invokes the action" do + face = Puppet::Interface.new(:action_builder_test_interface, '0.0.1') do + action(:foo) { when_invoked { "invoked the method" } } + end + + face.foo.should == "invoked the method" + end + + it "should require a block" do + expect { Puppet::Interface::ActionBuilder.build(nil, :foo) }. + should raise_error("Action :foo must specify a block") + end + + describe "when handling options" do + it "should have a #option DSL function" do + method = nil + Puppet::Interface::ActionBuilder.build(face, :foo) do + method = self.method(:option) + end + method.should be + end + + it "should define an option without a block" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + option "--bar" end - action.should be_a(Puppet::Interface::Action) - action.name.should == :foo + action.should be_option :bar end - it "should define a method on the face which invokes the action" do - face = Puppet::Interface.new(:action_builder_test_interface, '0.0.1') + it "should accept an empty block" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do - when_invoked do - "invoked the method" + option "--bar" do + # This space left deliberately blank. end end + action.should be_option :bar + end + end + + context "inline documentation" do + it "should set the summary" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + summary "this is some text" + end + action.summary.should == "this is some text" + end + end - face.foo.should == "invoked the method" + context "action defaulting" do + it "should set the default to true" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + default + end + action.default.should be_true end - it "should require a block" do - expect { Puppet::Interface::ActionBuilder.build(nil, :foo) }. - should raise_error("Action :foo must specify a block") + it "should not be default by, er, default. *cough*" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do end + action.default.should be_false end + end - describe "when handling options" do - let :face do Puppet::Interface.new(:option_handling, '0.0.1') end + context "#when_rendering" do + it "should fail if no rendering format is given" do + expect { + Puppet::Interface::ActionBuilder.build(face, :foo) do + when_rendering do true end + end + }.to raise_error ArgumentError, /must give a rendering format to when_rendering/ + end - it "should have a #option DSL function" do - method = nil + it "should fail if no block is given" do + expect { Puppet::Interface::ActionBuilder.build(face, :foo) do - method = self.method(:option) + when_rendering :json end - method.should be - end + }.to raise_error ArgumentError, /must give a block to when_rendering/ + end - it "should define an option without a block" do - action = Puppet::Interface::ActionBuilder.build(face, :foo) do - option "--bar" + it "should fail if the block takes no arguments" do + expect { + Puppet::Interface::ActionBuilder.build(face, :foo) do + when_rendering :json do true end end - action.should be_option :bar + }.to raise_error ArgumentError, /when_rendering methods take one argument, the result, not/ + end + + it "should fail if the block takes more than one argument" do + expect { + Puppet::Interface::ActionBuilder.build(face, :foo) do + when_rendering :json do |a, b, c| true end + end + }.to raise_error ArgumentError, /when_rendering methods take one argument, the result, not/ + end + + it "should fail if the block takes a variable number of arguments" do + expect { + Puppet::Interface::ActionBuilder.build(face, :foo) do + when_rendering :json do |*args| true end + end + }.to raise_error(ArgumentError, + /when_rendering methods take one argument, the result, not/) + end + + it "should stash a rendering block" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + when_rendering :json do |a| true end end + action.when_rendering(:json).should be_an_instance_of Method + end - it "should accept an empty block" do - action = Puppet::Interface::ActionBuilder.build(face, :foo) do - option "--bar" do - # This space left deliberately blank. - end + it "should fail if you try to set the same rendering twice" do + expect { + Puppet::Interface::ActionBuilder.build(face, :foo) do + when_rendering :json do |a| true end + when_rendering :json do |a| true end end - action.should be_option :bar + }.to raise_error ArgumentError, /You can't define a rendering method for json twice/ + end + + it "should work if you set two different renderings" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + when_rendering :json do |a| true end + when_rendering :yaml do |a| true end end + action.when_rendering(:json).should be_an_instance_of Method + action.when_rendering(:yaml).should be_an_instance_of Method end - context "inline documentation" do - let :face do Puppet::Interface.new(:inline_action_docs, '0.0.1') end + it "should be bound to the face when called" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + when_rendering :json do |a| self end + end + action.when_rendering(:json).call(true).should == face + end + end + + context "#render_as" do + it "should default to nil (eg: based on context)" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do end + action.render_as.should be_nil + end + + it "should fail if not rendering format is given" do + expect { + Puppet::Interface::ActionBuilder.build(face, :foo) do + render_as + end + }.to raise_error ArgumentError, /must give a rendering format to render_as/ + end - it "should set the summary" do + Puppet::Network::FormatHandler.formats.each do |name| + it "should accept #{name.inspect} format" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do - summary "this is some text" + render_as name end - action.summary.should == "this is some text" + action.render_as.should == name + end + end + + it "should accept :for_humans format" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + render_as :for_humans + end + action.render_as.should == :for_humans + end + + [:if_you_define_this_format_you_frighten_me, "json", 12].each do |input| + it "should fail if given #{input.inspect}" do + expect { + Puppet::Interface::ActionBuilder.build(face, :foo) do + render_as input + end + }.to raise_error ArgumentError, /#{input.inspect} is not a valid rendering format/ end end end diff --git a/spec/unit/interface/action_manager_spec.rb b/spec/unit/interface/action_manager_spec.rb index c4b21eaac..07d517c18 100755 --- a/spec/unit/interface/action_manager_spec.rb +++ b/spec/unit/interface/action_manager_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' # This is entirely an internal class for Interface, so we have to load it instead of our class. require 'puppet/interface' +require 'puppet/face' class ActionManagerTester include Puppet::Interface::ActionManager @@ -103,6 +104,8 @@ describe Puppet::Interface::ActionManager do @klass = Class.new do include Puppet::Interface::ActionManager extend Puppet::Interface::ActionManager + def __invoke_decorations(*args) true end + def options() [] end end @instance = @klass.new end @@ -213,6 +216,23 @@ describe Puppet::Interface::ActionManager do end end + describe "#action" do + it 'should add an action' do + subject.action(:foo) { } + subject.get_action(:foo).should be_a Puppet::Interface::Action + end + + it 'should support default actions' do + subject.action(:foo) { default } + subject.get_default_action.should == subject.get_action(:foo) + end + + it 'should not support more than one default action' do + subject.action(:foo) { default } + expect { subject.action(:bar) { default } }.should raise_error + end + end + describe "#get_action" do let :parent_class do parent_class = Class.new(Puppet::Interface) diff --git a/spec/unit/interface/action_spec.rb b/spec/unit/interface/action_spec.rb index 8c6782976..0eb450ee2 100755 --- a/spec/unit/interface/action_spec.rb +++ b/spec/unit/interface/action_spec.rb @@ -66,8 +66,8 @@ describe Puppet::Interface::Action do let :face do Puppet::Interface.new(:ruby_api, '1.0.0') do action :bar do - when_invoked do |options| - options + when_invoked do |*args| + args.last end end end @@ -79,9 +79,14 @@ describe Puppet::Interface::Action do end it "should work when options are supplied" do - options = face.bar :bar => "beer" + options = face.bar(:bar => "beer") options.should == { :bar => "beer" } end + + it "should call #validate_args on the action when invoked" do + face.get_action(:bar).expects(:validate_args).with([1, :two, 'three', {}]) + face.bar 1, :two, 'three' + end end end @@ -168,5 +173,220 @@ describe Puppet::Interface::Action do end }.should raise_error ArgumentError, /Option foo conflicts with existing option foo/i end + + it "should fail when a required action option is not provided" do + face = Puppet::Interface.new(:required_action_option, '0.0.1') do + action(:bar) do + option('--foo') { required } + when_invoked { } + end + end + expect { face.bar }.to raise_error ArgumentError, /missing required options \(foo\)/ + end + + it "should fail when a required face option is not provided" do + face = Puppet::Interface.new(:required_face_option, '0.0.1') do + option('--foo') { required } + action(:bar) { when_invoked { } } + end + expect { face.bar }.to raise_error ArgumentError, /missing required options \(foo\)/ + end + end + + context "with action decorators" do + context "local only" do + let :face do + Puppet::Interface.new(:action_decorators, '0.0.1') do + action :bar do when_invoked do true end end + def report(arg) end + end + end + + it "should call action before decorators" do + face.action(:baz) do + option "--baz" do + before_action do |action, args, options| + report(:action_option) + end + end + when_invoked do true end + end + + face.expects(:report).with(:action_option) + face.baz :baz => true + end + + it "should call action after decorators" do + face.action(:baz) do + option "--baz" do + after_action do |action, args, options| + report(:action_option) + end + end + when_invoked do true end + end + + face.expects(:report).with(:action_option) + face.baz :baz => true + end + + it "should call local before decorators" do + face.option "--foo FOO" do + before_action do |action, args, options| + report(:before) + end + end + face.expects(:report).with(:before) + face.bar({:foo => 12}) + end + + it "should call local after decorators" do + face.option "--foo FOO" do + after_action do |action, args, options| report(:after) end + end + face.expects(:report).with(:after) + face.bar({:foo => 12}) + end + + context "with inactive decorators" do + it "should not invoke a decorator if the options are empty" do + face.option "--foo FOO" do + before_action do |action, args, options| + report :before_action + end + end + face.expects(:report).never # I am testing the negative. + face.bar + end + + context "with some decorators only" do + before :each do + face.option "--foo" do + before_action do |action, args, options| report :foo end + end + face.option "--bar" do + before_action do |action, args, options| report :bar end + end + end + + it "should work with the foo option" do + face.expects(:report).with(:foo) + face.bar(:foo => true) + end + + it "should work with the bar option" do + face.expects(:report).with(:bar) + face.bar(:bar => true) + end + + it "should work with both options" do + face.expects(:report).with(:foo) + face.expects(:report).with(:bar) + face.bar(:foo => true, :bar => true) + end + end + end + end + + context "with inherited decorators" do + let :parent do + parent = Class.new(Puppet::Interface) + parent.script :on_parent do :on_parent end + parent.define_method :report do |arg| arg end + parent + end + + let :child do + child = parent.new(:inherited_decorators, '0.0.1') do + script :on_child do :on_child end + end + end + + context "with a child decorator" do + subject do + child.option "--foo FOO" do + before_action do |action, args, options| + report(:child_before) + end + end + child.expects(:report).with(:child_before) + child + end + + it "child actions should invoke the decorator" do + subject.on_child({:foo => true, :bar => true}).should == :on_child + end + + it "parent actions should invoke the decorator" do + subject.on_parent({:foo => true, :bar => true}).should == :on_parent + end + end + + context "with a parent decorator" do + subject do + parent.option "--foo FOO" do + before_action do |action, args, options| + report(:parent_before) + end + end + child.expects(:report).with(:parent_before) + child + end + + it "child actions should invoke the decorator" do + subject.on_child({:foo => true, :bar => true}).should == :on_child + end + + it "parent actions should invoke the decorator" do + subject.on_parent({:foo => true, :bar => true}).should == :on_parent + end + end + + context "with child and parent decorators" do + subject do + parent.option "--foo FOO" do + before_action { |action, args, options| report(:parent_before) } + after_action { |action, args, options| report(:parent_after) } + end + child.option "--bar BAR" do + before_action { |action, args, options| report(:child_before) } + after_action { |action, args, options| report(:child_after) } + end + + child.expects(:report).with(:child_before) + child.expects(:report).with(:parent_before) + child.expects(:report).with(:parent_after) + child.expects(:report).with(:child_after) + + child + end + + it "child actions should invoke all the decorator" do + subject.on_child({:foo => true, :bar => true}).should == :on_child + end + + it "parent actions should invoke all the decorator" do + subject.on_parent({:foo => true, :bar => true}).should == :on_parent + end + end + end + end + + it_should_behave_like "documentation on faces" do + subject do + face = Puppet::Interface.new(:action_documentation, '0.0.1') do + action :documentation do end + end + face.get_action(:documentation) + end + end + + context "#when_rendering" do + it "should fail if no type is given when_rendering" + it "should accept a when_rendering block" + it "should accept multiple when_rendering blocks" + it "should fail if when_rendering gets a non-symbol identifier" + it "should fail if a second block is given for the same type" + it "should return the block if asked" end end diff --git a/spec/unit/interface/face_collection_spec.rb b/spec/unit/interface/face_collection_spec.rb index d1114dde7..f9498cbb8 100755 --- a/spec/unit/interface/face_collection_spec.rb +++ b/spec/unit/interface/face_collection_spec.rb @@ -24,10 +24,6 @@ describe Puppet::Interface::FaceCollection do $".clear ; @original_required.each do |item| $" << item end end - describe "::faces" do - it "REVISIT: should have some tests here, if we describe it" - end - describe "::validate_version" do it 'should permit three number versions' do subject.validate_version('10.10.10').should == true diff --git a/spec/unit/interface/option_builder_spec.rb b/spec/unit/interface/option_builder_spec.rb index fae48324e..e9346852c 100755 --- a/spec/unit/interface/option_builder_spec.rb +++ b/spec/unit/interface/option_builder_spec.rb @@ -8,22 +8,68 @@ describe Puppet::Interface::OptionBuilder do should be_an_instance_of Puppet::Interface::Option end - describe "when using the DSL block" do - it "should work with an empty block" do + it "should work with an empty block" do + option = Puppet::Interface::OptionBuilder.build(face, "--foo") do + # This block deliberately left blank. + end + + option.should be_an_instance_of Puppet::Interface::Option + end + + it "should support documentation declarations" do + text = "this is the description" + option = Puppet::Interface::OptionBuilder.build(face, "--foo") do + desc text + end + option.should be_an_instance_of Puppet::Interface::Option + option.desc.should == text + end + + context "before_action hook" do + it "should support a before_action hook" do option = Puppet::Interface::OptionBuilder.build(face, "--foo") do - # This block deliberately left blank. + before_action do |a,b,c| :whatever end end + option.before_action.should be_an_instance_of UnboundMethod + end - option.should be_an_instance_of Puppet::Interface::Option + it "should fail if the hook block takes too few arguments" do + expect do + Puppet::Interface::OptionBuilder.build(face, "--foo") do + before_action do |one, two| true end + end + end.to raise_error ArgumentError, /takes three arguments/ end - it "should support documentation declarations" do - text = "this is the description" - option = Puppet::Interface::OptionBuilder.build(face, "--foo") do - desc text + it "should fail if the hook block takes too many arguments" do + expect do + Puppet::Interface::OptionBuilder.build(face, "--foo") do + before_action do |one, two, three, four| true end + end + end.to raise_error ArgumentError, /takes three arguments/ + end + + it "should fail if the hook block takes a variable number of arguments" do + expect do + Puppet::Interface::OptionBuilder.build(face, "--foo") do + before_action do |*blah| true end + end + end.to raise_error ArgumentError, /takes three arguments/ + end + + it "should support simple required declarations" do + opt = Puppet::Interface::OptionBuilder.build(face, "--foo") do + required end - option.should be_an_instance_of Puppet::Interface::Option - option.desc.should == text + opt.should be_required end + + it "should support arguments to the required property" do + opt = Puppet::Interface::OptionBuilder.build(face, "--foo") do + required(false) + end + opt.should_not be_required + end + end end diff --git a/spec/unit/interface/option_spec.rb b/spec/unit/interface/option_spec.rb index 3bcd121e2..e77b46e79 100755 --- a/spec/unit/interface/option_spec.rb +++ b/spec/unit/interface/option_spec.rb @@ -1,3 +1,4 @@ +require 'puppet/interface' require 'puppet/interface/option' describe Puppet::Interface::Option do @@ -72,4 +73,28 @@ describe Puppet::Interface::Option do option.to_s.should == "foo-bar" end end + + %w{before after}.each do |side| + describe "#{side} hooks" do + subject { Puppet::Interface::Option.new(face, "--foo") } + let :proc do Proc.new do :from_proc end end + + it { should respond_to "#{side}_action" } + it { should respond_to "#{side}_action=" } + + it "should set the #{side}_action hook" do + subject.send("#{side}_action").should be_nil + subject.send("#{side}_action=", proc) + subject.send("#{side}_action").should be_an_instance_of UnboundMethod + end + + data = [1, "foo", :foo, Object.new, method(:hash), method(:hash).unbind] + data.each do |input| + it "should fail if a #{input.class} is added to the #{side} hooks" do + expect { subject.send("#{side}_action=", input) }. + to raise_error ArgumentError, /not a proc/ + end + end + end + end end diff --git a/spec/unit/interface_spec.rb b/spec/unit/interface_spec.rb index e52b45d8a..b4fef0307 100755 --- a/spec/unit/interface_spec.rb +++ b/spec/unit/interface_spec.rb @@ -33,7 +33,7 @@ describe Puppet::Interface do describe "#define" do it "should register the face" do - face = subject.define(:face_test_register, '0.0.1') + face = subject.define(:face_test_register, '0.0.1') face.should == subject[:face_test_register, '0.0.1'] end @@ -51,22 +51,16 @@ describe Puppet::Interface do subject.new(:foo, '1.0.0').should respond_to(:summary=).with(1).arguments end - it "should set the summary text" do - text = "hello, freddy, my little pal" - subject.define(:face_test_summary, '1.0.0') do - summary text - end - subject[:face_test_summary, '1.0.0'].summary.should == text - end - - it "should support mutating the summary" do - text = "hello, freddy, my little pal" - subject.define(:face_test_summary, '1.0.0') do - summary text + # Required documentation methods... + { :summary => "summary", + :description => "This is the description of the stuff\n\nWhee" + }.each do |attr, value| + it "should support #{attr} in the builder" do + face = subject.new(:builder, '1.0.0') do + self.send(attr, value) + end + face.send(attr).should == value end - subject[:face_test_summary, '1.0.0'].summary.should == text - subject[:face_test_summary, '1.0.0'].summary = text + text - subject[:face_test_summary, '1.0.0'].summary.should == text + text end end @@ -99,16 +93,6 @@ describe Puppet::Interface do subject.new(:me, '0.0.1').to_s.should =~ /\bme\b/ end - it "should allow overriding of the default format" do - face = subject.new(:me, '0.0.1') - face.set_default_format :foo - face.default_format.should == :foo - end - - it "should default to :pson for its format" do - subject.new(:me, '0.0.1').default_format.should == :pson - end - # Why? it "should create a class-level autoloader" do subject.autoloader.should be_instance_of(Puppet::Util::Autoload) @@ -204,4 +188,10 @@ describe Puppet::Interface do end end end + + it_should_behave_like "documentation on faces" do + subject do + Puppet::Interface.new(:face_documentation, '0.0.1') + end + end end diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index 83969c504..c709d82fe 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -240,34 +240,30 @@ describe Puppet::Network::HTTP::Handler do describe "when performing head operation" do before do - @irequest = stub 'indirection_request', :method => :head, :indirection_name => "my_handler", :to_hash => {}, :key => "my_result", :model => @model_class + @handler.stubs(:model).with("my_handler").returns(stub 'model', :indirection => @model_class) + @handler.stubs(:http_method).with(@request).returns("HEAD") + @handler.stubs(:path).with(@request).returns("/production/my_handler/my_result") + @handler.stubs(:params).with(@request).returns({}) @model_class.stubs(:head).returns true end - it "should use the indirection request to find the model class" do - @irequest.expects(:model).returns @model_class - - @handler.do_head(@irequest, @request, @response) - end - it "should use the escaped request key" do @model_class.expects(:head).with do |key, args| key == "my_result" end.returns true - @handler.do_head(@irequest, @request, @response) + @handler.process(@request, @response) end it "should not generate a response when a model head call succeeds" do @handler.expects(:set_response).never - @handler.do_head(@irequest, @request, @response) + @handler.process(@request, @response) end it "should return a 404 when the model head call returns false" do - @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } @model_class.stubs(:head).returns(false) - @handler.do_head(@irequest, @request, @response) + @handler.process(@request, @response) end end diff --git a/spec/unit/node/facts_spec.rb b/spec/unit/node/facts_spec.rb index a130ae3f8..07f5f7e1b 100755 --- a/spec/unit/node/facts_spec.rb +++ b/spec/unit/node/facts_spec.rb @@ -1,6 +1,6 @@ #!/usr/bin/env rspec require 'spec_helper' - +require 'matchers/json' require 'puppet/node/facts' describe Puppet::Node::Facts, "when indirecting" do @@ -110,7 +110,11 @@ describe Puppet::Node::Facts, "when indirecting" do end it "should accept properly formatted pson" do - pson = %Q({"name": "foo", "expiration": "#{@expiration}", "timestamp": "#{@timestamp}", "values": {"a": "1", "b": "2", "c": "3"}}) + facts = Puppet::Node::Facts.new("foo") + facts.values = {"a" => "1", "b" => "2", "c" => "3"} + facts.expiration = Time.now + #pson = %Q({"document_type": "Puppet::Node::Facts", "data: {"name": "foo", "expiration": "#{@expiration}", "timestamp": "#{@timestamp}", "values": {"a": "1", "b": "2", "c": "3"}}}) + pson = %Q({"data": {"name":"foo", "expiration":"#{@expiration}", "timestamp": "#{@timestamp}", "values":{"a":"1","b":"2","c":"3"}}, "document_type":"Puppet::Node::Facts"}) format = Puppet::Network::FormatHandler.format('pson') facts = format.intern(Puppet::Node::Facts,pson) facts.name.should == 'foo' @@ -122,8 +126,30 @@ describe Puppet::Node::Facts, "when indirecting" do Time.stubs(:now).returns(@timestamp) facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3}) facts.expiration = @expiration - pson = PSON.parse(facts.to_pson) - pson.should == {"name"=>"foo", "timestamp"=>@timestamp.to_s, "expiration"=>@expiration.to_s, "values"=>{"a"=>1, "b"=>2, "c"=>3}} + result = PSON.parse(facts.to_pson) + result.name.should == facts.name + result.values.should == facts.values + result.timestamp.should == facts.timestamp + result.expiration.should == facts.expiration + result.type.should == Puppet::Node::Facts + end + + it "should not include nil values" do + facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3}) + + # XXX:LAK For some reason this is resurrection the full instance, instead + # of just returning the hash. This code works, but I can't figure out what's + # going on. + newfacts = PSON.parse(facts.to_pson) + newfacts.expiration.should be_nil + end + + it "should be able to handle nil values" do + pson = %Q({"name": "foo", "values": {"a": "1", "b": "2", "c": "3"}}) + format = Puppet::Network::FormatHandler.format('pson') + facts = format.intern(Puppet::Node::Facts,pson) + facts.name.should == 'foo' + facts.expiration.should be_nil end end end diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index 7d813ba30..e8f826dca 100755 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -1,5 +1,6 @@ #!/usr/bin/env rspec require 'spec_helper' +require 'matchers/json' describe Puppet::Node do describe "when managing its environment" do @@ -35,6 +36,69 @@ describe Puppet::Node do node.environment.name.should == :bar end end + + describe "when converting to json" do + before do + @node = Puppet::Node.new("mynode") + end + + it "should provide its name" do + @node.should set_json_attribute('name').to("mynode") + end + + it "should include the classes if set" do + @node.classes = %w{a b c} + @node.should set_json_attribute("classes").to(%w{a b c}) + end + + it "should not include the classes if there are none" do + @node.should_not set_json_attribute('classes') + end + + it "should include parameters if set" do + @node.parameters = {"a" => "b", "c" => "d"} + @node.should set_json_attribute('parameters').to({"a" => "b", "c" => "d"}) + end + + it "should not include the parameters if there are none" do + @node.should_not set_json_attribute('parameters') + end + + it "should include the environment" do + @node.environment = "production" + @node.should set_json_attribute('environment').to('production') + end + end + + describe "when converting from json" do + before do + @node = Puppet::Node.new("mynode") + @format = Puppet::Network::FormatHandler.format('pson') + end + + def from_json(json) + @format.intern(Puppet::Node, json) + end + + it "should set its name" do + Puppet::Node.should read_json_attribute('name').from(@node.to_pson).as("mynode") + end + + it "should include the classes if set" do + @node.classes = %w{a b c} + Puppet::Node.should read_json_attribute('classes').from(@node.to_pson).as(%w{a b c}) + end + + it "should include parameters if set" do + @node.parameters = {"a" => "b", "c" => "d"} + Puppet::Node.should read_json_attribute('parameters').from(@node.to_pson).as({"a" => "b", "c" => "d"}) + end + + it "should include the environment" do + @node.environment = "production" + Puppet::Node.should read_json_attribute('environment').from(@node.to_pson).as(Puppet::Node::Environment.new(:production)) + end + end end describe Puppet::Node, "when initializing" do diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb index ced760b76..48aeb98dd 100755 --- a/spec/unit/parser/compiler_spec.rb +++ b/spec/unit/parser/compiler_spec.rb @@ -32,6 +32,14 @@ class CompilerTestResource def evaluate end + + def file + "/fake/file/goes/here" + end + + def line + "42" + end end describe Puppet::Parser::Compiler do @@ -413,52 +421,6 @@ describe Puppet::Parser::Compiler do @compiler.catalog.should be_edge(@scope.resource, resource) end - it "should add an edge to any specified stage for class resources" do - other_stage = resource(:stage, "other") - @compiler.add_resource(@scope, other_stage) - resource = resource(:class, "foo") - resource[:stage] = 'other' - - @compiler.add_resource(@scope, resource) - - @compiler.catalog.edge?(other_stage, resource).should be_true - end - - it "should fail if a non-class resource attempts to set a stage" do - other_stage = resource(:stage, "other") - @compiler.add_resource(@scope, other_stage) - resource = resource(:file, "foo") - resource[:stage] = 'other' - - lambda { @compiler.add_resource(@scope, resource) }.should raise_error(ArgumentError) - end - - it "should fail if an unknown stage is specified" do - resource = resource(:class, "foo") - resource[:stage] = 'other' - - lambda { @compiler.add_resource(@scope, resource) }.should raise_error(ArgumentError) - end - - it "should add edges from the class resources to the parent's stage if no stage is specified" do - main = @compiler.catalog.resource(:stage, :main) - foo_stage = resource(:stage, :foo_stage) - @compiler.add_resource(@scope, foo_stage) - resource = resource(:class, "foo") - @scope.stubs(:resource).returns(:stage => :foo_stage) - @compiler.add_resource(@scope, resource) - - @compiler.catalog.should be_edge(foo_stage, resource) - end - - it "should add edges from top-level class resources to the main stage if no stage is specified" do - main = @compiler.catalog.resource(:stage, :main) - resource = resource(:class, "foo") - @compiler.add_resource(@scope, resource) - - @compiler.catalog.should be_edge(main, resource) - end - it "should not add non-class resources that don't specify a stage to the 'main' stage" do main = @compiler.catalog.resource(:stage, :main) resource = resource(:file, "foo") diff --git a/spec/unit/parser/resource_spec.rb b/spec/unit/parser/resource_spec.rb index b03c18e5f..0d9cba60b 100755 --- a/spec/unit/parser/resource_spec.rb +++ b/spec/unit/parser/resource_spec.rb @@ -131,9 +131,19 @@ describe Puppet::Parser::Resource do end describe "when evaluating" do + before do + @node = Puppet::Node.new "test-node" + @compiler = Puppet::Parser::Compiler.new @node + @catalog = Puppet::Resource::Catalog.new + source = stub('source') + source.stubs(:module_name) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => source) + @catalog.add_resource(Puppet::Parser::Resource.new("stage", :main, :scope => @scope)) + end + it "should evaluate the associated AST definition" do definition = newdefine "mydefine" - res = Puppet::Parser::Resource.new("mydefine", "whatever", :scope => @scope, :source => @source) + res = Puppet::Parser::Resource.new("mydefine", "whatever", :scope => @scope, :source => @source, :catalog => @catalog) definition.expects(:evaluate_code).with(res) res.evaluate @@ -141,17 +151,65 @@ describe Puppet::Parser::Resource do it "should evaluate the associated AST class" do @class = newclass "myclass" - res = Puppet::Parser::Resource.new("class", "myclass", :scope => @scope, :source => @source) + res = Puppet::Parser::Resource.new("class", "myclass", :scope => @scope, :source => @source, :catalog => @catalog) @class.expects(:evaluate_code).with(res) res.evaluate end it "should evaluate the associated AST node" do nodedef = newnode("mynode") - res = Puppet::Parser::Resource.new("node", "mynode", :scope => @scope, :source => @source) + res = Puppet::Parser::Resource.new("node", "mynode", :scope => @scope, :source => @source, :catalog => @catalog) nodedef.expects(:evaluate_code).with(res) res.evaluate end + + it "should add an edge to any specified stage for class resources" do + @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", '') + + other_stage = Puppet::Parser::Resource.new(:stage, "other", :scope => @scope, :catalog => @catalog) + @compiler.add_resource(@scope, other_stage) + resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) + resource[:stage] = 'other' + @compiler.add_resource(@scope, resource) + + resource.evaluate + + @compiler.catalog.edge?(other_stage, resource).should be_true + end + + it "should fail if an unknown stage is specified" do + @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", '') + + resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) + resource[:stage] = 'other' + + lambda { resource.evaluate }.should raise_error(ArgumentError, /Could not find stage other specified by/) + end + + it "should add edges from the class resources to the parent's stage if no stage is specified" do + main = @compiler.catalog.resource(:stage, :main) + foo_stage = Puppet::Parser::Resource.new(:stage, :foo_stage, :scope => @scope, :catalog => @catalog) + @compiler.add_resource(@scope, foo_stage) + @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", '') + resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) + resource[:stage] = 'foo_stage' + @compiler.add_resource(@scope, resource) + + resource.evaluate + + @compiler.catalog.should be_edge(foo_stage, resource) + end + + it "should add edges from top-level class resources to the main stage if no stage is specified" do + main = @compiler.catalog.resource(:stage, :main) + @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", '') + resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) + @compiler.add_resource(@scope, resource) + + resource.evaluate + + @compiler.catalog.should be_edge(main, resource) + end end describe "when finishing" do diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index bf4d1e29e..5308856ed 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -121,7 +121,11 @@ describe Puppet::Parser::Scope do def create_class_scope(name) klass = newclass(name) - Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source')).evaluate + + catalog = Puppet::Resource::Catalog.new + catalog.add_resource(Puppet::Parser::Resource.new("stage", :main, :scope => Puppet::Parser::Scope.new)) + + Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source'), :catalog => catalog).evaluate @scope.class_scope(klass) end diff --git a/spec/unit/provider/cisco_spec.rb b/spec/unit/provider/cisco_spec.rb new file mode 100644 index 000000000..08320731c --- /dev/null +++ b/spec/unit/provider/cisco_spec.rb @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/cisco' + +describe Puppet::Provider::Cisco do + it "should implement a device class method" do + Puppet::Provider::Cisco.should respond_to(:device) + end + + it "should create a cisco device instance" do + Puppet::Util::NetworkDevice::Cisco::Device.expects(:new).returns :device + Puppet::Provider::Cisco.device(:url).should == :device + end +end
\ No newline at end of file diff --git a/spec/unit/provider/interface/cisco_spec.rb b/spec/unit/provider/interface/cisco_spec.rb index d1f70609f..c18f87cf8 100755 --- a/spec/unit/provider/interface/cisco_spec.rb +++ b/spec/unit/provider/interface/cisco_spec.rb @@ -8,12 +8,13 @@ provider_class = Puppet::Type.type(:interface).provider(:cisco) describe provider_class do before do + @device = stub_everything 'device' @resource = stub("resource", :name => "Fa0/1") - @provider = provider_class.new(@resource) + @provider = provider_class.new(@device, @resource) end - it "should have a parent of Puppet::Provider::NetworkDevice" do - provider_class.should < Puppet::Provider::NetworkDevice + it "should have a parent of Puppet::Provider::Cisco" do + provider_class.should < Puppet::Provider::Cisco end it "should have an instances method" do @@ -22,31 +23,24 @@ describe provider_class do describe "when looking up instances at prefetch" do before do - @device = stub_everything 'device' - Puppet::Util::NetworkDevice::Cisco::Device.stubs(:new).returns(@device) @device.stubs(:command).yields(@device) end - it "should initialize the network device with the given url" do - Puppet::Util::NetworkDevice::Cisco::Device.expects(:new).with(:url).returns(@device) - provider_class.lookup(:url, "Fa0/1") - end - it "should delegate to the device interface fetcher" do @device.expects(:interface) - provider_class.lookup("", "Fa0/1") + provider_class.lookup(@device, "Fa0/1") end it "should return the given interface data" do @device.expects(:interface).returns({ :description => "thisone", :mode => :access}) - provider_class.lookup("", "Fa0").should == {:description => "thisone", :mode => :access } + provider_class.lookup(@device, "Fa0").should == {:description => "thisone", :mode => :access } end end describe "when an instance is being flushed" do it "should call the device interface update method with current and past properties" do - @instance = provider_class.new(:ensure => :present, :name => "Fa0/1", :description => "myinterface") + @instance = provider_class.new(@device, :ensure => :present, :name => "Fa0/1", :description => "myinterface") @instance.description = "newdesc" @instance.resource = @resource @resource.stubs(:[]).with(:name).returns("Fa0/1") diff --git a/spec/unit/provider/network_device_spec.rb b/spec/unit/provider/network_device_spec.rb index 83d2bdc01..e2a87cf4e 100755 --- a/spec/unit/provider/network_device_spec.rb +++ b/spec/unit/provider/network_device_spec.rb @@ -3,10 +3,15 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/provider/network_device' +require 'ostruct' Puppet::Type.type(:vlan).provide :test, :parent => Puppet::Provider::NetworkDevice do mk_resource_methods - def self.lookup(device_url, name) + def self.lookup(device, name) + end + + def self.device(url) + :device end end @@ -34,7 +39,7 @@ describe provider_class do end it "should lookup an entry for each passed resource" do - provider_class.expects(:lookup).with(nil, "200").returns nil + provider_class.expects(:lookup).with{ |device,value| value == "200" }.returns nil provider_class.stubs(:new) @resource.stubs(:provider=) @@ -44,7 +49,7 @@ describe provider_class do describe "resources that do not exist" do it "should create a provider with :ensure => :absent" do provider_class.stubs(:lookup).returns(nil) - provider_class.expects(:new).with(:ensure => :absent).returns "myprovider" + provider_class.expects(:new).with(:device, :ensure => :absent).returns "myprovider" @resource.expects(:provider=).with("myprovider") provider_class.prefetch(@resources) end @@ -54,7 +59,7 @@ describe provider_class do it "should create a provider with the results of the find and ensure at present" do provider_class.stubs(:lookup).returns({ :name => "200", :description => "myvlan"}) - provider_class.expects(:new).with(:name => "200", :description => "myvlan", :ensure => :present).returns "myprovider" + provider_class.expects(:new).with(:device, :name => "200", :description => "myvlan", :ensure => :present).returns "myprovider" @resource.expects(:provider=).with("myprovider") provider_class.prefetch(@resources) @@ -74,7 +79,7 @@ describe provider_class do end it "should store a copy of the hash as its vlan_properties" do - instance = provider_class.new(:one => :two) + instance = provider_class.new(:device, :one => :two) instance.former_properties.should == {:one => :two} end end @@ -82,7 +87,7 @@ describe provider_class do describe "when an instance" do before do - @instance = provider_class.new + @instance = provider_class.new(:device) @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property @resource_class = stub 'resource_class', :attrclass => @property_class, :valid_parameter? => true, :validproperties => [:description] @@ -98,12 +103,12 @@ describe provider_class do end it "should indicate when the instance already exists" do - @instance = provider_class.new(:ensure => :present) + @instance = provider_class.new(:device, :ensure => :present) @instance.exists?.should be_true end it "should indicate when the instance does not exist" do - @instance = provider_class.new(:ensure => :absent) + @instance = provider_class.new(:device, :ensure => :absent) @instance.exists?.should be_false end diff --git a/spec/unit/provider/package/pkgutil_spec.rb b/spec/unit/provider/package/pkgutil_spec.rb new file mode 100755 index 000000000..5549b3f6d --- /dev/null +++ b/spec/unit/provider/package/pkgutil_spec.rb @@ -0,0 +1,182 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider = Puppet::Type.type(:package).provider(:pkgutil) + +describe provider do + before(:each) do + @resource = Puppet::Type.type(:package).new( + :name => "TESTpkg", + :ensure => :present, + :provider => :pkgutil + ) + @provider = provider.new(@resource) + + # Stub all file and config tests + provider.stubs(:healthcheck) + end + + it "should have an install method" do + @provider.should respond_to(:install) + end + + it "should have a latest method" do + @provider.should respond_to(:uninstall) + end + + it "should have an update method" do + @provider.should respond_to(:update) + end + + it "should have a latest method" do + @provider.should respond_to(:latest) + end + + describe "when installing" do + it "should use a command without versioned package" do + @resource[:ensure] = :latest + @provider.expects(:pkguti).with('-y', '-i', 'TESTpkg') + @provider.install + end + end + + describe "when updating" do + it "should use a command without versioned package" do + @provider.expects(:pkguti).with('-y', '-u', 'TESTpkg') + @provider.update + end + end + + describe "when uninstalling" do + it "should call the remove operation" do + @provider.expects(:pkguti).with('-y', '-r', 'TESTpkg') + @provider.uninstall + end + end + + describe "when getting latest version" do + it "should return TESTpkg's version string" do + fake_data = " +noisy output here +TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data + @provider.latest.should == "1.4.5,REV=2007.11.20" + end + + it "should handle TESTpkg's 'SAME' version string" do + fake_data = " +noisy output here +TESTpkg 1.4.5,REV=2007.11.18 SAME" + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data + @provider.latest.should == "1.4.5,REV=2007.11.18" + end + + it "should handle a non-existent package" do + fake_data = "noisy output here +Not in catalog" + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data + @provider.latest.should == nil + end + + it "should warn on unknown pkgutil noise" do + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns("testingnoise") + @provider.latest.should == nil + end + + it "should ignore pkgutil noise/headers to find TESTpkg" do + fake_data = "# stuff +=> Fetching new catalog and descriptions (http://mirror.opencsw.org/opencsw/unstable/i386/5.11) if available ... +2011-02-19 23:05:46 URL:http://mirror.opencsw.org/opencsw/unstable/i386/5.11/catalog [534635/534635] -> \"/var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11.tmp\" [1] +Checking integrity of /var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11 with gpg. +gpg: Signature made February 17, 2011 05:27:53 PM GMT using DSA key ID E12E9D2F +gpg: Good signature from \"Distribution Manager <dm@blastwave.org>\" +==> 2770 packages loaded from /var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11 +package installed catalog +TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data + @provider.latest.should == "1.4.5,REV=2007.11.20" + end + + it "should find REALpkg via an alias (TESTpkg)" do + fake_data = " +noisy output here +REALpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data + @provider.query[:name].should == "TESTpkg" + end + end + + describe "when querying current version" do + it "should return TESTpkg's version string" do + fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data + @provider.query[:ensure].should == "1.4.5,REV=2007.11.18" + end + + it "should handle a package that isn't installed" do + fake_data = "TESTpkg notinst 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data + @provider.query[:ensure].should == :absent + end + + it "should handle a non-existent package" do + fake_data = "noisy output here +Not in catalog" + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data + @provider.query[:ensure].should == :absent + end + end + + describe "when querying current instances" do + it "should warn on unknown pkgutil noise" do + provider.expects(:pkguti).with(['-a']).returns("testingnoise") + provider.expects(:pkguti).with(['-c']).returns("testingnoise") + Puppet.expects(:warning).times(2) + provider.expects(:new).never + provider.instances.should == [] + end + + it "should return TESTpkg's version string" do + fake_data = "TESTpkg TESTpkg 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-a']).returns fake_data + + fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-c']).returns fake_data + + testpkg = mock 'pkg1' + provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "TESTpkg", :provider => :pkgutil).returns testpkg + provider.instances.should == [testpkg] + end + + it "should also return both TESTpkg and mypkg alias instances" do + fake_data = "mypkg TESTpkg 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-a']).returns fake_data + + fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-c']).returns fake_data + + testpkg = mock 'pkg1' + provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "TESTpkg", :provider => :pkgutil).returns testpkg + + aliaspkg = mock 'pkg2' + provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "mypkg", :provider => :pkgutil).returns aliaspkg + + provider.instances.should == [testpkg,aliaspkg] + end + + it "shouldn't mind noise in the -a output" do + fake_data = "noisy output here" + provider.expects(:pkguti).with(['-a']).returns fake_data + + fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" + provider.expects(:pkguti).with(['-c']).returns fake_data + + testpkg = mock 'pkg1' + provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "TESTpkg", :provider => :pkgutil).returns testpkg + + provider.instances.should == [testpkg] + end + end + +end diff --git a/spec/unit/provider/vlan/cisco_spec.rb b/spec/unit/provider/vlan/cisco_spec.rb index bb243a75e..a67290eb4 100755 --- a/spec/unit/provider/vlan/cisco_spec.rb +++ b/spec/unit/provider/vlan/cisco_spec.rb @@ -8,12 +8,13 @@ provider_class = Puppet::Type.type(:vlan).provider(:cisco) describe provider_class do before do + @device = stub_everything 'device' @resource = stub("resource", :name => "200") - @provider = provider_class.new(@resource) + @provider = provider_class.new(@device, @resource) end - it "should have a parent of Puppet::Provider::NetworkDevice" do - provider_class.should < Puppet::Provider::NetworkDevice + it "should have a parent of Puppet::Provider::Cisco" do + provider_class.should < Puppet::Provider::Cisco end it "should have an instances method" do @@ -22,31 +23,24 @@ describe provider_class do describe "when looking up instances at prefetch" do before do - @device = stub_everything 'device' - Puppet::Util::NetworkDevice::Cisco::Device.stubs(:new).returns(@device) @device.stubs(:command).yields(@device) end - it "should initialize the network device with the given url" do - Puppet::Util::NetworkDevice::Cisco::Device.expects(:new).with(:url).returns(@device) - provider_class.lookup(:url, "200") - end - it "should delegate to the device vlans" do @device.expects(:parse_vlans) - provider_class.lookup("", "200") + provider_class.lookup(@device, "200") end it "should return only the given vlan" do @device.expects(:parse_vlans).returns({"200" => { :description => "thisone" }, "1" => { :description => "nothisone" }}) - provider_class.lookup("", "200").should == {:description => "thisone" } + provider_class.lookup(@device, "200").should == {:description => "thisone" } end end describe "when an instance is being flushed" do it "should call the device update_vlan method with its vlan id, current attributes, and desired attributes" do - @instance = provider_class.new(:ensure => :present, :name => "200", :description => "myvlan") + @instance = provider_class.new(@device, :ensure => :present, :name => "200", :description => "myvlan") @instance.description = "myvlan2" @instance.resource = @resource @resource.stubs(:[]).with(:name).returns("200") diff --git a/spec/unit/resource/catalog_spec.rb b/spec/unit/resource/catalog_spec.rb index ae65aa91a..ebf523eab 100755 --- a/spec/unit/resource/catalog_spec.rb +++ b/spec/unit/resource/catalog_spec.rb @@ -592,6 +592,7 @@ describe Puppet::Resource::Catalog, "when compiling" do Puppet::Transaction.stubs(:new).returns(@transaction) @transaction.stubs(:evaluate) @transaction.stubs(:add_times) + @transaction.stubs(:for_network_device=) Puppet.settings.stubs(:use) end diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb index 4157e58ac..d7788c06c 100755 --- a/spec/unit/transaction_spec.rb +++ b/spec/unit/transaction_spec.rb @@ -270,6 +270,24 @@ describe Puppet::Transaction do @resource.stubs(:virtual?).returns true @transaction.should be_skip(@resource) end + + it "should skip device only resouce on normal host" do + @resource.stubs(:appliable_to_device?).returns true + @transaction.for_network_device = false + @transaction.should be_skip(@resource) + end + + it "should not skip device only resouce on remote device" do + @resource.stubs(:appliable_to_device?).returns true + @transaction.for_network_device = true + @transaction.should_not be_skip(@resource) + end + + it "should skip host resouce on device" do + @resource.stubs(:appliable_to_device?).returns false + @transaction.for_network_device = true + @transaction.should be_skip(@resource) + end end describe "when determining if tags are missing" do diff --git a/spec/unit/type/interface_spec.rb b/spec/unit/type/interface_spec.rb index 68f4c765f..12ba225d9 100755 --- a/spec/unit/type/interface_spec.rb +++ b/spec/unit/type/interface_spec.rb @@ -3,6 +3,7 @@ require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Type.type(:interface) do + it "should have a 'name' parameter'" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1")[:name].should == "FastEthernet 0/1" end @@ -15,6 +16,10 @@ describe Puppet::Type.type(:interface) do Puppet::Type.type(:interface).attrtype(:ensure).should == :property end + it "should be applied on device" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1").should be_appliable_to_device + end + [:description, :speed, :duplex, :native_vlan, :encapsulation, :mode, :allowed_trunk_vlans, :etherchannel, :ipaddress].each do |p| it "should have a #{p} property" do Puppet::Type.type(:interface).attrtype(p).should == :property diff --git a/spec/unit/type/schedule_spec.rb b/spec/unit/type/schedule_spec.rb index 7599411e4..33064ca6f 100755 --- a/spec/unit/type/schedule_spec.rb +++ b/spec/unit/type/schedule_spec.rb @@ -36,6 +36,14 @@ describe Puppet::Type.type(:schedule) do describe Puppet::Type.type(:schedule) do include ScheduleTesting + it "should apply to device" do + @schedule.should be_appliable_to_device + end + + it "should apply to host" do + @schedule.should be_appliable_to_host + end + it "should default to :distance for period-matching" do @schedule[:periodmatch].should == :distance end diff --git a/spec/unit/type/vlan_spec.rb b/spec/unit/type/vlan_spec.rb index 2983a58e9..3bee14bbd 100755 --- a/spec/unit/type/vlan_spec.rb +++ b/spec/unit/type/vlan_spec.rb @@ -3,6 +3,7 @@ require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Type.type(:vlan) do + it "should have a 'name' parameter'" do Puppet::Type.type(:vlan).new(:name => "200")[:name].should == "200" end @@ -11,6 +12,10 @@ describe Puppet::Type.type(:vlan) do Puppet::Type.type(:vlan).new(:name => "200", :device_url => :device)[:device_url].should == :device end + it "should be applied on device" do + Puppet::Type.type(:vlan).new(:name => "200").should be_appliable_to_device + end + it "should have an ensure property" do Puppet::Type.type(:vlan).attrtype(:ensure).should == :property end diff --git a/spec/unit/util/network_device/cisco/device_spec.rb b/spec/unit/util/network_device/cisco/device_spec.rb index 82b0666e6..33242a1ab 100755 --- a/spec/unit/util/network_device/cisco/device_spec.rb +++ b/spec/unit/util/network_device/cisco/device_spec.rb @@ -392,130 +392,17 @@ eos @cisco.parse_interface_config("Gi0/17").should == {:etherchannel=>"1"} end end + + describe "when finding device facts" do + it "should delegate to the cisco facts entity" do + facts = stub 'facts' + Puppet::Util::NetworkDevice::Cisco::Facts.expects(:new).returns(facts) + + facts.expects(:retrieve).returns(:facts) + + @cisco.facts.should == :facts + end + end + end -# static access -# Switch#sh interfaces FastEthernet 0/1 switchport -# Name: Fa0/1 -# Switchport: Enabled -# Administrative mode: static access -# Operational Mode: static access -# Administrative Trunking Encapsulation: isl -# Operational Trunking Encapsulation: isl -# Negotiation of Trunking: Disabled -# Access Mode VLAN: 100 (SHDSL) -# Trunking Native Mode VLAN: 1 (default) -# Trunking VLANs Enabled: NONE -# Pruning VLANs Enabled: NONE -# -# Priority for untagged frames: 0 -# Override vlan tag priority: FALSE -# Voice VLAN: none -# Appliance trust: none -# Self Loopback: No -# Switch# - -# c2960#sh interfaces GigabitEthernet 0/1 switchport -# Name: Gi0/1 -# Switchport: Enabled -# Administrative Mode: trunk -# Operational Mode: trunk -# Administrative Trunking Encapsulation: dot1q -# Operational Trunking Encapsulation: dot1q -# Negotiation of Trunking: On -# Access Mode VLAN: 1 (default) -# Trunking Native Mode VLAN: 1 (default) -# Administrative Native VLAN tagging: enabled -# Voice VLAN: none -# Administrative private-vlan host-association: none -# Administrative private-vlan mapping: none -# Administrative private-vlan trunk native VLAN: none -# Administrative private-vlan trunk Native VLAN tagging: enabled -# Administrative private-vlan trunk encapsulation: dot1q -# Administrative private-vlan trunk normal VLANs: none -# Administrative private-vlan trunk associations: none -# Administrative private-vlan trunk mappings: none -# Operational private-vlan: none -# Trunking VLANs Enabled: 1,99 -# Pruning VLANs Enabled: 2-1001 -# Capture Mode Disabled -# Capture VLANs Allowed: ALL -# -# Protected: false -# Unknown unicast blocked: disabled -# Unknown multicast blocked: disabled -# Appliance trust: none -# c2960# - -# c2960#sh interfaces GigabitEthernet 0/2 switchport -# Name: Gi0/2 -# Switchport: Enabled -# Administrative Mode: static access -# Operational Mode: static access -# Administrative Trunking Encapsulation: dot1q -# Operational Trunking Encapsulation: native -# Negotiation of Trunking: Off -# Access Mode VLAN: 99 (MGMT) -# Trunking Native Mode VLAN: 1 (default) -# Administrative Native VLAN tagging: enabled -# Voice VLAN: none -# Administrative private-vlan host-association: none -# Administrative private-vlan mapping: none -# Administrative private-vlan trunk native VLAN: none -# Administrative private-vlan trunk Native VLAN tagging: enabled -# Administrative private-vlan trunk encapsulation: dot1q -# Administrative private-vlan trunk normal VLANs: none -# Administrative private-vlan trunk associations: none -# Administrative private-vlan trunk mappings: none -# Operational private-vlan: none -# Trunking VLANs Enabled: ALL -# Pruning VLANs Enabled: 2-1001 -# Capture Mode Disabled -# Capture VLANs Allowed: ALL -# -# Protected: false -# Unknown unicast blocked: disabled -# Unknown multicast blocked: disabled -# Appliance trust: none -# c2960# - -# c877#sh interfaces FastEthernet 1 switchport -# Name: Fa1 -# Switchport: Enabled -# Administrative Mode: trunk -# Operational Mode: trunk -# Administrative Trunking Encapsulation: dot1q -# Operational Trunking Encapsulation: dot1q -# Negotiation of Trunking: Disabled -# Access Mode VLAN: 0 ((Inactive)) -# Trunking Native Mode VLAN: 1 (default) -# Trunking VLANs Enabled: ALL -# Trunking VLANs Active: 1 -# Protected: false -# Priority for untagged frames: 0 -# Override vlan tag priority: FALSE -# Voice VLAN: none -# Appliance trust: none - - -# c2960#sh etherchannel summary -# Flags: D - down P - bundled in port-channel -# I - stand-alone s - suspended -# H - Hot-standby (LACP only) -# R - Layer3 S - Layer2 -# U - in use f - failed to allocate aggregator -# -# M - not in use, minimum links not met -# u - unsuitable for bundling -# w - waiting to be aggregated -# d - default port -# -# -# Number of channel-groups in use: 1 -# Number of aggregators: 1 -# -# Group Port-channel Protocol Ports -# ------+-------------+-----------+----------------------------------------------- -# 1 Po1(SU) LACP Gi0/17(P) Gi0/18(P) -# -# c2960# diff --git a/spec/unit/util/network_device/cisco/facts_spec.rb b/spec/unit/util/network_device/cisco/facts_spec.rb new file mode 100644 index 000000000..bb29ac292 --- /dev/null +++ b/spec/unit/util/network_device/cisco/facts_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../../spec_helper' + +require 'puppet/util/network_device' +require 'puppet/util/network_device/cisco/facts' + +describe Puppet::Util::NetworkDevice::Cisco::Facts do + before(:each) do + @transport = stub_everything 'transport' + @facts = Puppet::Util::NetworkDevice::Cisco::Facts.new(@transport) + end + + { + "cisco WS-C2924C-XL (PowerPC403GA) processor (revision 0x11) with 8192K/1024K bytes of memory." => {:hardwaremodel => "WS-C2924C-XL", :memorysize => "8192K", :processor => "PowerPC403GA", :hardwarerevision => "0x11" }, + "Cisco 1841 (revision 5.0) with 355328K/37888K bytes of memory." => {:hardwaremodel=>"1841", :memorysize => "355328K", :hardwarerevision => "5.0" }, + "Cisco 877 (MPC8272) processor (revision 0x200) with 118784K/12288K bytes of memory." => {:hardwaremodel=>"877", :memorysize => "118784K", :processor => "MPC8272", :hardwarerevision => "0x200" }, + "cisco WS-C2960G-48TC-L (PowerPC405) processor (revision C0) with 61440K/4088K bytes of memory." => {:hardwaremodel=>"WS-C2960G-48TC-L", :memorysize => "61440K", :processor => "PowerPC405", :hardwarerevision => "C0" }, + "cisco WS-C2950T-24 (RC32300) processor (revision R0) with 19959K bytes of memory." => {:hardwaremodel=>"WS-C2950T-24", :memorysize => "19959K", :processor => "RC32300", :hardwarerevision => "R0" } + }.each do |ver, expected| + it "should parse show ver output for hardware device facts" do + @transport.stubs(:command).with("sh ver").returns(<<eos) +Switch>sh ver +#{ver} +Switch> +eos + @facts.parse_show_ver.should == expected + end + end + + { +"Switch uptime is 1 year, 12 weeks, 6 days, 22 hours, 32 minutes" => { :hostname => "Switch", :uptime => "1 year, 12 weeks, 6 days, 22 hours, 32 minutes", :uptime_seconds => 39393120, :uptime_days => 455 }, +"c2950 uptime is 3 weeks, 1 day, 23 hours, 36 minutes" => { :hostname => "c2950", :uptime => "3 weeks, 1 day, 23 hours, 36 minutes", :uptime_days => 22, :uptime_seconds => 1985760}, +"router uptime is 5 weeks, 1 day, 3 hours, 30 minutes" => { :hostname => "router", :uptime => "5 weeks, 1 day, 3 hours, 30 minutes", :uptime_days => 36, :uptime_seconds => 3123000 }, +"c2950 uptime is 1 minute" => { :hostname => "c2950", :uptime => "1 minute", :uptime_days => 0, :uptime_seconds => 60 } + }.each do |ver, expected| + it "should parse show ver output for device uptime facts" do + @transport.stubs(:command).with("sh ver").returns(<<eos) +Switch>sh ver +#{ver} +Switch> +eos + @facts.parse_show_ver.should == expected + end + end + + { +"IOS (tm) C2900XL Software (C2900XL-C3H2S-M), Version 12.0(5)WC10, RELEASE SOFTWARE (fc1)"=> { :operatingsystem => "IOS", :operatingsystemrelease => "12.0(5)WC10", :operatingsystemmajrelease => "12.0WC", :operatingsystemfeature => "C3H2S"}, +"IOS (tm) C2950 Software (C2950-I6K2L2Q4-M), Version 12.1(22)EA8a, RELEASE SOFTWARE (fc1)"=> { :operatingsystem => "IOS", :operatingsystemrelease => "12.1(22)EA8a", :operatingsystemmajrelease => "12.1EA", :operatingsystemfeature => "I6K2L2Q4"}, +"Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 12.2(44)SE, RELEASE SOFTWARE (fc1)"=>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.2(44)SE", :operatingsystemmajrelease => "12.2SE", :operatingsystemfeature => "LANBASEK9"}, +"Cisco IOS Software, C870 Software (C870-ADVIPSERVICESK9-M), Version 12.4(11)XJ4, RELEASE SOFTWARE (fc2)"=>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.4(11)XJ4", :operatingsystemmajrelease => "12.4XJ", :operatingsystemfeature => "ADVIPSERVICESK9"}, +"Cisco IOS Software, 1841 Software (C1841-ADVSECURITYK9-M), Version 12.4(24)T4, RELEASE SOFTWARE (fc2)" =>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.4(24)T4", :operatingsystemmajrelease => "12.4T", :operatingsystemfeature => "ADVSECURITYK9"}, + }.each do |ver, expected| + it "should parse show ver output for device software version facts" do + @transport.stubs(:command).with("sh ver").returns(<<eos) +Switch>sh ver +#{ver} +Switch> +eos + @facts.parse_show_ver.should == expected + end + end +end diff --git a/spec/unit/util/network_device/config_spec.rb b/spec/unit/util/network_device/config_spec.rb new file mode 100644 index 000000000..52796f30b --- /dev/null +++ b/spec/unit/util/network_device/config_spec.rb @@ -0,0 +1,102 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/util/network_device/config' + +describe Puppet::Util::NetworkDevice::Config do + before(:each) do + Puppet[:deviceconfig] = "/dummy" + FileTest.stubs(:exists?).with("/dummy").returns(true) + end + + describe "when initializing" do + before :each do + Puppet::Util::NetworkDevice::Config.any_instance.stubs(:read) + end + + it "should use the deviceconfig setting as pathname" do + Puppet.expects(:[]).with(:deviceconfig).returns("/dummy") + + Puppet::Util::NetworkDevice::Config.new + end + + it "should raise an error if no file is defined finally" do + Puppet.expects(:[]).with(:deviceconfig).returns(nil) + + lambda { Puppet::Util::NetworkDevice::Config.new }.should raise_error(Puppet::DevError) + end + + it "should read and parse the file" do + Puppet::Util::NetworkDevice::Config.any_instance.expects(:read) + + Puppet::Util::NetworkDevice::Config.new + end + end + + describe "when parsing device" do + before :each do + @config = Puppet::Util::NetworkDevice::Config.new + @config.stubs(:changed?).returns(true) + @fd = stub 'fd' + File.stubs(:open).yields(@fd) + end + + it "should skip comments" do + @fd.stubs(:each).yields(' # comment') + + OpenStruct.expects(:new).never + + @config.read + end + + it "should increment line number even on commented lines" do + @fd.stubs(:each).multiple_yields(' # comment','[router.puppetlabs.com]') + + @config.read + @config.devices.should be_include('router.puppetlabs.com') + end + + it "should skip blank lines" do + @fd.stubs(:each).yields(' ') + + @config.read + @config.devices.should be_empty + end + + it "should produce the correct line number" do + @fd.stubs(:each).multiple_yields(' ', '[router.puppetlabs.com]') + + @config.read + @config.devices['router.puppetlabs.com'].line.should == 2 + end + + it "should throw an error if the current device already exists" do + @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', '[router.puppetlabs.com]') + + lambda { @config.read }.should raise_error + end + + it "should create a new device for each found device line" do + @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', '[swith.puppetlabs.com]') + + @config.read + @config.devices.size.should == 2 + end + + it "should parse the device type" do + @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', 'type cisco') + + @config.read + @config.devices['router.puppetlabs.com'].provider.should == 'cisco' + end + + it "should parse the device url" do + @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', 'type cisco', 'url ssh://test/') + + @config.read + @config.devices['router.puppetlabs.com'].url.should == 'ssh://test/' + end + end + +end
\ No newline at end of file diff --git a/spec/unit/util/network_device/transport/ssh_spec.rb b/spec/unit/util/network_device/transport/ssh_spec.rb index 0e91ed9f9..18d22a953 100755 --- a/spec/unit/util/network_device/transport/ssh_spec.rb +++ b/spec/unit/util/network_device/transport/ssh_spec.rb @@ -30,6 +30,14 @@ describe Puppet::Util::NetworkDevice::Transport::Ssh, :if => Puppet.features.ssh @transport.connect end + it "should raise a Puppet::Error when encountering an authentication failure" do + Net::SSH.expects(:start).raises Net::SSH::AuthenticationFailed + @transport.host = "localhost" + @transport.user = "user" + + lambda { @transport.connect }.should raise_error Puppet::Error + end + describe "when connected" do before(:each) do @ssh = stub_everything 'ssh' diff --git a/spec/unit/util/network_device_spec.rb b/spec/unit/util/network_device_spec.rb new file mode 100644 index 000000000..0f7c6036b --- /dev/null +++ b/spec/unit/util/network_device_spec.rb @@ -0,0 +1,50 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'ostruct' +require 'puppet/util/network_device' + +describe Puppet::Util::NetworkDevice do + + before(:each) do + @device = OpenStruct.new(:name => "name", :provider => "test") + end + + after(:each) do + Puppet::Util::NetworkDevice.teardown + end + + class Puppet::Util::NetworkDevice::Test + class Device + def initialize(device) + end + end + end + + describe "when initializing the remote network device singleton" do + it "should load the network device code" do + Puppet::Util::NetworkDevice.expects(:require) + Puppet::Util::NetworkDevice.init(@device) + end + + it "should create a network device instance" do + Puppet::Util::NetworkDevice.stubs(:require) + Puppet::Util::NetworkDevice::Test::Device.expects(:new) + Puppet::Util::NetworkDevice.init(@device) + end + + it "should raise an error if the remote device instance can't be created" do + Puppet::Util::NetworkDevice.stubs(:require).raises("error") + lambda { Puppet::Util::NetworkDevice.init(@device) }.should raise_error + end + + it "should let caller to access the singleton device" do + device = stub 'device' + Puppet::Util::NetworkDevice.stubs(:require) + Puppet::Util::NetworkDevice::Test::Device.expects(:new).returns(device) + Puppet::Util::NetworkDevice.init(@device) + + Puppet::Util::NetworkDevice.current.should == device + end + end +end diff --git a/spec/unit/util/queue/stomp_spec.rb b/spec/unit/util/queue/stomp_spec.rb index f67189cf5..99c77d0b4 100755 --- a/spec/unit/util/queue/stomp_spec.rb +++ b/spec/unit/util/queue/stomp_spec.rb @@ -63,26 +63,26 @@ describe 'Puppet::Util::Queue::Stomp', :if => Puppet.features.stomp? do end end - describe "when sending a message" do + describe "when publishing a message" do before do @client = stub 'client' Stomp::Client.stubs(:new).returns @client @queue = Puppet::Util::Queue::Stomp.new end - it "should send it to the queue client instance" do - @client.expects(:send).with { |queue, msg, options| msg == "Smite!" } - @queue.send_message('fooqueue', 'Smite!') + it "should publish it to the queue client instance" do + @client.expects(:publish).with { |queue, msg, options| msg == "Smite!" } + @queue.publish_message('fooqueue', 'Smite!') end - it "should send it to the transformed queue name" do - @client.expects(:send).with { |queue, msg, options| queue == "/queue/fooqueue" } - @queue.send_message('fooqueue', 'Smite!') + it "should publish it to the transformed queue name" do + @client.expects(:publish).with { |queue, msg, options| queue == "/queue/fooqueue" } + @queue.publish_message('fooqueue', 'Smite!') end - it "should send it as a persistent message" do - @client.expects(:send).with { |queue, msg, options| options[:persistent] == true } - @queue.send_message('fooqueue', 'Smite!') + it "should publish it as a persistent message" do + @client.expects(:publish).with { |queue, msg, options| options[:persistent] == true } + @queue.publish_message('fooqueue', 'Smite!') end end diff --git a/spec/watchr.rb b/spec/watchr.rb index 26919d1a1..c0f1d0257 100755 --- a/spec/watchr.rb +++ b/spec/watchr.rb @@ -85,7 +85,11 @@ def run_spec_files(files) else opts = File.readlines('spec/spec.opts').collect { |l| l.chomp }.join(" ") end - run_spec("rspec #{opts} --tty #{files.join(' ')}") + begin + run_spec("rspec #{opts} --tty #{files.join(' ')}") + rescue => detail + puts "Failed to load #{files}: #{detail}" + end end def run_all_tests |
