diff options
author | Stig Sandbeck Mathisen <ssm@debian.org> | 2012-01-26 11:22:40 +0100 |
---|---|---|
committer | Stig Sandbeck Mathisen <ssm@debian.org> | 2012-01-26 11:22:40 +0100 |
commit | c17b3ba16e7013f06416f10b8752ef783f048717 (patch) | |
tree | 790f13f167199b954007e17d1c55a8d1b0218775 /spec/unit | |
parent | 32af6143486ceb24a93636445d1883f5fe2299d7 (diff) | |
download | puppet-upstream/2.7.10.tar.gz |
Imported Upstream version 2.7.10upstream/2.7.10
Diffstat (limited to 'spec/unit')
76 files changed, 3662 insertions, 703 deletions
diff --git a/spec/unit/agent/disabler_spec.rb b/spec/unit/agent/disabler_spec.rb new file mode 100644 index 000000000..5e43cf964 --- /dev/null +++ b/spec/unit/agent/disabler_spec.rb @@ -0,0 +1,60 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/agent' +require 'puppet/agent/locker' + +class LockerTester + include Puppet::Agent::Disabler +end + +describe Puppet::Agent::Disabler do + before do + @locker = LockerTester.new + @locker.stubs(:lockfile_path).returns "/my/lock" + end + + it "should use an AnonymousFilelock instance as its disable_lockfile" do + @locker.disable_lockfile.should be_instance_of(Puppet::Util::AnonymousFilelock) + end + + it "should use 'lockfile_path' to determine its disable_lockfile path" do + @locker.expects(:lockfile_path).returns "/my/lock" + lock = Puppet::Util::AnonymousFilelock.new("/my/lock") + Puppet::Util::AnonymousFilelock.expects(:new).with("/my/lock.disabled").returns lock + + @locker.disable_lockfile + end + + it "should reuse the same lock file each time" do + @locker.disable_lockfile.should equal(@locker.disable_lockfile) + end + + it "should lock the anonymous lock when disabled" do + @locker.disable_lockfile.expects(:lock) + + @locker.disable + end + + it "should disable with a message" do + @locker.disable_lockfile.expects(:lock).with("disabled because") + + @locker.disable("disabled because") + end + + it "should unlock the anonymous lock when enabled" do + @locker.disable_lockfile.expects(:unlock) + + @locker.enable + end + + it "should check the lock if it is disabled" do + @locker.disable_lockfile.expects(:locked?) + + @locker.disabled? + end + + it "should report the disable message when disabled" do + @locker.disable_lockfile.expects(:message).returns("message") + @locker.disable_message.should == "message" + end +end diff --git a/spec/unit/agent/locker_spec.rb b/spec/unit/agent/locker_spec.rb index 341859e3b..9b530c0d8 100755 --- a/spec/unit/agent/locker_spec.rb +++ b/spec/unit/agent/locker_spec.rb @@ -29,18 +29,6 @@ describe Puppet::Agent::Locker do @locker.lockfile.should equal(@locker.lockfile) end - it "should use the lock file to anonymously lock the process when disabled" do - @locker.lockfile.expects(:lock).with(:anonymous => true) - - @locker.disable - end - - it "should use the lock file to anonymously unlock the process when enabled" do - @locker.lockfile.expects(:unlock).with(:anonymous => true) - - @locker.enable - end - it "should have a method that yields when a lock is attained" do @locker.lockfile.expects(:lock).returns true diff --git a/spec/unit/agent_spec.rb b/spec/unit/agent_spec.rb index d955868a0..9e2840c58 100755 --- a/spec/unit/agent_spec.rb +++ b/spec/unit/agent_spec.rb @@ -24,6 +24,7 @@ describe Puppet::Agent do # So we don't actually try to hit the filesystem. @agent.stubs(:lock).yields + @agent.stubs(:disabled?).returns(false) # make Puppet::Application safe for stubbing; restore in an :after block; silence warnings for this. without_warnings { Puppet::Application = Class.new(Puppet::Application) } @@ -76,6 +77,7 @@ describe Puppet::Agent do describe "when being run" do before do + AgentTestClient.stubs(:lockfile_path).returns "/my/lock" @agent.stubs(:running?).returns false end @@ -92,6 +94,12 @@ describe Puppet::Agent do @agent.run end + it "should do nothing if disabled" do + @agent.expects(:disabled?).returns(true) + AgentTestClient.expects(:new).never + @agent.run + end + it "should use Puppet::Application.controlled_run to manage process state behavior" do calls = sequence('calls') Puppet::Application.expects(:controlled_run).yields.in_sequence(calls) diff --git a/spec/unit/application/agent_spec.rb b/spec/unit/application/agent_spec.rb index 6d31ec34a..13be1a5af 100755 --- a/spec/unit/application/agent_spec.rb +++ b/spec/unit/application/agent_spec.rb @@ -91,7 +91,7 @@ describe Puppet::Application::Agent do @puppetd.command_line.stubs(:args).returns([]) end - [:centrallogging, :disable, :enable, :debug, :fqdn, :test, :verbose, :digest].each do |option| + [:centrallogging, :enable, :debug, :fqdn, :test, :verbose, :digest].each do |option| it "should declare handle_#{option} method" do @puppetd.should respond_to("handle_#{option}".to_sym) end @@ -102,6 +102,24 @@ describe Puppet::Application::Agent do end end + describe "when handling --disable" do + it "should declare handle_disable method" do + @puppetd.should respond_to(:handle_disable) + end + + it "should set disable to true" do + @puppetd.options.stubs(:[]=) + @puppetd.options.expects(:[]=).with(:disable, true) + @puppetd.handle_disable('') + end + + it "should store disable message" do + @puppetd.options.stubs(:[]=) + @puppetd.options.expects(:[]=).with(:disable_message, "message") + @puppetd.handle_disable('message') + end + end + it "should set an existing handler on server" do Puppet::Network::Handler.stubs(:handler).with("handler").returns(true) @@ -349,6 +367,20 @@ describe Puppet::Application::Agent do end end + it "should pass the disable message when disabling" do + @puppetd.options.stubs(:[]).with(:disable).returns(true) + @puppetd.options.stubs(:[]).with(:disable_message).returns("message") + @agent.expects(:disable).with("message") + expect { @puppetd.enable_disable_client(@agent) }.to exit_with 0 + end + + it "should pass the default disable message when disabling without a message" do + @puppetd.options.stubs(:[]).with(:disable).returns(true) + @puppetd.options.stubs(:[]).with(:disable_message).returns(nil) + @agent.expects(:disable).with("reason not specified") + expect { @puppetd.enable_disable_client(@agent) }.to exit_with 0 + end + it "should finally exit" do expect { @puppetd.enable_disable_client(@agent) }.to exit_with 0 end @@ -437,6 +469,32 @@ describe Puppet::Application::Agent do @puppetd.setup_listen end end + + describe "when setting up for fingerprint" do + before(:each) do + @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) + end + + it "should not setup as an agent" do + @puppetd.expects(:setup_agent).never + @puppetd.setup + end + + it "should not create an agent" do + Puppet::Agent.stubs(:new).with(Puppet::Configurer).never + @puppetd.setup + end + + it "should not daemonize" do + @daemon.expects(:daemonize).never + @puppetd.setup + end + + it "should setup our certificate host" do + @puppetd.expects(:setup_host) + @puppetd.setup + end + end end @@ -497,6 +555,11 @@ describe Puppet::Application::Agent do expect { @puppetd.onetime }.to exit_with 0 end + it "should stop the daemon" do + @daemon.expects(:stop).with(:exit => false) + expect { @puppetd.onetime }.to exit_with 0 + end + describe "and --detailed-exitcodes" do before :each do @puppetd.options.stubs(:[]).with(:detailed_exitcodes).returns(true) diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index b12c4fae9..07a39223c 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -20,7 +20,7 @@ describe Puppet::Application::Apply do Puppet::Node.indirection.cache_class = nil end - [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes].each do |option| + [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes,:catalog].each do |option| it "should declare handle_#{option} method" do @apply.should respond_to("handle_#{option}".to_sym) end @@ -53,6 +53,17 @@ describe Puppet::Application::Apply do @apply.handle_logdest("console") end + + it "should deprecate --apply" do + Puppet.expects(:warning).with do |arg| + arg.match(/--apply is deprecated/) + end + + command_line = Puppet::Util::CommandLine.new('puppet', ['apply', '--apply', 'catalog.json']) + apply = Puppet::Application::Apply.new(command_line) + apply.stubs(:run_command) + apply.run + end end describe "during setup" do diff --git a/spec/unit/application/cert_spec.rb b/spec/unit/application/cert_spec.rb index 300234c2b..8f0021ae6 100755 --- a/spec/unit/application/cert_spec.rb +++ b/spec/unit/application/cert_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' require 'puppet/application/cert' -describe Puppet::Application::Cert, :'fails_on_ruby_1.9.2' => true do +describe Puppet::Application::Cert => true do before :each do @cert_app = Puppet::Application[:cert] Puppet::Util::Log.stubs(:newdestination) diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb index fd93ceb00..591358efb 100755 --- a/spec/unit/application_spec.rb +++ b/spec/unit/application_spec.rb @@ -8,6 +8,7 @@ require 'getoptlong' describe Puppet::Application do before do + Puppet::Util::Instrumentation.stubs(:init) @app = Class.new(Puppet::Application).new @appclass = @app.class @@ -117,6 +118,11 @@ describe Puppet::Application do @app.run_command end + it "should initialize the Puppet Instrumentation layer on creation" do + Puppet::Util::Instrumentation.expects(:init) + Class.new(Puppet::Application).new + end + describe 'when invoking clear!' do before :each do Puppet::Application.run_status = :stop_requested diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb index 215ef897f..69e5e6bf9 100755 --- a/spec/unit/configurer/downloader_spec.rb +++ b/spec/unit/configurer/downloader_spec.rb @@ -60,16 +60,35 @@ describe Puppet::Configurer::Downloader do @dler.file end - it "should always set the owner to the current UID" do - Process.expects(:uid).returns 51 - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == 51 } - @dler.file - end - - it "should always set the group to the current GID" do - Process.expects(:gid).returns 61 - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == 61 } - @dler.file + describe "on POSIX" do + before :each do + Puppet.features.stubs(:microsoft_windows?).returns false + end + + it "should always set the owner to the current UID" do + Process.expects(:uid).returns 51 + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == 51 } + @dler.file + end + + it "should always set the group to the current GID" do + Process.expects(:gid).returns 61 + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == 61 } + @dler.file + end + end + + describe "on Windows", :if => Puppet.features.microsoft_windows? do + it "should always set the owner to the current user" do + Sys::Admin.expects(:get_login).returns 'foo' + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == 'foo' } + @dler.file + end + + it "should always set the group to nobody" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == 'S-1-0-0' } + @dler.file + end end it "should always force the download" do diff --git a/spec/unit/configurer/fact_handler_spec.rb b/spec/unit/configurer/fact_handler_spec.rb index 4a3fe8b3b..41b4862ca 100755 --- a/spec/unit/configurer/fact_handler_spec.rb +++ b/spec/unit/configurer/fact_handler_spec.rb @@ -78,12 +78,6 @@ describe Puppet::Configurer::FactHandler do Puppet[:node_name_value].should == 'other_node_name' end - it "should reload Facter before finding facts" do - @facthandler.expects(:reload_facter) - - @facthandler.find_facts - end - it "should fail if finding facts fails" do Puppet[:trace] = false Puppet[:certname] = "myhost" @@ -93,6 +87,11 @@ describe Puppet::Configurer::FactHandler do end end + it "should only load fact plugins once" do + Puppet::Node::Facts.indirection.expects(:find).once + @facthandler.find_facts + end + it "should warn about factsync deprecation when factsync is enabled" do Puppet::Configurer::Downloader.stubs(:new).returns mock("downloader", :evaluate => nil) @@ -145,27 +144,4 @@ describe Puppet::Configurer::FactHandler do @facthandler.facts_for_uploading end - - describe "when reloading Facter" do - before do - Facter.stubs(:clear) - Facter.stubs(:load) - Facter.stubs(:loadfacts) - end - - it "should clear Facter" do - Facter.expects(:clear) - @facthandler.reload_facter - end - - it "should load all Facter facts" do - Facter.expects(:loadfacts) - @facthandler.reload_facter - end - - it "should use the Facter terminus load all Puppet Fact plugins" do - Puppet::Node::Facts::Facter.expects(:load_fact_plugins) - @facthandler.reload_facter - end - end end diff --git a/spec/unit/configurer_spec.rb b/spec/unit/configurer_spec.rb index 5c660cc31..5afed1f58 100755 --- a/spec/unit/configurer_spec.rb +++ b/spec/unit/configurer_spec.rb @@ -407,33 +407,33 @@ describe Puppet::Configurer do end describe "when saving the summary report file" do + include PuppetSpec::Files + before do Puppet.settings.stubs(:use).returns(true) @configurer = Puppet::Configurer.new - @report = stub 'report' - @trans = stub 'transaction' - @lastrunfd = stub 'lastrunfd' - Puppet::Util::FileLocking.stubs(:writelock).yields(@lastrunfd) + @report = stub 'report', :raw_summary => {} + + Puppet[:lastrunfile] = tmpfile('last_run_file') end - it "should write the raw summary to the lastrunfile setting value" do - Puppet::Util::FileLocking.expects(:writelock).with(Puppet[:lastrunfile], 0660) + it "should write the last run file" do @configurer.save_last_run_summary(@report) + FileTest.exists?(Puppet[:lastrunfile]).should be_true end it "should write the raw summary as yaml" do @report.expects(:raw_summary).returns("summary") - @lastrunfd.expects(:print).with(YAML.dump("summary")) @configurer.save_last_run_summary(@report) + File.read(Puppet[:lastrunfile]).should == YAML.dump("summary") end it "should log but not fail if saving the last run summary fails" do - Puppet::Util::FileLocking.expects(:writelock).raises "exception" + Puppet[:lastrunfile] = "/dev/null/inexistant" Puppet.expects(:err) lambda { @configurer.save_last_run_summary(@report) }.should_not raise_error end - end describe "when retrieving a catalog" do diff --git a/spec/unit/face/instrumentation_data.rb b/spec/unit/face/instrumentation_data.rb new file mode 100644 index 000000000..2d4cc74f6 --- /dev/null +++ b/spec/unit/face/instrumentation_data.rb @@ -0,0 +1,7 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/face' + +describe Puppet::Face[:instrumentation_data, '0.0.1'] do + it_should_behave_like "an indirector face" +end diff --git a/spec/unit/face/instrumentation_listener.rb b/spec/unit/face/instrumentation_listener.rb new file mode 100644 index 000000000..87f218855 --- /dev/null +++ b/spec/unit/face/instrumentation_listener.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/face' + +describe Puppet::Face[:instrumentation_listener, '0.0.1'] do + it_should_behave_like "an indirector face" + + [:enable, :disable].each do |m| + describe "when running ##{m}" do + before(:each) do + @listener = stub_everything 'listener' + Puppet::Face[:instrumentation_listener, '0.0.1'].stubs(:find).returns(@listener) + Puppet::Face[:instrumentation_listener, '0.0.1'].stubs(:save) + Puppet::Util::Instrumentation::Listener.indirection.stubs(:terminus_class=) + end + + it "should force the REST terminus" do + Puppet::Util::Instrumentation::Listener.indirection.expects(:terminus_class=).with(:rest) + subject.send(m, "dummy") + end + + it "should find the named listener" do + Puppet::Face[:instrumentation_listener, '0.0.1'].expects(:find).with("dummy").returns(@listener) + subject.send(m, "dummy") + end + + it "should #{m} the named listener" do + @listener.expects(:enabled=).with( m == :enable ) + subject.send(m, "dummy") + end + + it "should save finally the listener" do + Puppet::Face[:instrumentation_listener, '0.0.1'].expects(:save).with(@listener) + subject.send(m, "dummy") + end + end + end +end diff --git a/spec/unit/face/instrumentation_probe.rb b/spec/unit/face/instrumentation_probe.rb new file mode 100644 index 000000000..3e475906d --- /dev/null +++ b/spec/unit/face/instrumentation_probe.rb @@ -0,0 +1,21 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/face' + +describe Puppet::Face[:instrumentation_probe, '0.0.1'] do + it_should_behave_like "an indirector face" + + describe 'when running #enable' do + it 'should invoke #save' do + subject.expects(:save).with(nil) + subject.enable('hostname') + end + end + + describe 'when running #disable' do + it 'should invoke #destroy' do + subject.expects(:destroy).with(nil) + subject.disable('hostname') + end + end +end diff --git a/spec/unit/face/module_spec.rb b/spec/unit/face/module_spec.rb new file mode 100644 index 000000000..33e486e5a --- /dev/null +++ b/spec/unit/face/module_spec.rb @@ -0,0 +1,3 @@ +# For face related tests, look in the spec/unit/faces/module folder. +# For integration tests, which test the behavior of module, look in the +# spec/unit/integration folder. diff --git a/spec/unit/face/node_spec.rb b/spec/unit/face/node_spec.rb index cb36e58a4..b126af6bb 100755 --- a/spec/unit/face/node_spec.rb +++ b/spec/unit/face/node_spec.rb @@ -14,9 +14,7 @@ describe Puppet::Face[:node, '0.0.1'] do "cached_facts" => ['hostname'], "cached_node" => ['hostname'], "reports" => ['hostname'], - - # Support for cleaning storeconfigs has been temporarily suspended. - # "storeconfigs" => ['hostname', :unexport] + "storeconfigs" => ['hostname', :unexport] }.each { |k, v| subject.expects("clean_#{k}".to_sym).with(*v) } subject.cleanup('hostname', :unexport) end @@ -158,114 +156,116 @@ describe Puppet::Face[:node, '0.0.1'] do end end - # describe "when cleaning storeconfigs entries for host", :if => Puppet.features.rails? do - # before :each do - # # Stub this so we don't need access to the DB - # require 'puppet/rails/host' - # - # Puppet.stubs(:[]).with(:storeconfigs).returns(true) - # - # Puppet::Rails.stubs(:connect) - # @rails_node = stub_everything 'rails_node' - # Puppet::Rails::Host.stubs(:find_by_name).returns(@rails_node) - # end - # - # it "should connect to the database" do - # Puppet::Rails.expects(:connect) - # subject.clean_storeconfigs(@host, false) - # end - # - # it "should find the right host entry" do - # Puppet::Rails::Host.expects(:find_by_name).with(@host).returns(@rails_node) - # subject.clean_storeconfigs(@host, false) - # end - # - # describe "without unexport" do - # it "should remove the host and it's content" do - # @rails_node.expects(:destroy) - # subject.clean_storeconfigs(@host, false) - # end - # end - # - # describe "with unexport" do - # before :each do - # @rails_node.stubs(:id).returns(1234) - # - # @type = stub_everything 'type' - # @type.stubs(:validattr?).with(:ensure).returns(true) - # - # @ensure_name = stub_everything 'ensure_name', :id => 23453 - # Puppet::Rails::ParamName.stubs(:find_or_create_by_name).returns(@ensure_name) - # - # @param_values = stub_everything 'param_values' - # @resource = stub_everything 'resource', :param_values => @param_values, :restype => "File" - # Puppet::Rails::Resource.stubs(:find).returns([@resource]) - # end - # - # it "should find all resources" do - # Puppet::Rails::Resource.expects(:find).with(:all, {:include => {:param_values => :param_name}, :conditions => ["exported=? AND host_id=?", true, 1234]}).returns([]) - # - # subject.clean_storeconfigs(@host, true) - # end - # - # describe "with an exported native type" do - # before :each do - # Puppet::Type.stubs(:type).returns(@type) - # @type.expects(:validattr?).with(:ensure).returns(true) - # end - # - # it "should test a native type for ensure as an attribute" do - # subject.clean_storeconfigs(@host, true) - # end - # - # it "should delete the old ensure parameter" do - # ensure_param = stub 'ensure_param', :id => 12345, :line => 12 - # @param_values.stubs(:find).returns(ensure_param) - # Puppet::Rails::ParamValue.expects(:delete).with(12345); - # subject.clean_storeconfigs(@host, true) - # end - # - # it "should add an ensure => absent parameter" do - # @param_values.expects(:create).with(:value => "absent", - # :line => 0, - # :param_name => @ensure_name) - # subject.clean_storeconfigs(@host, true) - # end - # end - # - # describe "with an exported definition" do - # it "should try to lookup a definition and test it for the ensure argument" do - # Puppet::Type.stubs(:type).returns(nil) - # definition = stub_everything 'definition', :arguments => { 'ensure' => 'present' } - # Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(definition) - # subject.clean_storeconfigs(@host, true) - # end - # end - # - # it "should not unexport the resource of an unknown type" do - # Puppet::Type.stubs(:type).returns(nil) - # Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(nil) - # Puppet::Rails::ParamName.expects(:find_or_create_by_name).never - # subject.clean_storeconfigs(@host) - # end - # - # it "should not unexport the resource of a not ensurable native type" do - # Puppet::Type.stubs(:type).returns(@type) - # @type.expects(:validattr?).with(:ensure).returns(false) - # Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(nil) - # Puppet::Rails::ParamName.expects(:find_or_create_by_name).never - # subject.clean_storeconfigs(@host, true) - # end - # - # it "should not unexport the resource of a not ensurable definition" do - # Puppet::Type.stubs(:type).returns(nil) - # definition = stub_everything 'definition', :arguments => { 'foobar' => 'someValue' } - # Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(definition) - # Puppet::Rails::ParamName.expects(:find_or_create_by_name).never - # subject.clean_storeconfigs(@host, true) - # end - # end - # end + describe "when cleaning storeconfigs entries for host", :if => Puppet.features.rails? do + before :each do + # Stub this so we don't need access to the DB + require 'puppet/rails/host' + + Puppet.stubs(:[]).with(:storeconfigs).returns(true) + + Puppet::Rails.stubs(:connect) + @rails_node = stub_everything 'rails_node' + Puppet::Rails::Host.stubs(:find_by_name).returns(@rails_node) + end + + it "should connect to the database" do + Puppet::Rails.expects(:connect) + subject.clean_storeconfigs(@host, false) + end + + it "should find the right host entry" do + Puppet::Rails::Host.expects(:find_by_name).with(@host).returns(@rails_node) + + subject.clean_storeconfigs(@host, false) + end + + describe "without unexport" do + it "should remove the host and it's content" do + @rails_node.expects(:destroy) + + subject.clean_storeconfigs(@host, false) + end + end + + describe "with unexport" do + before :each do + @rails_node.stubs(:id).returns(1234) + + @type = stub_everything 'type' + @type.stubs(:validattr?).with(:ensure).returns(true) + + @ensure_name = stub_everything 'ensure_name', :id => 23453 + Puppet::Rails::ParamName.stubs(:find_or_create_by_name).returns(@ensure_name) + + @param_values = stub_everything 'param_values' + @resource = stub_everything 'resource', :param_values => @param_values, :restype => "File" + Puppet::Rails::Resource.stubs(:find).returns([@resource]) + end + + it "should find all resources" do + Puppet::Rails::Resource.expects(:find).with(:all, {:include => {:param_values => :param_name}, :conditions => ["exported=? AND host_id=?", true, 1234]}).returns([]) + + subject.clean_storeconfigs(@host, true) + end + + describe "with an exported native type" do + before :each do + Puppet::Type.stubs(:type).returns(@type) + @type.expects(:validattr?).with(:ensure).returns(true) + end + + it "should test a native type for ensure as an attribute" do + subject.clean_storeconfigs(@host, true) + end + + it "should delete the old ensure parameter" do + ensure_param = stub 'ensure_param', :id => 12345, :line => 12 + @param_values.stubs(:find).returns(ensure_param) + Puppet::Rails::ParamValue.expects(:delete).with(12345); + subject.clean_storeconfigs(@host, true) + end + + it "should add an ensure => absent parameter" do + @param_values.expects(:create).with(:value => "absent", + :line => 0, + :param_name => @ensure_name) + subject.clean_storeconfigs(@host, true) + end + end + + describe "with an exported definition" do + it "should try to lookup a definition and test it for the ensure argument" do + Puppet::Type.stubs(:type).returns(nil) + definition = stub_everything 'definition', :arguments => { 'ensure' => 'present' } + Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(definition) + subject.clean_storeconfigs(@host, true) + end + end + + it "should not unexport the resource of an unkown type" do + Puppet::Type.stubs(:type).returns(nil) + Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(nil) + Puppet::Rails::ParamName.expects(:find_or_create_by_name).never + subject.clean_storeconfigs(@host, true) + end + + it "should not unexport the resource of a not ensurable native type" do + Puppet::Type.stubs(:type).returns(@type) + @type.expects(:validattr?).with(:ensure).returns(false) + Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(nil) + Puppet::Rails::ParamName.expects(:find_or_create_by_name).never + subject.clean_storeconfigs(@host, true) + end + + it "should not unexport the resource of a not ensurable definition" do + Puppet::Type.stubs(:type).returns(nil) + definition = stub_everything 'definition', :arguments => { 'foobar' => 'someValue' } + Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(definition) + Puppet::Rails::ParamName.expects(:find_or_create_by_name).never + subject.clean_storeconfigs(@host, true) + end + end + end end end end diff --git a/spec/unit/file_serving/content_spec.rb b/spec/unit/file_serving/content_spec.rb index 335f0e701..c1627c18f 100755 --- a/spec/unit/file_serving/content_spec.rb +++ b/spec/unit/file_serving/content_spec.rb @@ -102,13 +102,13 @@ describe Puppet::FileServing::Content, "when returning the contents" do it "should return the contents of the path if the file exists" do File.expects(:stat).with(@path).returns stub("stat", :ftype => "file") - File.expects(:read).with(@path).returns(:mycontent) + Puppet::Util.expects(:binread).with(@path).returns(:mycontent) @content.content.should == :mycontent end it "should cache the returned contents" do File.expects(:stat).with(@path).returns stub("stat", :ftype => "file") - File.expects(:read).with(@path).returns(:mycontent) + Puppet::Util.expects(:binread).with(@path).returns(:mycontent) @content.content # The second run would throw a failure if the content weren't being cached. diff --git a/spec/unit/indirector/facts/facter_spec.rb b/spec/unit/indirector/facts/facter_spec.rb index 3b1574e52..0cb8037aa 100755 --- a/spec/unit/indirector/facts/facter_spec.rb +++ b/spec/unit/indirector/facts/facter_spec.rb @@ -21,14 +21,29 @@ describe Puppet::Node::Facts::Facter do Puppet::Node::Facts::Facter.name.should == :facter end - it "should load facts on initialization" do - Puppet::Node::Facts::Facter.expects(:load_fact_plugins) - Puppet::Node::Facts::Facter.new + describe "when reloading Facter" do + before do + @facter_class = Puppet::Node::Facts::Facter + Facter.stubs(:clear) + Facter.stubs(:load) + Facter.stubs(:loadfacts) + end + + it "should clear Facter" do + Facter.expects(:clear) + @facter_class.reload_facter + end + + it "should load all Facter facts" do + Facter.expects(:loadfacts) + @facter_class.reload_facter + end end end describe Puppet::Node::Facts::Facter do before :each do + Puppet::Node::Facts::Facter.stubs(:reload_facter) @facter = Puppet::Node::Facts::Facter.new Facter.stubs(:to_hash).returns({}) @name = "me" @@ -36,6 +51,13 @@ describe Puppet::Node::Facts::Facter do end describe Puppet::Node::Facts::Facter, " when finding facts" do + it "should reset and load facts" do + clear = sequence 'clear' + Puppet::Node::Facts::Facter.expects(:reload_facter).in_sequence(clear) + Puppet::Node::Facts::Facter.expects(:load_fact_plugins).in_sequence(clear) + @facter.find(@request) + end + it "should return a Facts instance" do @facter.find(@request).should be_instance_of(Puppet::Node::Facts) end diff --git a/spec/unit/indirector/facts/inventory_active_record_spec.rb b/spec/unit/indirector/facts/inventory_active_record_spec.rb index 88e5e5359..43dcc7c6b 100755 --- a/spec/unit/indirector/facts/inventory_active_record_spec.rb +++ b/spec/unit/indirector/facts/inventory_active_record_spec.rb @@ -38,10 +38,22 @@ describe "Puppet::Node::Facts::InventoryActiveRecord", :if => (Puppet.features.r end describe "#save" do + let(:node) { + Puppet::Rails::InventoryNode.new(:name => "foo", :timestamp => Time.now) + } + + let(:facts) { + Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") + } + + it "should retry on ActiveRecord error" do + Puppet::Rails::InventoryNode.expects(:create).twice.raises(ActiveRecord::StatementInvalid).returns node + + Puppet::Node::Facts.indirection.save(facts) + end + it "should use an existing node if possible" do - node = Puppet::Rails::InventoryNode.new(:name => "foo", :timestamp => Time.now) node.save - facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") Puppet::Node::Facts.indirection.save(facts) Puppet::Rails::InventoryNode.count.should == 1 @@ -52,7 +64,6 @@ describe "Puppet::Node::Facts::InventoryActiveRecord", :if => (Puppet.features.r # This test isn't valid if there are nodes to begin with Puppet::Rails::InventoryNode.count.should == 0 - facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") Puppet::Node::Facts.indirection.save(facts) Puppet::Rails::InventoryNode.count.should == 1 @@ -60,7 +71,6 @@ describe "Puppet::Node::Facts::InventoryActiveRecord", :if => (Puppet.features.r end it "should save the facts" do - facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") Puppet::Node::Facts.indirection.save(facts) Puppet::Rails::InventoryFact.all.map{|f| [f.name,f.value]}.should =~ [["uptime_days","60"],["kernel","Darwin"]] diff --git a/spec/unit/indirector/instrumentation_data/local_spec.rb b/spec/unit/indirector/instrumentation_data/local_spec.rb new file mode 100644 index 000000000..45ca1e07e --- /dev/null +++ b/spec/unit/indirector/instrumentation_data/local_spec.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/instrumentation/listener' +require 'puppet/indirector/instrumentation_data/local' + +describe Puppet::Indirector::InstrumentationData::Local do + it "should be a subclass of the Code terminus" do + Puppet::Indirector::InstrumentationData::Local.superclass.should equal(Puppet::Indirector::Code) + end + + it "should be registered with the configuration store indirection" do + indirection = Puppet::Indirector::Indirection.instance(:instrumentation_data) + Puppet::Indirector::InstrumentationData::Local.indirection.should equal(indirection) + end + + it "should have its name set to :local" do + Puppet::Indirector::InstrumentationData::Local.name.should == :local + end +end + +describe Puppet::Indirector::InstrumentationData::Local do + before :each do + Puppet::Util::Instrumentation.stubs(:listener) + @data = Puppet::Indirector::InstrumentationData::Local.new + @name = "me" + @request = stub 'request', :key => @name + end + + describe "when finding instrumentation data" do + it "should return a Instrumentation Data instance matching the key" do + end + end + + describe "when searching listeners" do + it "should raise an error" do + lambda { @data.search(@request) }.should raise_error(Puppet::DevError) + end + end + + describe "when saving listeners" do + it "should raise an error" do + lambda { @data.save(@request) }.should raise_error(Puppet::DevError) + end + end + + describe "when destroying listeners" do + it "should raise an error" do + lambda { @data.destroy(@reques) }.should raise_error(Puppet::DevError) + end + end +end diff --git a/spec/unit/indirector/instrumentation_data/rest_spec.rb b/spec/unit/indirector/instrumentation_data/rest_spec.rb new file mode 100644 index 000000000..762667ea7 --- /dev/null +++ b/spec/unit/indirector/instrumentation_data/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/instrumentation/data' +require 'puppet/indirector/instrumentation_data/rest' + +describe Puppet::Indirector::InstrumentationData::Rest do + it "should be a subclass of Puppet::Indirector::REST" do + Puppet::Indirector::InstrumentationData::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/indirector/instrumentation_listener/local_spec.rb b/spec/unit/indirector/instrumentation_listener/local_spec.rb new file mode 100644 index 000000000..b251736b4 --- /dev/null +++ b/spec/unit/indirector/instrumentation_listener/local_spec.rb @@ -0,0 +1,65 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/instrumentation/listener' +require 'puppet/indirector/instrumentation_listener/local' + +describe Puppet::Indirector::InstrumentationListener::Local do + it "should be a subclass of the Code terminus" do + Puppet::Indirector::InstrumentationListener::Local.superclass.should equal(Puppet::Indirector::Code) + end + + it "should be registered with the configuration store indirection" do + indirection = Puppet::Indirector::Indirection.instance(:instrumentation_listener) + Puppet::Indirector::InstrumentationListener::Local.indirection.should equal(indirection) + end + + it "should have its name set to :local" do + Puppet::Indirector::InstrumentationListener::Local.name.should == :local + end +end + +describe Puppet::Indirector::InstrumentationListener::Local do + before :each do + Puppet::Util::Instrumentation.stubs(:listener) + @listener = Puppet::Indirector::InstrumentationListener::Local.new + @name = "me" + @request = stub 'request', :key => @name + end + + describe "when finding listeners" do + it "should return a Instrumentation Listener instance matching the key" do + Puppet::Util::Instrumentation.expects(:[]).with("me").returns(:instance) + @listener.find(@request).should == :instance + end + end + + describe "when searching listeners" do + it "should return a list of all loaded Instrumentation Listenesrs irregardless of the given key" do + Puppet::Util::Instrumentation.expects(:listeners).returns([:instance1, :instance2]) + @listener.search(@request).should == [:instance1, :instance2] + end + end + + describe "when saving listeners" do + it "should set the new listener to the global listener list" do + newlistener = stub 'listener', :name => @name + @request.stubs(:instance).returns(newlistener) + Puppet::Util::Instrumentation.expects(:[]=).with("me", newlistener) + @listener.save(@request) + end + end + + describe "when destroying listeners" do + it "should raise an error if listener wasn't subscribed" do + Puppet::Util::Instrumentation.expects(:[]).with("me").returns(nil) + lambda { @listener.destroy(@request) }.should raise_error + end + + it "should unsubscribe the listener" do + Puppet::Util::Instrumentation.expects(:[]).with("me").returns(:instancce) + Puppet::Util::Instrumentation.expects(:unsubscribe).with(:instancce) + @listener.destroy(@request) + end + end +end diff --git a/spec/unit/indirector/instrumentation_listener/rest_spec.rb b/spec/unit/indirector/instrumentation_listener/rest_spec.rb new file mode 100644 index 000000000..6355a1c53 --- /dev/null +++ b/spec/unit/indirector/instrumentation_listener/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/instrumentation/listener' +require 'puppet/indirector/instrumentation_listener/rest' + +describe Puppet::Indirector::InstrumentationListener::Rest do + it "should be a subclass of Puppet::Indirector::REST" do + Puppet::Indirector::InstrumentationListener::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/indirector/instrumentation_probe/local_spec.rb b/spec/unit/indirector/instrumentation_probe/local_spec.rb new file mode 100644 index 000000000..40d64dc2b --- /dev/null +++ b/spec/unit/indirector/instrumentation_probe/local_spec.rb @@ -0,0 +1,65 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/instrumentation/indirection_probe' +require 'puppet/indirector/instrumentation_probe/local' +require 'puppet/util/instrumentation/instrumentable' + +describe Puppet::Indirector::InstrumentationProbe::Local do + it "should be a subclass of the Code terminus" do + Puppet::Indirector::InstrumentationProbe::Local.superclass.should equal(Puppet::Indirector::Code) + end + + it "should be registered with the configuration store indirection" do + indirection = Puppet::Indirector::Indirection.instance(:instrumentation_probe) + Puppet::Indirector::InstrumentationProbe::Local.indirection.should equal(indirection) + end + + it "should have its name set to :local" do + Puppet::Indirector::InstrumentationProbe::Local.name.should == :local + end +end + +describe Puppet::Indirector::InstrumentationProbe::Local do + before :each do + Puppet::Util::Instrumentation.stubs(:listener) + @probe = Puppet::Indirector::InstrumentationProbe::Local.new + @name = "me" + @request = stub 'request', :key => @name + end + + describe "when finding probes" do + it "should do nothing" do + @probe.find(@request).should be_nil + end + end + + describe "when searching probes" do + it "should return a list of all loaded probes irregardless of the given key" do + instance1 = stub 'instance1', :method => "probe1", :klass => "Klass1" + instance2 = stub 'instance2', :method => "probe2", :klass => "Klass2" + Puppet::Util::Instrumentation::IndirectionProbe.expects(:new).with("Klass1.probe1").returns(:instance1) + Puppet::Util::Instrumentation::IndirectionProbe.expects(:new).with("Klass2.probe2").returns(:instance2) + Puppet::Util::Instrumentation::Instrumentable.expects(:each_probe).multiple_yields([instance1], [instance2]) + @probe.search(@request).should == [ :instance1, :instance2 ] + end + end + + describe "when saving probes" do + it "should enable probes" do + newprobe = stub 'probe', :name => @name + @request.stubs(:instance).returns(newprobe) + Puppet::Util::Instrumentation::Instrumentable.expects(:enable_probes) + @probe.save(@request) + end + end + + describe "when destroying probes" do + it "should disable probes" do + newprobe = stub 'probe', :name => @name + @request.stubs(:instance).returns(newprobe) + Puppet::Util::Instrumentation::Instrumentable.expects(:disable_probes) + @probe.destroy(@request) + end + end +end diff --git a/spec/unit/indirector/instrumentation_probe/rest_spec.rb b/spec/unit/indirector/instrumentation_probe/rest_spec.rb new file mode 100644 index 000000000..0b73fbdf5 --- /dev/null +++ b/spec/unit/indirector/instrumentation_probe/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/instrumentation/indirection_probe' +require 'puppet/indirector/instrumentation_probe/rest' + +describe Puppet::Indirector::InstrumentationProbe::Rest do + it "should be a subclass of Puppet::Indirector::REST" do + Puppet::Indirector::InstrumentationProbe::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index a0f64c6d3..e1172497d 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -89,83 +89,6 @@ describe Puppet::Module do lambda { mod.validate_puppet_version }.should raise_error(Puppet::Module::IncompatibleModule) end - describe "when specifying required modules" do - it "should support specifying a required module" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar" - end - - it "should support specifying multiple required modules" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar" - mod.requires "baz" - end - - it "should support specifying a required module and version" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar", 1.0 - end - - it "should fail when required modules are missing" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar" - - mod.environment.expects(:module).with("foobar").returns nil - - lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) - end - - it "should fail when required modules are present but of the wrong version" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar", 1.0 - - foobar = Puppet::Module.new("foobar") - foobar.version = 2.0 - - mod.environment.expects(:module).with("foobar").returns foobar - - lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::IncompatibleModule) - end - - it "should have valid dependencies when no dependencies have been specified" do - mod = Puppet::Module.new("mymod") - - lambda { mod.validate_dependencies }.should_not raise_error - end - - it "should fail when some dependencies are present but others aren't" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar" - mod.requires "baz" - - mod.environment.expects(:module).with("foobar").returns Puppet::Module.new("foobar") - mod.environment.expects(:module).with("baz").returns nil - - lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) - end - - it "should have valid dependencies when all dependencies are met" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar", 1.0 - mod.requires "baz" - - foobar = Puppet::Module.new("foobar") - foobar.version = 1.0 - - baz = Puppet::Module.new("baz") - - mod.environment.expects(:module).with("foobar").returns foobar - mod.environment.expects(:module).with("baz").returns baz - - lambda { mod.validate_dependencies }.should_not raise_error - end - - it "should validate its dependendencies on initialization" do - Puppet::Module.any_instance.expects(:validate_dependencies) - Puppet::Module.new("mymod") - end - end - describe "when managing supported platforms" do it "should support specifying a supported platform" do mod = Puppet::Module.new("mymod") @@ -251,11 +174,11 @@ describe Puppet::Module do end it "should convert an environment name into an Environment instance" do - Puppet::Module.new("foo", "prod").environment.should be_instance_of(Puppet::Node::Environment) + Puppet::Module.new("foo", :environment => "prod").environment.should be_instance_of(Puppet::Node::Environment) end it "should accept an environment at initialization" do - Puppet::Module.new("foo", :prod).environment.name.should == :prod + Puppet::Module.new("foo", :environment => :prod).environment.name.should == :prod end it "should use the default environment if none is provided" do @@ -265,43 +188,53 @@ describe Puppet::Module do it "should use any provided Environment instance" do env = Puppet::Node::Environment.new - Puppet::Module.new("foo", env).environment.should equal(env) + Puppet::Module.new("foo", :environment => env).environment.should equal(env) end - it "should return the path to the first found instance in its environment's module paths as its path" do - dir = tmpdir("deep_path") - first = File.join(dir, "first") - second = File.join(dir, "second") + describe ".path" do + before do + dir = tmpdir("deep_path") - FileUtils.mkdir_p(first) - FileUtils.mkdir_p(second) - Puppet[:modulepath] = "#{first}#{File::PATH_SEPARATOR}#{second}" + @first = File.join(dir, "first") + @second = File.join(dir, "second") + Puppet[:modulepath] = "#{@first}#{File::PATH_SEPARATOR}#{@second}" - modpath = File.join(first, "foo") - FileUtils.mkdir_p(modpath) + FileUtils.mkdir_p(@first) + FileUtils.mkdir_p(@second) + end - # Make a second one, which we shouldn't find - FileUtils.mkdir_p(File.join(second, "foo")) + it "should return the path to the first found instance in its environment's module paths as its path" do + modpath = File.join(@first, "foo") + FileUtils.mkdir_p(modpath) - mod = Puppet::Module.new("foo") - mod.path.should == modpath - end + # Make a second one, which we shouldn't find + FileUtils.mkdir_p(File.join(@second, "foo")) + + mod = Puppet::Module.new("foo") + mod.path.should == modpath + end - it "should be able to find itself in a directory other than the first directory in the module path" do - dir = tmpdir("deep_path") - first = File.join(dir, "first") - second = File.join(dir, "second") + it "should be able to find itself in a directory other than the first directory in the module path" do + modpath = File.join(@second, "foo") + FileUtils.mkdir_p(modpath) - FileUtils.mkdir_p(first) - FileUtils.mkdir_p(second) - Puppet[:modulepath] = "#{first}#{File::PATH_SEPARATOR}#{second}" + mod = Puppet::Module.new("foo") + mod.should be_exist + mod.path.should == modpath + end - modpath = File.join(second, "foo") - FileUtils.mkdir_p(modpath) + it "should be able to find itself in a directory other than the first directory in the module path even when it exists in the first" do + environment = Puppet::Node::Environment.new - mod = Puppet::Module.new("foo") - mod.should be_exist - mod.path.should == modpath + first_modpath = File.join(@first, "foo") + FileUtils.mkdir_p(first_modpath) + second_modpath = File.join(@second, "foo") + FileUtils.mkdir_p(second_modpath) + + mod = Puppet::Module.new("foo", :environment => environment, :path => second_modpath) + mod.path.should == File.join(@second, "foo") + mod.environment.should == environment + end end it "should be considered existent if it exists in at least one module path" do @@ -403,24 +336,6 @@ describe Puppet::Module do end end -describe Puppet::Module, " when building its search path" do - it "should use the current environment's search path if no environment is specified" do - env = mock 'env' - env.expects(:modulepath).returns "eh" - Puppet::Node::Environment.expects(:new).with(nil).returns env - - Puppet::Module.modulepath.should == "eh" - end - - it "should use the specified environment's search path if an environment is specified" do - env = mock 'env' - env.expects(:modulepath).returns "eh" - Puppet::Node::Environment.expects(:new).with("foo").returns env - - Puppet::Module.modulepath("foo").should == "eh" - end -end - describe Puppet::Module, "when finding matching manifests" do before do @mod = Puppet::Module.new("mymod") @@ -592,7 +507,6 @@ describe Puppet::Module do @module.puppetversion.should == @data[:puppetversion] end - it "should fail if the discovered name is different than the metadata name" end end diff --git a/spec/unit/module_tool/application_spec.rb b/spec/unit/module_tool/application_spec.rb new file mode 100644 index 000000000..22d3632fd --- /dev/null +++ b/spec/unit/module_tool/application_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' +require 'puppet/module_tool' + +describe Puppet::Module::Tool::Applications::Application do + describe 'app' do + + good_versions = %w{ 1.2.4 0.0.1 0.0.0 0.0.2git-8-g3d316d1 0.0.3b1 10.100.10000 + 0.1.2rc1 0.1.2dev-1 0.1.2svn12345 } + bad_versions = %w{ 0.1.2-3 0.1 0 0.1.2.3 dev } + + before do + @app = Class.new(described_class).new + end + + good_versions.each do |ver| + it "should accept version string #{ver}" do + @app.instance_eval("@filename=%q{puppetlabs-ntp-#{ver}}") + @app.parse_filename! + end + end + + bad_versions.each do |ver| + it "should not accept version string #{ver}" do + @app.instance_eval("@filename=%q{puppetlabs-ntp-#{ver}}") + lambda { @app.parse_filename! }.should raise_error + end + end + end +end diff --git a/spec/unit/module_tool/metadata_spec.rb b/spec/unit/module_tool/metadata_spec.rb new file mode 100644 index 000000000..85d743fbc --- /dev/null +++ b/spec/unit/module_tool/metadata_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' +require 'puppet/module_tool' + +describe Puppet::Module::Tool::Metadata do + context "when using default values" do + it "should set license to 'Apache License, Version 2.0'" do + metadata = Puppet::Module::Tool::Metadata.new + metadata.license.should == "Apache License, Version 2.0" + end + end +end diff --git a/spec/unit/module_tool/repository_spec.rb b/spec/unit/module_tool/repository_spec.rb new file mode 100644 index 000000000..69be1661e --- /dev/null +++ b/spec/unit/module_tool/repository_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' +require 'net/http' +require 'puppet/module_tool' + +describe Puppet::Module::Tool::Repository do + describe 'instances' do + before do + @repository = described_class.new('http://fake.com') + end + + describe '#make_http_request' do + before do + # Do a mock of the Proxy call so we can do proper expects for + # Net::HTTP + Net::HTTP.expects(:Proxy).returns(Net::HTTP) + Net::HTTP.expects(:start) + end + context "when not given an :authenticate option" do + it "should authenticate" do + @repository.expects(:authenticate).never + @repository.make_http_request(nil) + end + end + context "when given an :authenticate option" do + it "should authenticate" do + @repository.expects(:authenticate) + @repository.make_http_request(nil, :authenticate => true) + end + end + end + + describe '#authenticate' do + it "should set basic auth on the request" do + authenticated_request = stub + authenticated_request.expects(:basic_auth) + @repository.expects(:prompt).twice + @repository.authenticate(authenticated_request) + end + end + + describe '#retrieve' do + before do + @uri = URI.parse('http://some.url.com') + end + + it "should access the cache" do + @repository.cache.expects(:retrieve).with(@uri) + @repository.retrieve(@uri) + end + end + end +end diff --git a/spec/unit/module_tool/uninstaller_spec.rb b/spec/unit/module_tool/uninstaller_spec.rb new file mode 100644 index 000000000..abf2db0f8 --- /dev/null +++ b/spec/unit/module_tool/uninstaller_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' +require 'puppet/module_tool' +require 'tmpdir' + +describe Puppet::Module::Tool::Applications::Uninstaller do + include PuppetSpec::Files + + describe "instances" do + let(:tmp_module_path1) { tmpdir("uninstaller_module_path1") } + let(:tmp_module_path2) { tmpdir("uninstaller_module_path2") } + let(:options) do + { :target_directories => [ tmp_module_path1, tmp_module_path2 ] } + end + + it "should return an empty list if the module is not installed" do + described_class.new('foo', options).run.should == [] + end + + it "should uninstall an installed module" do + foo_module_path = File.join(tmp_module_path1, 'foo') + Dir.mkdir(foo_module_path) + described_class.new('foo', options).run.should == [ foo_module_path ] + end + + it "should only uninstall the requested module" do + foo_module_path = File.join(tmp_module_path1, 'foo') + bar_module_path = File.join(tmp_module_path1, 'bar') + Dir.mkdir(foo_module_path) + Dir.mkdir(bar_module_path) + described_class.new('foo', options).run.should == [ foo_module_path ] + end + + it "should uninstall the module from all target directories" do + foo1_module_path = File.join(tmp_module_path1, 'foo') + foo2_module_path = File.join(tmp_module_path2, 'foo') + Dir.mkdir(foo1_module_path) + Dir.mkdir(foo2_module_path) + described_class.new('foo', options).run.should == [ foo1_module_path, foo2_module_path ] + end + + #11803 + it "should check for broken dependencies" + end +end diff --git a/spec/unit/module_tool_spec.rb b/spec/unit/module_tool_spec.rb new file mode 100644 index 000000000..15ca6c766 --- /dev/null +++ b/spec/unit/module_tool_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' +require 'puppet/module_tool' + +describe Puppet::Module::Tool do + describe 'http_proxy support' do + before :each do + ENV["http_proxy"] = nil + end + + after :each do + ENV["http_proxy"] = nil + end + + it "should support environment variable for port and host" do + ENV["http_proxy"] = "http://test.com:8011" + described_class.http_proxy_host.should == "test.com" + described_class.http_proxy_port.should == 8011 + end + + it "should support puppet configuration for port and host" do + ENV["http_proxy"] = nil + Puppet.settings.stubs(:[]).with(:http_proxy_host).returns('test.com') + Puppet.settings.stubs(:[]).with(:http_proxy_port).returns(7456) + + described_class.http_proxy_port.should == 7456 + described_class.http_proxy_host.should == "test.com" + end + + it "should use environment variable before puppet settings" do + ENV["http_proxy"] = "http://test1.com:8011" + Puppet.settings.stubs(:[]).with(:http_proxy_host).returns('test2.com') + Puppet.settings.stubs(:[]).with(:http_proxy_port).returns(7456) + + described_class.http_proxy_host.should == "test1.com" + described_class.http_proxy_port.should == 8011 + end + end +end diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb index a952f24e2..039bccfd5 100755 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -123,6 +123,10 @@ describe Puppet::Network::HTTP::API::V1 do @tester.uri2indirection("GET", "/env/statuses/bar", {})[0].should == 'status' end + it "should change indirection name to 'probe' if the http method is a GET and the indirection name is probes" do + @tester.uri2indirection("GET", "/env/probes/bar", {})[0].should == 'probe' + end + it "should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular" do @tester.uri2indirection("DELETE", "/env/foo/bar", {})[1].should == :destroy end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 78d383440..d5d3068a1 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -7,6 +7,8 @@ require 'puppet/node/environment' require 'puppet/util/execution' describe Puppet::Node::Environment do + let(:env) { Puppet::Node::Environment.new("testing") } + include PuppetSpec::Files after do Puppet::Node::Environment.clear @@ -44,59 +46,57 @@ describe Puppet::Node::Environment do describe "when managing known resource types" do before do - @env = Puppet::Node::Environment.new("dev") - @collection = Puppet::Resource::TypeCollection.new(@env) - @env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) + @collection = Puppet::Resource::TypeCollection.new(env) + env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) Thread.current[:known_resource_types] = nil end it "should create a resource type collection if none exists" do - Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection - @env.known_resource_types.should equal(@collection) + Puppet::Resource::TypeCollection.expects(:new).with(env).returns @collection + env.known_resource_types.should equal(@collection) end it "should reuse any existing resource type collection" do - @env.known_resource_types.should equal(@env.known_resource_types) + env.known_resource_types.should equal(env.known_resource_types) end it "should perform the initial import when creating a new collection" do - @env = Puppet::Node::Environment.new("dev") - @env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) - @env.known_resource_types + env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) + env.known_resource_types end it "should return the same collection even if stale if it's the same thread" do Puppet::Resource::TypeCollection.stubs(:new).returns @collection - @env.known_resource_types.stubs(:stale?).returns true + env.known_resource_types.stubs(:stale?).returns true - @env.known_resource_types.should equal(@collection) + env.known_resource_types.should equal(@collection) end it "should return the current thread associated collection if there is one" do Thread.current[:known_resource_types] = @collection - @env.known_resource_types.should equal(@collection) + env.known_resource_types.should equal(@collection) end it "should give to all threads using the same environment the same collection if the collection isn't stale" do - original_thread_type_collection = Puppet::Resource::TypeCollection.new(@env) - Puppet::Resource::TypeCollection.expects(:new).with(@env).returns original_thread_type_collection - @env.known_resource_types.should equal(original_thread_type_collection) + original_thread_type_collection = Puppet::Resource::TypeCollection.new(env) + Puppet::Resource::TypeCollection.expects(:new).with(env).returns original_thread_type_collection + env.known_resource_types.should equal(original_thread_type_collection) original_thread_type_collection.expects(:require_reparse?).returns(false) - Puppet::Resource::TypeCollection.stubs(:new).with(@env).returns @collection + Puppet::Resource::TypeCollection.stubs(:new).with(env).returns @collection t = Thread.new { - @env.known_resource_types.should equal(original_thread_type_collection) + env.known_resource_types.should equal(original_thread_type_collection) } t.join end it "should generate a new TypeCollection if the current one requires reparsing" do - old_type_collection = @env.known_resource_types + old_type_collection = env.known_resource_types old_type_collection.stubs(:require_reparse?).returns true Thread.current[:known_resource_types] = nil - new_type_collection = @env.known_resource_types + new_type_collection = env.known_resource_types new_type_collection.should be_a Puppet::Resource::TypeCollection new_type_collection.should_not equal(old_type_collection) @@ -109,14 +109,11 @@ describe Puppet::Node::Environment do Puppet[:modulepath] = path - env = Puppet::Node::Environment.new("testing") - env.modulepath.should == [real_file] end it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do Puppet::Util::Execution.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do - env = Puppet::Node::Environment.new("testing") module_path = %w{/one /two}.join(File::PATH_SEPARATOR) env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two} env.expects(:[]).with(:modulepath).returns module_path @@ -132,8 +129,6 @@ describe Puppet::Node::Environment do end it "should not return non-directories" do - env = Puppet::Node::Environment.new("testing") - FileTest.expects(:directory?).with(@path_one).returns true FileTest.expects(:directory?).with(@path_two).returns false @@ -142,7 +137,6 @@ describe Puppet::Node::Environment do it "should use the current working directory to fully-qualify unqualified paths" do FileTest.stubs(:directory?).returns true - env = Puppet::Node::Environment.new("testing") two = File.expand_path(File.join(Dir.getwd, "two")) env.validate_dirs([@path_one, 'two']).should == [@path_one, two] @@ -155,44 +149,75 @@ describe Puppet::Node::Environment do end it "should provide an array-like accessor method for returning any environment-specific setting" do - env = Puppet::Node::Environment.new("testing") env.should respond_to(:[]) end it "should ask the Puppet settings instance for the setting qualified with the environment name" do Puppet.settings.expects(:value).with("myvar", :testing).returns("myval") - env = Puppet::Node::Environment.new("testing") env["myvar"].should == "myval" end it "should be able to return an individual module that exists in its module path" do - env = Puppet::Node::Environment.new("testing") mod = mock 'module' - Puppet::Module.expects(:new).with("one", env).returns mod + Puppet::Module.expects(:new).with("one", :environment => env).returns mod mod.expects(:exist?).returns true env.module("one").should equal(mod) end it "should return nil if asked for a module that does not exist in its path" do - env = Puppet::Node::Environment.new("testing") mod = mock 'module' - Puppet::Module.expects(:new).with("one", env).returns mod + Puppet::Module.expects(:new).with("one", :environment => env).returns mod mod.expects(:exist?).returns false env.module("one").should be_nil end - it "should be able to return its modules" do - Puppet::Node::Environment.new("testing").should respond_to(:modules) + describe ".modules_by_path" do + before do + dir = tmpdir("deep_path") + + @first = File.join(dir, "first") + @second = File.join(dir, "second") + Puppet[:modulepath] = "#{@first}#{File::PATH_SEPARATOR}#{@second}" + + FileUtils.mkdir_p(@first) + FileUtils.mkdir_p(@second) + end + + it "should return an empty list if there are no modules" do + env.modules_by_path.should == { + @first => [], + @second => [] + } + end + + it "should include modules even if they exist in multiple dirs in the modulepath" do + modpath1 = File.join(@first, "foo") + FileUtils.mkdir_p(modpath1) + modpath2 = File.join(@second, "foo") + FileUtils.mkdir_p(modpath2) + + env.modules_by_path.should == { + @first => [Puppet::Module.new('foo', :environment => env, :path => modpath1)], + @second => [Puppet::Module.new('foo', :environment => env, :path => modpath2)] + } + end end describe ".modules" do + it "should return an empty list if there are no modules" do + env.modulepath = %w{/a /b} + Dir.expects(:entries).with("/a").returns [] + Dir.expects(:entries).with("/b").returns [] + + env.modules.should == [] + end + it "should return a module named for every directory in each module path" do - env = Puppet::Node::Environment.new("testing") - env.expects(:modulepath).at_least_once.returns %w{/a /b} + env.modulepath = %w{/a /b} Dir.expects(:entries).with("/a").returns %w{foo bar} Dir.expects(:entries).with("/b").returns %w{bee baz} @@ -200,8 +225,7 @@ describe Puppet::Node::Environment do end it "should remove duplicates" do - env = Puppet::Node::Environment.new("testing") - env.expects(:modulepath).returns( %w{/a /b} ).at_least_once + env.modulepath = %w{/a /b} Dir.expects(:entries).with("/a").returns %w{foo} Dir.expects(:entries).with("/b").returns %w{foo} @@ -209,8 +233,7 @@ describe Puppet::Node::Environment do end it "should ignore invalid modules" do - env = Puppet::Node::Environment.new("testing") - env.stubs(:modulepath).returns %w{/a} + env.modulepath = %w{/a} Dir.expects(:entries).with("/a").returns %w{foo bar} Puppet::Module.expects(:new).with { |name, env| name == "foo" }.returns mock("foomod", :name => "foo") @@ -220,16 +243,14 @@ describe Puppet::Node::Environment do end it "should create modules with the correct environment" do - env = Puppet::Node::Environment.new("testing") - env.expects(:modulepath).at_least_once.returns %w{/a} + env.modulepath = %w{/a} Dir.expects(:entries).with("/a").returns %w{foo} env.modules.each {|mod| mod.environment.should == env } end it "should cache the module list" do - env = Puppet::Node::Environment.new("testing") - env.expects(:modulepath).at_least_once.returns %w{/a} + env.modulepath = %w{/a} Dir.expects(:entries).once.with("/a").returns %w{foo} env.modules @@ -244,20 +265,18 @@ describe Puppet::Node::Environment do @helper.extend(Puppet::Node::Environment::Helper) end - it "should be able to set and retrieve the environment" do + it "should be able to set and retrieve the environment as a symbol" do @helper.environment = :foo @helper.environment.name.should == :foo end it "should accept an environment directly" do - env = Puppet::Node::Environment.new :foo - @helper.environment = env + @helper.environment = Puppet::Node::Environment.new(:foo) @helper.environment.name.should == :foo end it "should accept an environment as a string" do - env = Puppet::Node::Environment.new "foo" - @helper.environment = env + @helper.environment = 'foo' @helper.environment.name.should == :foo end end @@ -266,14 +285,13 @@ describe Puppet::Node::Environment do before do @parser = Puppet::Parser::Parser.new("test") Puppet::Parser::Parser.stubs(:new).returns @parser - @env = Puppet::Node::Environment.new("env") end it "should set the parser's string to the 'code' setting and parse if code is available" do Puppet.settings[:code] = "my code" @parser.expects(:string=).with "my code" @parser.expects(:parse) - @env.instance_eval { perform_initial_import } + env.instance_eval { perform_initial_import } end it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do @@ -282,7 +300,7 @@ describe Puppet::Node::Environment do Puppet.settings[:manifest] = filename @parser.expects(:file=).with filename @parser.expects(:parse) - @env.instance_eval { perform_initial_import } + env.instance_eval { perform_initial_import } end it "should pass the manifest file to the parser even if it does not exist on disk" do @@ -291,15 +309,15 @@ describe Puppet::Node::Environment do Puppet.settings[:manifest] = filename @parser.expects(:file=).with(filename).once @parser.expects(:parse).once - @env.instance_eval { perform_initial_import } + env.instance_eval { perform_initial_import } end it "should fail helpfully if there is an error importing" do File.stubs(:exist?).returns true - @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env) + env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env) @parser.expects(:file=).once @parser.expects(:parse).raises ArgumentError - lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error) + lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error) end it "should not do anything if the ignore_import settings is set" do @@ -307,15 +325,15 @@ describe Puppet::Node::Environment do @parser.expects(:string=).never @parser.expects(:file=).never @parser.expects(:parse).never - @env.instance_eval { perform_initial_import } + env.instance_eval { perform_initial_import } end it "should mark the type collection as needing a reparse when there is an error parsing" do @parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...") - @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env) + env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env) - lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../) - @env.known_resource_types.require_reparse?.should be_true + lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../) + env.known_resource_types.require_reparse?.should be_true end end end diff --git a/spec/unit/parser/ast/asthash_spec.rb b/spec/unit/parser/ast/asthash_spec.rb index d7fbbfae9..ab1281f91 100755 --- a/spec/unit/parser/ast/asthash_spec.rb +++ b/spec/unit/parser/ast/asthash_spec.rb @@ -91,7 +91,6 @@ describe Puppet::Parser::AST::ASTHash do it "should return a valid string with to_s" do hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b", "c" => "d" }) - - hash.to_s.should == '{a => b, c => d}' + ["{a => b, c => d}", "{c => d, a => b}"].should be_include hash.to_s end end diff --git a/spec/unit/parser/ast/leaf_spec.rb b/spec/unit/parser/ast/leaf_spec.rb index ff3fed5e9..881506ea4 100755 --- a/spec/unit/parser/ast/leaf_spec.rb +++ b/spec/unit/parser/ast/leaf_spec.rb @@ -151,6 +151,14 @@ describe Puppet::Parser::AST::HashOrArrayAccess do lambda { access.evaluate(@scope) }.should raise_error end + it "should be able to return :undef for an unknown array index" do + @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns(["val1", "val2", "val3"]) + + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => 6 ) + + access.evaluate(@scope).should == :undef + end + it "should be able to return an hash value" do @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns({ "key1" => "val1", "key2" => "val2", "key3" => "val3" }) @@ -159,6 +167,14 @@ describe Puppet::Parser::AST::HashOrArrayAccess do access.evaluate(@scope).should == "val2" end + it "should be able to return :undef for unknown hash keys" do + @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns({ "key1" => "val1", "key2" => "val2", "key3" => "val3" }) + + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key12" ) + + access.evaluate(@scope).should == :undef + end + it "should be able to return an hash value with a numerical key" do @scope.stubs(:lookupvar).with { |name,options| name == "a"}.returns({ "key1" => "val1", "key2" => "val2", "45" => "45", "key3" => "val3" }) diff --git a/spec/unit/parser/functions/create_resources_spec.rb b/spec/unit/parser/functions/create_resources_spec.rb index da76e75d0..64314f777 100755 --- a/spec/unit/parser/functions/create_resources_spec.rb +++ b/spec/unit/parser/functions/create_resources_spec.rb @@ -20,8 +20,9 @@ describe 'function for dynamically creating resources' do it "should exist" do Puppet::Parser::Functions.function(:create_resources).should == "function_create_resources" end - it 'should require two arguments' do - lambda { @scope.function_create_resources(['foo']) }.should raise_error(ArgumentError, 'create_resources(): wrong number of arguments (1; must be 2)') + it 'should require two or three arguments' do + lambda { @scope.function_create_resources(['foo']) }.should raise_error(ArgumentError, 'create_resources(): wrong number of arguments (1; must be 2 or 3)') + lambda { @scope.function_create_resources(['foo', 'bar', 'blah', 'baz']) }.should raise_error(ArgumentError, 'create_resources(): wrong number of arguments (4; must be 2 or 3)') end describe 'when creating native types' do before :each do @@ -58,6 +59,11 @@ describe 'function for dynamically creating resources' do foo.should be rg.path_between(test,foo).should be end + it 'should account for default values' do + @scope.function_create_resources(['file', {'/etc/foo'=>{'ensure'=>'present'}, '/etc/baz'=>{'group'=>'food'}}, {'group' => 'bar'}]) + @compiler.catalog.resource(:file, "/etc/foo")['group'].should == 'bar' + @compiler.catalog.resource(:file, "/etc/baz")['group'].should == 'food' + end end describe 'when dynamically creating resource types' do before :each do @@ -103,6 +109,11 @@ notify{test:} rg.path_between(test,blah).should be @compiler.catalog.resource(:notify, "blah")['message'].should == 'two' end + it 'should account for default values' do + @scope.function_create_resources(['foo', {'blah'=>{}}, {'one' => 'two'}]) + @scope.compiler.compile + @compiler.catalog.resource(:notify, "blah")['message'].should == 'two' + end end describe 'when creating classes' do before :each do @@ -114,7 +125,7 @@ notify{tester:} @scope.resource=Puppet::Parser::Resource.new('class', 't', :scope => @scope) Puppet::Parser::Functions.function(:create_resources) end - it 'should be able to create classes', :'fails_on_ruby_1.9.2' => true do + it 'should be able to create classes' do @scope.function_create_resources(['class', {'bar'=>{'one'=>'two'}}]) @scope.compiler.compile @compiler.catalog.resource(:notify, "test")['message'].should == 'two' @@ -123,7 +134,7 @@ notify{tester:} it 'should fail to create non-existing classes' do lambda { @scope.function_create_resources(['class', {'blah'=>{'one'=>'two'}}]) }.should raise_error(ArgumentError ,'could not find hostclass blah') end - it 'should be able to add edges', :'fails_on_ruby_1.9.2' => true do + it 'should be able to add edges' do @scope.function_create_resources(['class', {'bar'=>{'one'=>'two', 'require' => 'Notify[tester]'}}]) @scope.compiler.compile rg = @scope.compiler.catalog.to_ral.relationship_graph @@ -133,5 +144,11 @@ notify{tester:} tester.should be rg.path_between(tester,test).should be end + it 'should account for default values' do + @scope.function_create_resources(['class', {'bar'=>{}}, {'one' => 'two'}]) + @scope.compiler.compile + @compiler.catalog.resource(:notify, "test")['message'].should == 'two' + @compiler.catalog.resource(:class, "bar").should_not be_nil#['message'].should == 'two' + end end end diff --git a/spec/unit/property/keyvalue_spec.rb b/spec/unit/property/keyvalue_spec.rb index 821c61799..2bead50e3 100755 --- a/spec/unit/property/keyvalue_spec.rb +++ b/spec/unit/property/keyvalue_spec.rb @@ -35,8 +35,11 @@ describe klass do @property.should_to_s({:foo => "baz", :bar => "boo"}) == "foo=baz;bar=boo" end - it "should return the passed in array values joined with the delimiter from is_to_s" do - @property.is_to_s({"foo" => "baz" , "bar" => "boo"}).should == "foo=baz;bar=boo" + it "should return the passed in hash values joined with the delimiter from is_to_s" do + s = @property.is_to_s({"foo" => "baz" , "bar" => "boo"}) + + # We can't predict the order the hash is processed in... + ["foo=baz;bar=boo", "bar=boo;foo=baz"].should be_include s end describe "when calling inclusive?" do diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb index 874f70a8d..52ebb34f9 100755 --- a/spec/unit/provider/augeas/augeas_spec.rb +++ b/spec/unit/provider/augeas/augeas_spec.rb @@ -353,7 +353,7 @@ describe provider_class do @augeas_stub = stub("augeas") @provider.aug = @augeas_stub - @augeas_stub.stubs("get").with("/augeas/version").returns("0.7.2") + @augeas_stub.stubs("get").with("/augeas/version").returns("0.10.0") @augeas_stub.stubs(:set).returns(true) @augeas_stub.stubs(:save).returns(true) end @@ -467,7 +467,7 @@ describe provider_class do @augeas = stub("augeas") @provider.aug= @augeas @provider.stubs(:get_augeas_version).returns("0.3.5") - @augeas.stubs(:match).with("/augeas/events/saved") + @augeas.stubs(:match).with("/augeas/events/saved").returns([]) end it "should handle set commands" do diff --git a/spec/unit/provider/group/pw_spec.rb b/spec/unit/provider/group/pw_spec.rb new file mode 100755 index 000000000..3dfc5ec71 --- /dev/null +++ b/spec/unit/provider/group/pw_spec.rb @@ -0,0 +1,81 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +provider_class = Puppet::Type.type(:group).provider(:pw) + +describe provider_class do + let :resource do + Puppet::Type.type(:group).new(:name => "testgroup", :provider => :pw) + end + + let :provider do + resource.provider + end + + describe "when creating groups" do + let :provider do + prov = resource.provider + prov.expects(:exists?).returns nil + prov + end + + it "should run pw with no additional flags when no properties are given" do + provider.addcmd.must == [provider_class.command(:pw), "groupadd", "testgroup"] + provider.expects(:execute).with([provider_class.command(:pw), "groupadd", "testgroup"]) + provider.create + end + + it "should use -o when allowdupe is enabled" do + resource[:allowdupe] = true + provider.expects(:execute).with(includes("-o")) + provider.create + end + + it "should use -g with the correct argument when the gid property is set" do + resource[:gid] = 12345 + provider.expects(:execute).with(all_of(includes("-g"), includes(12345))) + provider.create + end + + it "should use -M with the correct argument when the members property is set" do + resource[:members] = "user1" + provider.expects(:execute).with(all_of(includes("-M"), includes("user1"))) + provider.create + end + + it "should use -M with all the given users when the members property is set to an array" do + resource[:members] = ["user1", "user2"] + provider.expects(:execute).with(all_of(includes("-M"), includes("user1,user2"))) + provider.create + end + end + + describe "when deleting groups" do + it "should run pw with no additional flags" do + provider.expects(:exists?).returns true + provider.deletecmd.must == [provider_class.command(:pw), "groupdel", "testgroup"] + provider.expects(:execute).with([provider_class.command(:pw), "groupdel", "testgroup"]) + provider.delete + end + end + + describe "when modifying groups" do + it "should run pw with the correct arguments" do + provider.modifycmd("gid", 12345).must == [provider_class.command(:pw), "groupmod", "testgroup", "-g", 12345] + provider.expects(:execute).with([provider_class.command(:pw), "groupmod", "testgroup", "-g", 12345]) + provider.gid = 12345 + end + + it "should use -M with the correct argument when the members property is changed" do + resource[:members] = "user1" + provider.expects(:execute).with(all_of(includes("-M"), includes("user2"))) + provider.members = "user2" + end + + it "should use -M with all the given users when the members property is changed with an array" do + resource[:members] = ["user1", "user2"] + provider.expects(:execute).with(all_of(includes("-M"), includes("user3,user4"))) + provider.members = ["user3", "user4"] + end + end +end diff --git a/spec/unit/provider/nameservice/directoryservice_spec.rb b/spec/unit/provider/nameservice/directoryservice_spec.rb index 7a83d7f20..c585b626f 100755 --- a/spec/unit/provider/nameservice/directoryservice_spec.rb +++ b/spec/unit/provider/nameservice/directoryservice_spec.rb @@ -95,3 +95,105 @@ describe 'DirectoryService.get_exec_preamble' do Puppet::Provider::NameService::DirectoryService.get_exec_preamble('-list').should include("-plist") end end + +describe 'DirectoryService password behavior' do + # The below is a binary plist containing a ShadowHashData key which CONTAINS + # another binary plist. The nested binary plist contains a 'SALTED-SHA512' + # key that contains a base64 encoded salted-SHA512 password hash... + let (:binary_plist) { "bplist00\324\001\002\003\004\005\006\a\bXCRAM-MD5RNT]SALTED-SHA512[RECOVERABLEO\020 \231k2\3360\200GI\201\355J\216\202\215y\243\001\206J\300\363\032\031\022\006\2359\024\257\217<\361O\020\020F\353\at\377\277\226\276c\306\254\031\037J(\235O\020D\335\006{\3744g@\377z\204\322\r\332t\021\330\n\003\246K\223\356\034!P\261\305t\035\346\352p\206\003n\247MMA\310\301Z<\366\246\023\0161W3\340\357\000\317T\t\301\311+\204\246L7\276\370\320*\245O\021\002\000k\024\221\270x\353\001\237\346D}\377?\265]\356+\243\v[\350\316a\340h\376<\322\266\327\016\306n\272r\t\212A\253L\216\214\205\016\241 [\360/\335\002#\\A\372\241a\261\346\346\\\251\330\312\365\016\n\341\017\016\225&;\322\\\004*\ru\316\372\a \362?8\031\247\231\030\030\267\315\023\v\343{@\227\301s\372h\212\000a\244&\231\366\nt\277\2036,\027bZ+\223W\212g\333`\264\331N\306\307\362\257(^~ b\262\247&\231\261t\341\231%\244\247\203eOt\365\271\201\273\330\350\363C^A\327F\214!\217hgf\e\320k\260n\315u~\336\371M\t\235k\230S\375\311\303\240\351\037d\273\321y\335=K\016`_\317\230\2612_\023K\036\350\v\232\323Y\310\317_\035\227%\237\v\340\023\016\243\233\025\306:\227\351\370\364x\234\231\266\367\016w\275\333-\351\210}\375x\034\262\272kRuHa\362T/F!\347B\231O`K\304\037'k$$\245h)e\363\365mT\b\317\\2\361\026\351\254\375Jl1~\r\371\267\352\2322I\341\272\376\243^Un\266E7\230[VocUJ\220N\2116D/\025f=\213\314\325\vG}\311\360\377DT\307m\261&\263\340\272\243_\020\271rG^BW\210\030l\344\0324\335\233\300\023\272\225Im\330\n\227*Yv[\006\315\330y'\a\321\373\273A\240\305F{S\246I#/\355\2425\031\031GGF\270y\n\331\004\023G@\331\000\361\343\350\264$\032\355_\210y\000\205\342\375\212q\024\004\026W:\205 \363v?\035\270L-\270=\022\323\2003\v\336\277\t\237\356\374\n\267n\003\367\342\330;\371S\326\016`B6@Njm>\240\021%\336\345\002(P\204Yn\3279l\0228\264\254\304\2528t\372h\217\347sA\314\345\245\337)]\000\b\000\021\000\032\000\035\000+\0007\000Z\000m\000\264\000\000\000\000\000\000\002\001\000\000\000\000\000\000\000\t\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\270" } + + # The below is a base64 encoded salted-SHA512 password hash. + let (:pw_string) { "\335\006{\3744g@\377z\204\322\r\332t\021\330\n\003\246K\223\356\034!P\261\305t\035\346\352p\206\003n\247MMA\310\301Z<\366\246\023\0161W3\340\357\000\317T\t\301\311+\204\246L7\276\370\320*\245" } + + # The below is a salted-SHA512 password hash in hex. + let (:sha512_hash) { 'dd067bfc346740ff7a84d20dda7411d80a03a64b93ee1c2150b1c5741de6ea7086036ea74d4d41c8c15a3cf6a6130e315733e0ef00cf5409c1c92b84a64c37bef8d02aa5' } + + let :plist_path do + '/var/db/dslocal/nodes/Default/users/jeff.plist' + end + + let :ds_provider do + Puppet::Provider::NameService::DirectoryService + end + + let :shadow_hash_data do + {'ShadowHashData' => [StringIO.new(binary_plist)]} + end + + subject do + Puppet::Provider::NameService::DirectoryService + end + + before :each do + subject.expects(:get_macosx_version_major).returns("10.7") + end + + it 'should execute convert_binary_to_xml once when getting the password on >= 10.7' do + subject.expects(:convert_binary_to_xml).returns({'SALTED-SHA512' => StringIO.new(pw_string)}) + File.expects(:exists?).with(plist_path).once.returns(true) + Plist.expects(:parse_xml).returns(shadow_hash_data) + # On Mac OS X 10.7 we first need to convert to xml when reading the password + subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path) + subject.get_password('uid', 'jeff') + end + + it 'should fail if a salted-SHA512 password hash is not passed in >= 10.7' do + expect { + subject.set_password('jeff', 'uid', 'badpassword') + }.should raise_error(RuntimeError, /OS X 10.7 requires a Salted SHA512 hash password of 136 characters./) + end + + it 'should convert xml-to-binary and binary-to-xml when setting the pw on >= 10.7' do + subject.expects(:convert_binary_to_xml).returns({'SALTED-SHA512' => StringIO.new(pw_string)}) + subject.expects(:convert_xml_to_binary).returns(binary_plist) + File.expects(:exists?).with(plist_path).once.returns(true) + Plist.expects(:parse_xml).returns(shadow_hash_data) + # On Mac OS X 10.7 we first need to convert to xml + subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path) + # And again back to a binary plist or DirectoryService will complain + subject.expects(:plutil).with('-convert', 'binary1', plist_path) + Plist::Emit.expects(:save_plist).with(shadow_hash_data, plist_path) + subject.set_password('jeff', 'uid', sha512_hash) + end +end + +describe '(#4855) directoryservice group resource failure' do + let :provider_class do + Puppet::Type.type(:group).provider(:directoryservice) + end + + let :group_members do + ['root','jeff'] + end + + let :user_account do + ['root'] + end + + let :stub_resource do + stub('resource') + end + + subject do + provider_class.new(stub_resource) + end + + before :each do + @resource = stub("resource") + @provider = provider_class.new(@resource) + end + + it 'should delete a group member if the user does not exist' do + stub_resource.stubs(:[]).with(:name).returns('fake_group') + stub_resource.stubs(:name).returns('fake_group') + subject.expects(:execute).with([:dseditgroup, '-o', 'edit', '-n', '.', + '-d', 'jeff', + 'fake_group']).raises(Puppet::ExecutionFailure, + 'it broke') + subject.expects(:execute).with([:dscl, '.', '-delete', + '/Groups/fake_group', 'GroupMembership', + 'jeff']) + subject.remove_unwanted_members(group_members, user_account) + end +end + diff --git a/spec/unit/provider/package/pip_spec.rb b/spec/unit/provider/package/pip_spec.rb index 97b3b5e73..7849ec49c 100755 --- a/spec/unit/provider/package/pip_spec.rb +++ b/spec/unit/provider/package/pip_spec.rb @@ -175,6 +175,13 @@ describe provider_class do expect { @provider.method(:lazy_pip).call("freeze") }.to raise_error(NoMethodError) end + it "should output a useful error message if pip is missing" do + @provider.expects(:pip).with('freeze').raises(NoMethodError) + @provider.expects(:which).with('pip').returns(nil) + expect { @provider.method(:lazy_pip).call("freeze") }. + to raise_error(NoMethodError, 'Could not locate the pip command.') + end + end end diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb index 601c24009..5148ac079 100755 --- a/spec/unit/provider/package/yum_spec.rb +++ b/spec/unit/provider/package/yum_spec.rb @@ -30,16 +30,19 @@ describe provider do @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, 'mypackage') @provider.install end + it 'should use :install to update' do @provider.expects(:install) @provider.update end + it 'should be able to set version' do @resource.stubs(:should).with(:ensure).returns '1.2' @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, 'mypackage-1.2') @provider.stubs(:query).returns :ensure => '1.2' @provider.install end + it 'should be able to downgrade' do @resource.stubs(:should).with(:ensure).returns '1.0' @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :downgrade, 'mypackage-1.0') @@ -53,6 +56,7 @@ describe provider do @provider.expects(:yum).with('-y', :erase, 'mypackage') @provider.purge end + it 'should use rpm to uninstall' do @provider.expects(:rpm).with('-e', 'mypackage-1-1.i386') @provider.uninstall @@ -62,5 +66,45 @@ describe provider do it 'should be versionable' do provider.should be_versionable end -end + describe '#latest' do + describe 'when latest_info is nil' do + before :each do + @provider.stubs(:latest_info).returns(nil) + end + + it 'raises if ensure is absent and latest_info is nil' do + @provider.stubs(:properties).returns({:ensure => :absent}) + + expect { @provider.latest }.to raise_error( + Puppet::DevError, + 'Tried to get latest on a missing package' + ) + end + + it 'returns the ensure value if the package is not already installed' do + @provider.stubs(:properties).returns({:ensure => '3.4.5'}) + + @provider.latest.should == '3.4.5' + end + end + + describe 'when latest_info is populated' do + before :each do + @provider.stubs(:latest_info).returns({ + :name => 'mypackage', + :epoch => '1', + :version => '2.3.4', + :release => '5', + :arch => 'i686', + :provider => :yum, + :ensure => '2.3.4-5' + }) + end + + it 'includes the epoch in the version string' do + @provider.latest.should == '1:2.3.4-5' + end + end + end +end diff --git a/spec/unit/provider/service/debian_spec.rb b/spec/unit/provider/service/debian_spec.rb index 4e3d30d61..f858568dc 100755 --- a/spec/unit/provider/service/debian_spec.rb +++ b/spec/unit/provider/service/debian_spec.rb @@ -88,6 +88,21 @@ describe provider_class do @provider.enabled?.should == :true end + context "when invoke-rc.d exits with 105 status" do + it "links count is 4" do + @provider.stubs(:system) + $CHILD_STATUS.stubs(:exitstatus).returns(105) + @provider.stubs(:get_start_link_count).returns(4) + @provider.enabled?.should == :true + end + it "links count is less than 4" do + @provider.stubs(:system) + $CHILD_STATUS.stubs(:exitstatus).returns(105) + @provider.stubs(:get_start_link_count).returns(3) + @provider.enabled?.should == :false + end + end + # pick a range of non-[104.106] numbers, strings and booleans to test with. [-100, -1, 0, 1, 100, "foo", "", :true, :false].each do |exitstatus| it "should return false when invoke-rc.d exits with #{exitstatus} status" do diff --git a/spec/unit/provider/service/launchd_spec.rb b/spec/unit/provider/service/launchd_spec.rb index 04c1f466f..8651ac562 100755 --- a/spec/unit/provider/service/launchd_spec.rb +++ b/spec/unit/provider/service/launchd_spec.rb @@ -37,22 +37,21 @@ describe Puppet::Type.type(:service).provider(:launchd) do describe "when checking whether the service is enabled on OS X 10.5" do it "should return true in if the job plist says disabled is false" do - Facter.stubs(:value).with(:macosx_productversion_major).returns('10.5') - Facter.stubs(:value).with(:kernel).returns('Darwin') - Facter.stubs(:value).with(:macaddress).returns('') - Facter.stubs(:value).with(:arp).returns('') + subject.expects(:has_macosx_plist_overrides?).returns(false) subject.expects(:plist_from_label).with(joblabel).returns(["foo", {"Disabled" => false}]) subject.expects(:resource).returns({:name => joblabel}) subject.enabled?.should == :true end it "should return true in if the job plist has no disabled key" do + subject.expects(:has_macosx_plist_overrides?).returns(false) subject.expects(:resource).returns({:name => joblabel}) - subject.stubs(:plist_from_label).returns(["foo", {}]) + subject.expects(:plist_from_label).returns(["foo", {}]) subject.enabled?.should == :true end it "should return false in if the job plist says disabled is true" do + subject.expects(:has_macosx_plist_overrides?).returns(false) subject.expects(:resource).returns({:name => joblabel}) - subject.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) + subject.expects(:plist_from_label).returns(["foo", {"Disabled" => true}]) subject.enabled?.should == :false end end @@ -61,7 +60,7 @@ describe Puppet::Type.type(:service).provider(:launchd) do it "should return true if the job plist says disabled is true and the global overrides says disabled is false" do provider.expects(:get_macosx_version_major).returns("10.6") subject.expects(:plist_from_label).returns([joblabel, {"Disabled" => true}]) - provider.stubs(:read_plist).returns({joblabel => {"Disabled" => false}}) + provider.expects(:read_plist).returns({joblabel => {"Disabled" => false}}) FileTest.expects(:file?).with(launchd_overrides).returns(true) subject.stubs(:resource).returns({:name => joblabel}) subject.enabled?.should == :true @@ -69,7 +68,7 @@ describe Puppet::Type.type(:service).provider(:launchd) do it "should return false if the job plist says disabled is false and the global overrides says disabled is true" do provider.expects(:get_macosx_version_major).returns("10.6") subject.expects(:plist_from_label).returns([joblabel, {"Disabled" => false}]) - provider.stubs(:read_plist).returns({joblabel => {"Disabled" => true}}) + provider.expects(:read_plist).returns({joblabel => {"Disabled" => true}}) FileTest.expects(:file?).with(launchd_overrides).returns(true) subject.stubs(:resource).returns({:name => joblabel}) subject.enabled?.should == :false @@ -77,7 +76,7 @@ describe Puppet::Type.type(:service).provider(:launchd) do it "should return true if the job plist and the global overrides have no disabled keys" do provider.expects(:get_macosx_version_major).returns("10.6") subject.expects(:plist_from_label).returns([joblabel, {}]) - provider.stubs(:read_plist).returns({}) + provider.expects(:read_plist).returns({}) FileTest.expects(:file?).with(launchd_overrides).returns(true) subject.stubs(:resource).returns({:name => joblabel}) subject.enabled?.should == :true @@ -87,61 +86,69 @@ describe Puppet::Type.type(:service).provider(:launchd) do describe "when starting the service" do it "should look for the relevant plist once" do subject.expects(:plist_from_label).returns([joblabel, {}]).once - subject.stubs(:enabled?).returns :true - subject.stubs(:execute).with([:launchctl, :load, joblabel]) + subject.expects(:enabled?).returns :true + subject.expects(:execute).with([:launchctl, :load, joblabel]) subject.stubs(:resource).returns({:name => joblabel}) subject.start end it "should execute 'launchctl load' once without writing to the plist if the job is enabled" do - subject.stubs(:plist_from_label).returns([joblabel, {}]) - subject.stubs(:enabled?).returns :true + subject.expects(:plist_from_label).returns([joblabel, {}]) + subject.expects(:enabled?).returns :true subject.expects(:execute).with([:launchctl, :load, joblabel]).once subject.stubs(:resource).returns({:name => joblabel}) subject.start end it "should execute 'launchctl load' with writing to the plist once if the job is disabled" do - subject.stubs(:plist_from_label).returns([joblabel, {}]) - subject.stubs(:enabled?).returns(:false) + subject.expects(:plist_from_label).returns([joblabel, {}]) + subject.expects(:enabled?).returns(:false) subject.stubs(:resource).returns({:name => joblabel}) subject.expects(:execute).with([:launchctl, :load, "-w", joblabel]).once subject.start end it "should disable the job once if the job is disabled and should be disabled at boot" do - subject.stubs(:plist_from_label).returns([joblabel, {"Disabled" => true}]) - subject.stubs(:enabled?).returns :false - subject.stubs(:execute).with([:launchctl, :load, "-w", joblabel]) + subject.expects(:plist_from_label).returns([joblabel, {"Disabled" => true}]) + subject.expects(:enabled?).returns :false + subject.expects(:execute).with([:launchctl, :load, "-w", joblabel]) subject.stubs(:resource).returns({:name => joblabel, :enable => :false}) subject.expects(:disable).once subject.start end + it "(#2773) should execute 'launchctl load -w' if the job is enabled but stopped" do + subject.expects(:plist_from_label).returns([joblabel, {}]) + subject.expects(:enabled?).returns(:true) + subject.expects(:status).returns(:stopped) + subject.expects(:resource).returns({:name => joblabel}).twice + subject.expects(:execute).with([:launchctl, :load, '-w', joblabel]) + subject.start + end end describe "when stopping the service" do it "should look for the relevant plist once" do subject.expects(:plist_from_label).returns([joblabel, {}]).once - subject.stubs(:enabled?).returns :true - subject.stubs(:execute).with([:launchctl, :unload, '-w', joblabel]) + subject.expects(:enabled?).returns :true + subject.expects(:execute).with([:launchctl, :unload, '-w', joblabel]) subject.stubs(:resource).returns({:name => joblabel}) subject.stop end it "should execute 'launchctl unload' once without writing to the plist if the job is disabled" do - subject.stubs(:plist_from_label).returns([joblabel, {}]) - subject.stubs(:enabled?).returns :false + subject.expects(:plist_from_label).returns([joblabel, {}]) + subject.expects(:enabled?).returns :false subject.expects(:execute).with([:launchctl, :unload, joblabel]).once subject.stubs(:resource).returns({:name => joblabel}) subject.stop end it "should execute 'launchctl unload' with writing to the plist once if the job is enabled" do - subject.stubs(:plist_from_label).returns([joblabel, {}]) - subject.stubs(:enabled?).returns :true + subject.expects(:plist_from_label).returns([joblabel, {}]) + subject.expects(:enabled?).returns :true subject.expects(:execute).with([:launchctl, :unload, '-w', joblabel]).once subject.stubs(:resource).returns({:name => joblabel}) subject.stop end it "should enable the job once if the job is enabled and should be enabled at boot" do - subject.stubs(:plist_from_label).returns([joblabel, {"Disabled" => false}]) - subject.stubs(:enabled?).returns :true - subject.stubs(:execute).with([:launchctl, :unload, "-w", joblabel]) + subject.expects(:plist_from_label).returns([joblabel, {"Disabled" => false}]) + subject.expects(:enabled?).returns :true + subject.expects(:execute).with([:launchctl, :unload, "-w", joblabel]) subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) subject.expects(:enable).once subject.stop @@ -151,15 +158,15 @@ describe Puppet::Type.type(:service).provider(:launchd) do describe "when enabling the service" do it "should look for the relevant plist once" do ### Do we need this test? Differentiating it? subject.expects(:plist_from_label).returns([joblabel, {}]).once - subject.stubs(:enabled?).returns :false - subject.stubs(:execute).with([:launchctl, :unload, joblabel]) + subject.expects(:enabled?).returns :false + subject.expects(:execute).with([:launchctl, :unload, joblabel]) subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) subject.stop end it "should check if the job is enabled once" do - subject.stubs(:plist_from_label).returns([joblabel, {}]).once + subject.expects(:plist_from_label).returns([joblabel, {}]).once subject.expects(:enabled?).once - subject.stubs(:execute).with([:launchctl, :unload, joblabel]) + subject.expects(:execute).with([:launchctl, :unload, joblabel]) subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) subject.stop end @@ -168,8 +175,8 @@ describe Puppet::Type.type(:service).provider(:launchd) do describe "when disabling the service" do it "should look for the relevant plist once" do subject.expects(:plist_from_label).returns([joblabel, {}]).once - subject.stubs(:enabled?).returns :true - subject.stubs(:execute).with([:launchctl, :unload, '-w', joblabel]) + subject.expects(:enabled?).returns :true + subject.expects(:execute).with([:launchctl, :unload, '-w', joblabel]) subject.stubs(:resource).returns({:name => joblabel, :enable => :false}) subject.stop end @@ -177,8 +184,8 @@ describe Puppet::Type.type(:service).provider(:launchd) do describe "when enabling the service on OS X 10.6" do it "should write to the global launchd overrides file once" do - provider.stubs(:get_macosx_version_major).returns("10.6") - provider.stubs(:read_plist).returns({}) + provider.expects(:get_macosx_version_major).returns("10.6") + provider.expects(:read_plist).returns({}) Plist::Emit.expects(:save_plist).once subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) subject.enable @@ -200,15 +207,13 @@ describe Puppet::Type.type(:service).provider(:launchd) do provider.instance_variable_set(:@macosx_version_major, nil) end it "should display a deprecation warning" do - Facter.stubs(:value).with(:macosx_productversion_major).returns(nil) - Facter.stubs(:value).with(:kernel).returns('Darwin') - Facter.stubs(:value).with(:macosx_productversion).returns('10.5.8') + Facter.expects(:value).with(:macosx_productversion_major).returns(nil) + Facter.expects(:value).with(:macosx_productversion).returns('10.5.8') + Facter.expects(:loadfacts) Puppet::Util::Warnings.expects(:maybe_log) - provider.stubs(:read_plist).returns({joblabel => {"Disabled" => false}}) - subject.stubs(:plist_from_label).returns([joblabel, {"Disabled" => false}]) - subject.stubs(:enabled?).returns :false - subject.stubs(:execute).with([:launchctl, :load, '-w', joblabel]).returns('') - File.stubs(:open).returns('') + subject.expects(:plist_from_label).returns([joblabel, {"Disabled" => false}]) + subject.expects(:enabled?).returns :false + File.expects(:open).returns('') subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) subject.enable end diff --git a/spec/unit/provider/service/smf_spec.rb b/spec/unit/provider/service/smf_spec.rb index dc7438cae..752702940 100755 --- a/spec/unit/provider/service/smf_spec.rb +++ b/spec/unit/provider/service/smf_spec.rb @@ -92,7 +92,7 @@ describe provider_class do it "should always execute external command 'svcadm enable /system/myservice'" do @provider.stubs(:status).returns :running - @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "/system/myservice"], true) + @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "-s", "/system/myservice"], true) @provider.start end @@ -112,7 +112,7 @@ describe provider_class do it "should import the manifest if service is missing" do @provider.expects(:svccfg).with(:import, "/tmp/myservice.xml") - @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "/system/myservice"], true) + @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "-s", "/system/myservice"], true) @provider.expects(:svcs).with('-H', '-o', 'state,nstate', "/system/myservice").returns("online\t-") @provider.start end @@ -125,7 +125,7 @@ describe provider_class do describe "when stopping" do it "should execute external command 'svcadm disable /system/myservice'" do - @provider.expects(:texecute).with(:stop, ["/usr/sbin/svcadm", :disable, "/system/myservice"], true) + @provider.expects(:texecute).with(:stop, ["/usr/sbin/svcadm", :disable, "-s", "/system/myservice"], true) @provider.stop end end diff --git a/spec/unit/provider/user/pw_spec.rb b/spec/unit/provider/user/pw_spec.rb new file mode 100755 index 000000000..495fef35b --- /dev/null +++ b/spec/unit/provider/user/pw_spec.rb @@ -0,0 +1,183 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +provider_class = Puppet::Type.type(:user).provider(:pw) + +describe provider_class do + let :resource do + Puppet::Type.type(:user).new(:name => "testuser", :provider => :pw) + end + + describe "when creating users" do + let :provider do + prov = resource.provider + prov.expects(:exists?).returns nil + prov + end + + it "should run pw with no additional flags when no properties are given" do + provider.addcmd.must == [provider_class.command(:pw), "useradd", "testuser"] + provider.expects(:execute).with([provider_class.command(:pw), "useradd", "testuser"]) + provider.create + end + + it "should use -o when allowdupe is enabled" do + resource[:allowdupe] = true + provider.expects(:execute).with(includes("-o")) + provider.create + end + + it "should use -c with the correct argument when the comment property is set" do + resource[:comment] = "Testuser Name" + provider.expects(:execute).with(all_of(includes("-c"), includes("Testuser Name"))) + provider.create + end + + it "should use -e with the correct argument when the expiry property is set" do + resource[:expiry] = "2010-02-19" + provider.expects(:execute).with(all_of(includes("-e"), includes("19-02-2010"))) + provider.create + end + + it "should use -g with the correct argument when the gid property is set" do + resource[:gid] = 12345 + provider.expects(:execute).with(all_of(includes("-g"), includes(12345))) + provider.create + end + + it "should use -G with the correct argument when the groups property is set" do + resource[:groups] = "group1" + provider.expects(:execute).with(all_of(includes("-G"), includes("group1"))) + provider.create + end + + it "should use -G with all the given groups when the groups property is set to an array" do + resource[:groups] = ["group1", "group2"] + provider.expects(:execute).with(all_of(includes("-G"), includes("group1,group2"))) + provider.create + end + + it "should use -d with the correct argument when the home property is set" do + resource[:home] = "/home/testuser" + provider.expects(:execute).with(all_of(includes("-d"), includes("/home/testuser"))) + provider.create + end + + it "should use -m when the managehome property is enabled" do + resource[:managehome] = true + provider.expects(:execute).with(includes("-m")) + provider.create + end + + it "should call the password set function with the correct argument when the password property is set" do + resource[:password] = "*" + provider.expects(:execute) + provider.expects(:password=).with("*") + provider.create + end + + it "should use -s with the correct argument when the shell property is set" do + resource[:shell] = "/bin/sh" + provider.expects(:execute).with(all_of(includes("-s"), includes("/bin/sh"))) + provider.create + end + + it "should use -u with the correct argument when the uid property is set" do + resource[:uid] = 12345 + provider.expects(:execute).with(all_of(includes("-u"), includes(12345))) + provider.create + end + + # (#7500) -p should not be used to set a password (it means something else) + it "should not use -p when a password is given" do + resource[:password] = "*" + provider.addcmd.should_not include("-p") + provider.expects(:password=) + provider.expects(:execute).with(Not(includes("-p"))) + provider.create + end + end + + describe "when deleting users" do + it "should run pw with no additional flags" do + provider = resource.provider + provider.expects(:exists?).returns true + provider.deletecmd.must == [provider_class.command(:pw), "userdel", "testuser"] + provider.expects(:execute).with([provider_class.command(:pw), "userdel", "testuser"]) + provider.delete + end + end + + describe "when modifying users" do + let :provider do + resource.provider + end + + it "should run pw with the correct arguments" do + provider.modifycmd("uid", 12345).must == [provider_class.command(:pw), "usermod", "testuser", "-u", 12345] + provider.expects(:execute).with([provider_class.command(:pw), "usermod", "testuser", "-u", 12345]) + provider.uid = 12345 + end + + it "should use -c with the correct argument when the comment property is changed" do + resource[:comment] = "Testuser Name" + provider.expects(:execute).with(all_of(includes("-c"), includes("Testuser New Name"))) + provider.comment = "Testuser New Name" + end + + it "should use -e with the correct argument when the expiry property is changed" do + resource[:expiry] = "2010-02-19" + provider.expects(:execute).with(all_of(includes("-e"), includes("19-02-2011"))) + provider.expiry = "2011-02-19" + end + + it "should use -g with the correct argument when the gid property is changed" do + resource[:gid] = 12345 + provider.expects(:execute).with(all_of(includes("-g"), includes(54321))) + provider.gid = 54321 + end + + it "should use -G with the correct argument when the groups property is changed" do + resource[:groups] = "group1" + provider.expects(:execute).with(all_of(includes("-G"), includes("group2"))) + provider.groups = "group2" + end + + it "should use -G with all the given groups when the groups property is changed with an array" do + resource[:groups] = ["group1", "group2"] + provider.expects(:execute).with(all_of(includes("-G"), includes("group3,group4"))) + provider.groups = "group3,group4" + end + + it "should use -d with the correct argument when the home property is changed" do + resource[:home] = "/home/testuser" + provider.expects(:execute).with(all_of(includes("-d"), includes("/newhome/testuser"))) + provider.home = "/newhome/testuser" + end + + it "should use -m and -d with the correct argument when the home property is changed and managehome is enabled" do + resource[:home] = "/home/testuser" + resource[:managehome] = true + provider.expects(:execute).with(all_of(includes("-d"), includes("/newhome/testuser"), includes("-m"))) + provider.home = "/newhome/testuser" + end + + it "should call the password set function with the correct argument when the password property is changed" do + resource[:password] = "*" + provider.expects(:password=).with("!") + provider.password = "!" + end + + it "should use -s with the correct argument when the shell property is changed" do + resource[:shell] = "/bin/sh" + provider.expects(:execute).with(all_of(includes("-s"), includes("/bin/tcsh"))) + provider.shell = "/bin/tcsh" + end + + it "should use -u with the correct argument when the uid property is changed" do + resource[:uid] = 12345 + provider.expects(:execute).with(all_of(includes("-u"), includes(54321))) + provider.uid = 54321 + end + end +end diff --git a/spec/unit/provider/user/windows_adsi_spec.rb b/spec/unit/provider/user/windows_adsi_spec.rb index 6eff947e0..23fba9983 100755 --- a/spec/unit/provider/user/windows_adsi_spec.rb +++ b/spec/unit/provider/user/windows_adsi_spec.rb @@ -74,6 +74,7 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do user.stubs(:groups).returns(['group2', 'group3']) create = sequence('create') + user.expects(:password=).in_sequence(create) user.expects(:commit).in_sequence(create) user.expects(:set_groups).with('group1,group2', false).in_sequence(create) user.expects(:[]=).with('Description', 'a test user') diff --git a/spec/unit/rails_spec.rb b/spec/unit/rails_spec.rb index fe7fd8e29..60062db1c 100755 --- a/spec/unit/rails_spec.rb +++ b/spec/unit/rails_spec.rb @@ -88,169 +88,100 @@ describe Puppet::Rails, "when initializing a sqlite3 connection", :if => Puppet. end end -describe Puppet::Rails, "when initializing a mysql connection", :if => Puppet.features.rails? do - it "should provide the adapter, log_level, and host, port, username, password, database, and reconnect arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 45).to_s) - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("") - - Puppet::Rails.database_arguments.should == { - :adapter => "mysql", - :log_level => "testlevel", - :host => "testserver", - :username => "testuser", - :password => "testpassword", - :pool => pool_size, - :database => "testname", - :reconnect => true - } - end - - it "should provide the adapter, log_level, and host, port, username, password, database, socket, connections, and reconnect arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 12).to_s) - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") - - Puppet::Rails.database_arguments.should == { - :adapter => "mysql", - :log_level => "testlevel", - :host => "testserver", - :port => "9999", - :username => "testuser", - :password => "testpassword", - :pool => pool_size, - :database => "testname", - :socket => "testsocket", - :reconnect => true - } - end - - it "should provide the adapter, log_level, and host, port, username, password, database, socket, and connections arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 23).to_s) - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") - - Puppet::Rails.database_arguments.should == { - :adapter => "mysql", - :log_level => "testlevel", - :host => "testserver", - :port => "9999", - :username => "testuser", - :password => "testpassword", - :pool => pool_size, - :database => "testname", - :socket => "testsocket", - :reconnect => true - } - end - - it "should not provide the pool if dbconnections is 0, '0', or ''" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") - - Puppet.settings.stubs(:value).with(:dbconnections).returns(0) - Puppet::Rails.database_arguments.should_not be_include(:pool) - - Puppet.settings.stubs(:value).with(:dbconnections).returns('0') - Puppet::Rails.database_arguments.should_not be_include(:pool) - - Puppet.settings.stubs(:value).with(:dbconnections).returns('') - Puppet::Rails.database_arguments.should_not be_include(:pool) - end -end - -describe Puppet::Rails, "when initializing a postgresql connection", :if => Puppet.features.rails? do - it "should provide the adapter, log_level, and host, port, username, password, connections, and database arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("postgresql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 200).to_s) - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("") - - Puppet::Rails.database_arguments.should == { - :adapter => "postgresql", - :log_level => "testlevel", - :host => "testserver", - :port => "9999", - :username => "testuser", - :password => "testpassword", - :pool => pool_size, - :database => "testname", - :reconnect => true - } - end +['mysql','mysql2','postgresql'].each do |dbadapter| + describe Puppet::Rails, "when initializing a #{dbadapter} connection", :if => Puppet.features.rails? do + it "should provide the adapter, log_level, and host, port, username, password, database, and reconnect arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns(dbadapter) + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") + Puppet.settings.stubs(:value).with(:dbport).returns("") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 45).to_s) + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbsocket).returns("") + + Puppet::Rails.database_arguments.should == { + :adapter => dbadapter, + :log_level => "testlevel", + :host => "testserver", + :username => "testuser", + :password => "testpassword", + :pool => pool_size, + :database => "testname", + :reconnect => true + } + end - it "should provide the adapter, log_level, and host, port, username, password, database, connections, and socket arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("postgresql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 122).to_s) - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") + it "should provide the adapter, log_level, and host, port, username, password, database, socket, connections, and reconnect arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns(dbadapter) + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") + Puppet.settings.stubs(:value).with(:dbport).returns("9999") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 12).to_s) + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") + + Puppet::Rails.database_arguments.should == { + :adapter => dbadapter, + :log_level => "testlevel", + :host => "testserver", + :port => "9999", + :username => "testuser", + :password => "testpassword", + :pool => pool_size, + :database => "testname", + :socket => "testsocket", + :reconnect => true + } + end - Puppet::Rails.database_arguments.should == { - :adapter => "postgresql", - :log_level => "testlevel", - :host => "testserver", - :port => "9999", - :username => "testuser", - :password => "testpassword", - :pool => pool_size, - :database => "testname", - :socket => "testsocket", - :reconnect => true - } - end + it "should provide the adapter, log_level, and host, port, username, password, database, socket, and connections arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns(dbadapter) + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") + Puppet.settings.stubs(:value).with(:dbport).returns("9999") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 23).to_s) + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") + + Puppet::Rails.database_arguments.should == { + :adapter => dbadapter, + :log_level => "testlevel", + :host => "testserver", + :port => "9999", + :username => "testuser", + :password => "testpassword", + :pool => pool_size, + :database => "testname", + :socket => "testsocket", + :reconnect => true + } + end - it "should not provide the pool if dbconnections is 0, '0', or ''" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") + it "should not provide the pool if dbconnections is 0, '0', or ''" do + Puppet.settings.stubs(:value).with(:dbadapter).returns(dbadapter) + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") + Puppet.settings.stubs(:value).with(:dbport).returns("9999") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") - Puppet.settings.stubs(:value).with(:dbconnections).returns(0) - Puppet::Rails.database_arguments.should_not be_include(:pool) + Puppet.settings.stubs(:value).with(:dbconnections).returns(0) + Puppet::Rails.database_arguments.should_not be_include(:pool) - Puppet.settings.stubs(:value).with(:dbconnections).returns('0') - Puppet::Rails.database_arguments.should_not be_include(:pool) + Puppet.settings.stubs(:value).with(:dbconnections).returns('0') + Puppet::Rails.database_arguments.should_not be_include(:pool) - Puppet.settings.stubs(:value).with(:dbconnections).returns('') - Puppet::Rails.database_arguments.should_not be_include(:pool) + Puppet.settings.stubs(:value).with(:dbconnections).returns('') + Puppet::Rails.database_arguments.should_not be_include(:pool) + end end end @@ -292,7 +223,7 @@ describe Puppet::Rails, "when initializing an Oracle connection", :if => Puppet. end it "should not provide the pool if dbconnections is 0, '0', or ''" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") + Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") diff --git a/spec/unit/reports/store_spec.rb b/spec/unit/reports/store_spec.rb index 73a7e353f..5b752c4b7 100755 --- a/spec/unit/reports/store_spec.rb +++ b/spec/unit/reports/store_spec.rb @@ -3,6 +3,9 @@ require 'spec_helper' require 'puppet/reports' require 'time' +require 'pathname' +require 'tempfile' +require 'fileutils' processor = Puppet::Reports.report(:store) @@ -10,7 +13,7 @@ describe processor do describe "#process" do include PuppetSpec::Files before :each do - Puppet[:reportdir] = tmpdir('reports') << '/reports' + Puppet[:reportdir] = File.join(tmpdir('reports'), 'reports') @report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report2.6.x.yaml')).extend processor end @@ -26,5 +29,20 @@ describe processor do File.read(File.join(Puppet[:reportdir], @report.host, "201101061200.yaml")).should == @report.to_yaml end + + it "should write to the report directory in the correct sequence" do + # By doing things in this sequence we should protect against race + # conditions + Time.stubs(:now).returns(Time.parse("2011-01-06 12:00:00 UTC")) + writeseq = sequence("write") + file = mock "file" + Tempfile.expects(:new).in_sequence(writeseq).returns(file) + file.expects(:chmod).in_sequence(writeseq).with(0640) + file.expects(:print).with(@report.to_yaml).in_sequence(writeseq) + file.expects(:close).in_sequence(writeseq) + file.stubs(:path).returns(File.join(Dir.tmpdir, "foo123")) + FileUtils.expects(:mv).in_sequence(writeseq).with(File.join(Dir.tmpdir, "foo123"), File.join(Puppet[:reportdir], @report.host, "201101061200.yaml")) + @report.process + end end end diff --git a/spec/unit/ssl/certificate_factory_spec.rb b/spec/unit/ssl/certificate_factory_spec.rb index fc1b3a740..88521b0fb 100755 --- a/spec/unit/ssl/certificate_factory_spec.rb +++ b/spec/unit/ssl/certificate_factory_spec.rb @@ -52,19 +52,19 @@ describe Puppet::SSL::CertificateFactory do it "should have 24 hours grace on the start of the cert" do cert = subject.build(:server, csr, issuer, serial) - cert.not_before.should be_within(1).of(Time.now - 24*60*60) + cert.not_before.should be_within(30).of(Time.now - 24*60*60) end it "should set the default TTL of the certificate" do ttl = Puppet::SSL::CertificateFactory.ttl cert = subject.build(:server, csr, issuer, serial) - cert.not_after.should be_within(1).of(Time.now + ttl) + cert.not_after.should be_within(30).of(Time.now + ttl) end it "should respect a custom TTL for the CA" do Puppet[:ca_ttl] = 12 cert = subject.build(:server, csr, issuer, serial) - cert.not_after.should be_within(1).of(Time.now + 12) + cert.not_after.should be_within(30).of(Time.now + 12) end it "should build extensions for the certificate" do diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb index dd6434359..3f94407be 100755 --- a/spec/unit/ssl/host_spec.rb +++ b/spec/unit/ssl/host_spec.rb @@ -182,57 +182,48 @@ describe Puppet::SSL::Host do it "should cache the localhost instance" do host = stub 'host', :certificate => "eh", :key => 'foo' Puppet::SSL::Host.expects(:new).once.returns host - Puppet::SSL::Host.localhost.should == Puppet::SSL::Host.localhost end it "should be able to verify its certificate matches its key" do - Puppet::SSL::Host.new("foo").should respond_to(:certificate_matches_key?) + Puppet::SSL::Host.new("foo").should respond_to(:validate_certificate_with_key) end it "should consider the certificate invalid if it cannot find a key" do host = Puppet::SSL::Host.new("foo") + certificate = mock('cert', :fingerprint => 'DEADBEEF') + host.expects(:certificate).twice.returns certificate host.expects(:key).returns nil - - host.should_not be_certificate_matches_key + lambda { host.validate_certificate_with_key }.should raise_error(Puppet::Error, "No private key with which to validate certificate with fingerprint: DEADBEEF") end it "should consider the certificate invalid if it cannot find a certificate" do host = Puppet::SSL::Host.new("foo") - host.expects(:key).returns mock("key") + host.expects(:key).never host.expects(:certificate).returns nil - - host.should_not be_certificate_matches_key + lambda { host.validate_certificate_with_key }.should raise_error(Puppet::Error, "No certificate to validate.") end it "should consider the certificate invalid if the SSL certificate's key verification fails" do host = Puppet::SSL::Host.new("foo") - key = mock 'key', :content => "private_key" sslcert = mock 'sslcert' - certificate = mock 'cert', :content => sslcert - + certificate = mock 'cert', {:content => sslcert, :fingerprint => 'DEADBEEF'} host.stubs(:key).returns key host.stubs(:certificate).returns certificate - sslcert.expects(:check_private_key).with("private_key").returns false - - host.should_not be_certificate_matches_key + lambda { host.validate_certificate_with_key }.should raise_error(Puppet::Error, /DEADBEEF/) end it "should consider the certificate valid if the SSL certificate's key verification succeeds" do host = Puppet::SSL::Host.new("foo") - key = mock 'key', :content => "private_key" sslcert = mock 'sslcert' certificate = mock 'cert', :content => sslcert - host.stubs(:key).returns key host.stubs(:certificate).returns certificate - sslcert.expects(:check_private_key).with("private_key").returns true - - host.should be_certificate_matches_key + lambda{ host.validate_certificate_with_key }.should_not raise_error end describe "when specifying the CA location" do @@ -511,15 +502,13 @@ describe Puppet::SSL::Host do before do @realcert = mock 'certificate' @cert = stub 'cert', :content => @realcert - @host.stubs(:key).returns mock("key") - @host.stubs(:certificate_matches_key?).returns true + @host.stubs(:validate_certificate_with_key) end it "should find the CA certificate if it does not have a certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.stubs(:find).with("myname").returns @cert - @host.certificate end @@ -541,34 +530,22 @@ describe Puppet::SSL::Host do it "should find the key if it does not have one" do Puppet::SSL::Certificate.indirection.stubs(:find) @host.expects(:key).returns mock("key") - @host.certificate end it "should generate the key if one cannot be found" do Puppet::SSL::Certificate.indirection.stubs(:find) - @host.expects(:key).returns nil @host.expects(:generate_key) - @host.certificate end it "should find the certificate in the Certificate class and return the Puppet certificate instance" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.expects(:find).with("myname").returns @cert - @host.certificate.should equal(@cert) end - it "should fail if the found certificate does not match the private key" do - @host.expects(:certificate_matches_key?).returns false - - Puppet::SSL::Certificate.indirection.stubs(:find).returns @cert - - lambda { @host.certificate }.should raise_error(Puppet::Error) - end - it "should return any previously found certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.expects(:find).with("myname").returns(@cert).once diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index fe5c2e218..91a294d01 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -179,6 +179,11 @@ describe Puppet::Transaction::Report do @report.finalize_report metric(:resources, state.to_s).should == 3 end + + it "should provide 0 for states not in status" do + @report.finalize_report + metric(:resources, state.to_s).should == 0 + end end it "should mark the report as 'failed' if there are failing resources" do @@ -274,13 +279,14 @@ describe Puppet::Transaction::Report do resource = Puppet::Type.type(:notify).new(:name => "testing") catalog = Puppet::Resource::Catalog.new catalog.add_resource resource + catalog.version = 1234567 trans = catalog.apply @report = trans.report @report.finalize_report end - %w{changes time resources events}.each do |main| + %w{changes time resources events version}.each do |main| it "should include the key #{main} in the raw summary hash" do @report.raw_summary.should be_key main end @@ -291,6 +297,28 @@ describe Puppet::Transaction::Report do @report.raw_summary["time"]["last_run"].should == 1289390424 end + it "should include all resource statuses" do + resources_report = @report.raw_summary["resources"] + Puppet::Resource::Status::STATES.each do |state| + resources_report.should be_include(state.to_s) + end + end + + %w{total failure success}.each do |r| + it "should include event #{r}" do + events_report = @report.raw_summary["events"] + events_report.should be_include(r) + end + end + + it "should include config version" do + @report.raw_summary["version"]["config"].should == 1234567 + end + + it "should include puppet version" do + @report.raw_summary["version"]["puppet"].should == Puppet.version + end + %w{Changes Total Resources Time Events}.each do |main| it "should include information on #{main} in the textual summary" do @report.summary.should be_include(main) diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb index 9e85390eb..f5720e75a 100755 --- a/spec/unit/transaction_spec.rb +++ b/spec/unit/transaction_spec.rb @@ -436,6 +436,10 @@ describe Puppet::Transaction do @transaction.catalog.add_resource(dependent, dependent2) + # We enqueue them here just so we can check their blockers. This is done + # again in traverse. + graph.enqueue_roots + graph.blockers[dependent].should == 1 graph.blockers[dependent2].should == 1 @@ -453,6 +457,8 @@ describe Puppet::Transaction do @transaction.catalog.add_resource(dependent, dependent2) + graph.enqueue_roots + graph.blockers[dependent].should == 1 graph.blockers[dependent2].should == 1 @@ -510,70 +516,50 @@ describe Puppet::Transaction do end end - describe "when generating resources" do - it "should call 'generate' on all created resources" do - first = Puppet::Type.type(:notify).new(:name => "first") - second = Puppet::Type.type(:notify).new(:name => "second") - third = Puppet::Type.type(:notify).new(:name => "third") + describe "when generating resources before traversal" do + let(:catalog) { Puppet::Resource::Catalog.new } + let(:transaction) { Puppet::Transaction.new(catalog) } + let(:generator) { Puppet::Type.type(:notify).create :title => "generator" } + let(:generated) do + %w[a b c].map { |name| Puppet::Type.type(:notify).new(:name => name) } + end - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) + before :each do + catalog.add_resource generator + generator.stubs(:generate).returns generated + end - first.expects(:generate).returns [second] - second.expects(:generate).returns [third] - third.expects(:generate) + it "should call 'generate' on all created resources" do + generated.each { |res| res.expects(:generate) } - @transaction.generate_additional_resources(first) + transaction.add_dynamically_generated_resources end it "should finish all resources" do - generator = stub 'generator', :depthfirst? => true, :tags => [], :ref => "Some[resource]" - resource = stub 'resource', :tag => nil - - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) - - generator.expects(:generate).returns [resource] - - @catalog.expects(:add_resource).yields(resource) - - resource.expects(:finish) + generated.each { |res| res.expects(:finish) } - @transaction.generate_additional_resources(generator) + transaction.add_dynamically_generated_resources end it "should skip generated resources that conflict with existing resources" do - generator = mock 'generator', :tags => [] - resource = stub 'resource', :tag => nil - - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) + duplicate = generated.first + catalog.add_resource(duplicate) - generator.expects(:generate).returns [resource] + duplicate.expects(:finish).never - @catalog.expects(:add_resource).raises(Puppet::Resource::Catalog::DuplicateResourceError.new("foo")) + duplicate.expects(:info).with { |msg| msg =~ /Duplicate generated resource/ } - resource.expects(:finish).never - resource.expects(:info) # log that it's skipped - - @transaction.generate_additional_resources(generator) + transaction.add_dynamically_generated_resources end it "should copy all tags to the newly generated resources" do - child = stub 'child', :ref => "Some[child_resource]" - generator = stub 'resource', :tags => ["one", "two"], :ref => "Some[resource]" - - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) - - generator.stubs(:generate).returns [child] - @catalog.stubs(:add_resource) + generator.tag('one', 'two') - child.expects(:tag).with("one", "two") - child.expects(:finish) - generator.expects(:depthfirst?) + transaction.add_dynamically_generated_resources - @transaction.generate_additional_resources(generator) + generated.each do |res| + res.should be_tagged(generator.tags) + end end end diff --git a/spec/unit/type/file/mode_spec.rb b/spec/unit/type/file/mode_spec.rb index 2c6772aba..88881043c 100755 --- a/spec/unit/type/file/mode_spec.rb +++ b/spec/unit/type/file/mode_spec.rb @@ -21,7 +21,7 @@ describe Puppet::Type.type(:file).attrclass(:mode) do it "should not accept strings other than octal numbers" do expect do mode.value = 'readable please!' - end.to raise_error(Puppet::Error, /File modes can only be octal numbers/) + end.to raise_error(Puppet::Error, /The file mode specification is invalid/) end end diff --git a/spec/unit/type/file/source_spec.rb b/spec/unit/type/file/source_spec.rb index f77645770..50e3679ef 100755 --- a/spec/unit/type/file/source_spec.rb +++ b/spec/unit/type/file/source_spec.rb @@ -156,8 +156,10 @@ describe Puppet::Type.type(:file).attrclass(:source) do @resource = Puppet::Type.type(:file).new :path => @foobar @source = source.new(:resource => @resource) - @metadata = stub 'metadata', :owner => 100, :group => 200, :mode => 123, :checksum => "{md5}asdfasdf", :ftype => "file" + @metadata = stub 'metadata', :owner => 100, :group => 200, :mode => 123, :checksum => "{md5}asdfasdf", :ftype => "file", :source => @foobar @source.stubs(:metadata).returns @metadata + + Puppet.features.stubs(:root?).returns true end it "should fail if there is no metadata" do @@ -184,11 +186,10 @@ describe Puppet::Type.type(:file).attrclass(:source) do describe "and the source is a file" do before do @metadata.stubs(:ftype).returns "file" + Puppet.features.stubs(:microsoft_windows?).returns false end it "should copy the metadata's owner, group, checksum, and mode to the resource if they are not set on the resource" do - Puppet.features.expects(:root?).returns true - @source.copy_source_values @resource[:owner].must == 100 @@ -221,6 +222,30 @@ describe Puppet::Type.type(:file).attrclass(:source) do @resource[:owner].should be_nil end end + + describe "on Windows" do + before :each do + Puppet.features.stubs(:microsoft_windows?).returns true + end + + it "should not copy owner and group from remote sources" do + @source.stubs(:local?).returns false + + @source.copy_source_values + + @resource[:owner].must be_nil + @resource[:group].must be_nil + end + + it "should copy owner and group from local sources" do + @source.stubs(:local?).returns true + + @source.copy_source_values + + @resource[:owner].must == 100 + @resource[:group].must == 200 + end + end end describe "and the source is a link" do diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb index cf278b4f8..d4df006b3 100755 --- a/spec/unit/type/file_spec.rb +++ b/spec/unit/type/file_spec.rb @@ -500,8 +500,9 @@ describe Puppet::Type.type(:file) do end it "should not copy values to the child which were set by the source" do - file[:source] = File.expand_path(__FILE__) - metadata = stub 'metadata', :owner => "root", :group => "root", :mode => 0755, :ftype => "file", :checksum => "{md5}whatever" + source = File.expand_path(__FILE__) + file[:source] = source + metadata = stub 'metadata', :owner => "root", :group => "root", :mode => 0755, :ftype => "file", :checksum => "{md5}whatever", :source => source file.parameter(:source).stubs(:metadata).returns metadata file.parameter(:source).copy_source_values @@ -841,7 +842,7 @@ describe Puppet::Type.type(:file) do describe "and multiple sources are provided" do let(:sources) do h = {} - %w{/one /two /three /four}.each do |key| + %w{/a /b /c /d}.each do |key| h[key] = URI.unescape(Puppet::Util.path_to_uri(File.expand_path(key)).to_s) end h @@ -850,11 +851,11 @@ describe Puppet::Type.type(:file) do describe "and :sourceselect is set to :first" do it "should create file instances for the results for the first source to return any values" do data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar") - file[:source] = sources.keys.map { |key| File.expand_path(key) } - file.expects(:perform_recursion).with(sources['/one']).returns nil - file.expects(:perform_recursion).with(sources['/two']).returns [] - file.expects(:perform_recursion).with(sources['/three']).returns [data] - file.expects(:perform_recursion).with(sources['/four']).never + file[:source] = sources.keys.sort.map { |key| File.expand_path(key) } + file.expects(:perform_recursion).with(sources['/a']).returns nil + file.expects(:perform_recursion).with(sources['/b']).returns [] + file.expects(:perform_recursion).with(sources['/c']).returns [data] + file.expects(:perform_recursion).with(sources['/d']).never file.expects(:newchild).with("foobar").returns @resource file.recurse_remote({}) end @@ -867,22 +868,22 @@ describe Puppet::Type.type(:file) do it "should return every found file that is not in a previous source" do klass = Puppet::FileServing::Metadata - file[:source] = %w{/one /two /three /four}.map {|f| File.expand_path(f) } + file[:source] = %w{/a /b /c /d}.map {|f| File.expand_path(f) } file.stubs(:newchild).returns @resource - one = [klass.new("/one", :relative_path => "a")] - file.expects(:perform_recursion).with(sources['/one']).returns one + one = [klass.new("/a", :relative_path => "a")] + file.expects(:perform_recursion).with(sources['/a']).returns one file.expects(:newchild).with("a").returns @resource - two = [klass.new("/two", :relative_path => "a"), klass.new("/two", :relative_path => "b")] - file.expects(:perform_recursion).with(sources['/two']).returns two + two = [klass.new("/b", :relative_path => "a"), klass.new("/b", :relative_path => "b")] + file.expects(:perform_recursion).with(sources['/b']).returns two file.expects(:newchild).with("b").returns @resource - three = [klass.new("/three", :relative_path => "a"), klass.new("/three", :relative_path => "c")] - file.expects(:perform_recursion).with(sources['/three']).returns three + three = [klass.new("/c", :relative_path => "a"), klass.new("/c", :relative_path => "c")] + file.expects(:perform_recursion).with(sources['/c']).returns three file.expects(:newchild).with("c").returns @resource - file.expects(:perform_recursion).with(sources['/four']).returns [] + file.expects(:perform_recursion).with(sources['/d']).returns [] file.recurse_remote({}) end @@ -1253,6 +1254,36 @@ describe Puppet::Type.type(:file) do end describe "when autorequiring" do + describe "target" do + it "should require file resource when specified with the target property" do + file = described_class.new(:path => File.expand_path("/foo"), :ensure => :directory) + link = described_class.new(:path => File.expand_path("/bar"), :ensure => :symlink, :target => File.expand_path("/foo")) + catalog.add_resource file + catalog.add_resource link + reqs = link.autorequire + reqs.size.must == 1 + reqs[0].source.must == file + reqs[0].target.must == link + end + + it "should require file resource when specified with the ensure property" do + file = described_class.new(:path => File.expand_path("/foo"), :ensure => :directory) + link = described_class.new(:path => File.expand_path("/bar"), :ensure => File.expand_path("/foo")) + catalog.add_resource file + catalog.add_resource link + reqs = link.autorequire + reqs.size.must == 1 + reqs[0].source.must == file + reqs[0].target.must == link + end + + it "should not require target if target is not managed" do + link = described_class.new(:path => File.expand_path('/foo'), :ensure => :symlink, :target => '/bar') + catalog.add_resource link + link.autorequire.size.should == 0 + end + end + describe "directories" do it "should autorequire its parent directory" do dir = described_class.new(:path => File.dirname(path)) diff --git a/spec/unit/type/host_spec.rb b/spec/unit/type/host_spec.rb index 602c428af..3cbe56943 100755 --- a/spec/unit/type/host_spec.rb +++ b/spec/unit/type/host_spec.rb @@ -70,10 +70,537 @@ describe host do proc { @class.new(:name => "foo", :ip => '192.168.0.300') }.should raise_error end + it "should reject over-long IPv4 addresses" do + expect { @class.new(:name => "foo", :ip => '10.10.10.10.10') }.to raise_error + end + it "should not accept malformed IP addresses like 2001:0dg8:85a3:08d3:1319:8a2e:0370:7344" do proc { @class.new(:name => "foo", :ip => '2001:0dg8:85a3:08d3:1319:8a2e:0370:7344') }.should raise_error end + # Assorted, annotated IPv6 passes. + ["::1", # loopback, compressed, non-routable + "::", # unspecified, compressed, non-routable + "0:0:0:0:0:0:0:1", # loopback, full + "0:0:0:0:0:0:0:0", # unspecified, full + "2001:DB8:0:0:8:800:200C:417A", # unicast, full + "FF01:0:0:0:0:0:0:101", # multicast, full + "2001:DB8::8:800:200C:417A", # unicast, compressed + "FF01::101", # multicast, compressed + # Some more test cases that should pass. + "2001:0000:1234:0000:0000:C1C0:ABCD:0876", + "3ffe:0b00:0000:0000:0001:0000:0000:000a", + "FF02:0000:0000:0000:0000:0000:0000:0001", + "0000:0000:0000:0000:0000:0000:0000:0001", + "0000:0000:0000:0000:0000:0000:0000:0000", + # Assorted valid, compressed IPv6 addresses. + "2::10", + "ff02::1", + "fe80::", + "2002::", + "2001:db8::", + "2001:0db8:1234::", + "::ffff:0:0", + "::1", + "1:2:3:4:5:6:7:8", + "1:2:3:4:5:6::8", + "1:2:3:4:5::8", + "1:2:3:4::8", + "1:2:3::8", + "1:2::8", + "1::8", + "1::2:3:4:5:6:7", + "1::2:3:4:5:6", + "1::2:3:4:5", + "1::2:3:4", + "1::2:3", + "1::8", + "::2:3:4:5:6:7:8", + "::2:3:4:5:6:7", + "::2:3:4:5:6", + "::2:3:4:5", + "::2:3:4", + "::2:3", + "::8", + "1:2:3:4:5:6::", + "1:2:3:4:5::", + "1:2:3:4::", + "1:2:3::", + "1:2::", + "1::", + "1:2:3:4:5::7:8", + "1:2:3:4::7:8", + "1:2:3::7:8", + "1:2::7:8", + "1::7:8", + # IPv4 addresses as dotted-quads + "1:2:3:4:5:6:1.2.3.4", + "1:2:3:4:5::1.2.3.4", + "1:2:3:4::1.2.3.4", + "1:2:3::1.2.3.4", + "1:2::1.2.3.4", + "1::1.2.3.4", + "1:2:3:4::5:1.2.3.4", + "1:2:3::5:1.2.3.4", + "1:2::5:1.2.3.4", + "1::5:1.2.3.4", + "1::5:11.22.33.44", + "fe80::217:f2ff:254.7.237.98", + "::ffff:192.168.1.26", + "::ffff:192.168.1.1", + "0:0:0:0:0:0:13.1.68.3", # IPv4-compatible IPv6 address, full, deprecated + "0:0:0:0:0:FFFF:129.144.52.38", # IPv4-mapped IPv6 address, full + "::13.1.68.3", # IPv4-compatible IPv6 address, compressed, deprecated + "::FFFF:129.144.52.38", # IPv4-mapped IPv6 address, compressed + "fe80:0:0:0:204:61ff:254.157.241.86", + "fe80::204:61ff:254.157.241.86", + "::ffff:12.34.56.78", + "::ffff:192.0.2.128", # this is OK, since there's a single zero digit in IPv4 + "fe80:0000:0000:0000:0204:61ff:fe9d:f156", + "fe80:0:0:0:204:61ff:fe9d:f156", + "fe80::204:61ff:fe9d:f156", + "::1", + "fe80::", + "fe80::1", + "::ffff:c000:280", + + # Additional test cases from http://rt.cpan.org/Public/Bug/Display.html?id=50693 + "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "2001:db8:85a3:0:0:8a2e:370:7334", + "2001:db8:85a3::8a2e:370:7334", + "2001:0db8:0000:0000:0000:0000:1428:57ab", + "2001:0db8:0000:0000:0000::1428:57ab", + "2001:0db8:0:0:0:0:1428:57ab", + "2001:0db8:0:0::1428:57ab", + "2001:0db8::1428:57ab", + "2001:db8::1428:57ab", + "0000:0000:0000:0000:0000:0000:0000:0001", + "::1", + "::ffff:0c22:384e", + "2001:0db8:1234:0000:0000:0000:0000:0000", + "2001:0db8:1234:ffff:ffff:ffff:ffff:ffff", + "2001:db8:a::123", + "fe80::", + + "1111:2222:3333:4444:5555:6666:7777:8888", + "1111:2222:3333:4444:5555:6666:7777::", + "1111:2222:3333:4444:5555:6666::", + "1111:2222:3333:4444:5555::", + "1111:2222:3333:4444::", + "1111:2222:3333::", + "1111:2222::", + "1111::", + "1111:2222:3333:4444:5555:6666::8888", + "1111:2222:3333:4444:5555::8888", + "1111:2222:3333:4444::8888", + "1111:2222:3333::8888", + "1111:2222::8888", + "1111::8888", + "::8888", + "1111:2222:3333:4444:5555::7777:8888", + "1111:2222:3333:4444::7777:8888", + "1111:2222:3333::7777:8888", + "1111:2222::7777:8888", + "1111::7777:8888", + "::7777:8888", + "1111:2222:3333:4444::6666:7777:8888", + "1111:2222:3333::6666:7777:8888", + "1111:2222::6666:7777:8888", + "1111::6666:7777:8888", + "::6666:7777:8888", + "1111:2222:3333::5555:6666:7777:8888", + "1111:2222::5555:6666:7777:8888", + "1111::5555:6666:7777:8888", + "::5555:6666:7777:8888", + "1111:2222::4444:5555:6666:7777:8888", + "1111::4444:5555:6666:7777:8888", + "::4444:5555:6666:7777:8888", + "1111::3333:4444:5555:6666:7777:8888", + "::3333:4444:5555:6666:7777:8888", + "::2222:3333:4444:5555:6666:7777:8888", + "1111:2222:3333:4444:5555:6666:123.123.123.123", + "1111:2222:3333:4444:5555::123.123.123.123", + "1111:2222:3333:4444::123.123.123.123", + "1111:2222:3333::123.123.123.123", + "1111:2222::123.123.123.123", + "1111::123.123.123.123", + "::123.123.123.123", + "1111:2222:3333:4444::6666:123.123.123.123", + "1111:2222:3333::6666:123.123.123.123", + "1111:2222::6666:123.123.123.123", + "1111::6666:123.123.123.123", + "::6666:123.123.123.123", + "1111:2222:3333::5555:6666:123.123.123.123", + "1111:2222::5555:6666:123.123.123.123", + "1111::5555:6666:123.123.123.123", + "::5555:6666:123.123.123.123", + "1111:2222::4444:5555:6666:123.123.123.123", + "1111::4444:5555:6666:123.123.123.123", + "::4444:5555:6666:123.123.123.123", + "1111::3333:4444:5555:6666:123.123.123.123", + "::2222:3333:4444:5555:6666:123.123.123.123", + + # Playing with combinations of "0" and "::"; these are all sytactically + # correct, but are bad form because "0" adjacent to "::" should be + # combined into "::" + "::0:0:0:0:0:0:0", + "::0:0:0:0:0:0", + "::0:0:0:0:0", + "::0:0:0:0", + "::0:0:0", + "::0:0", + "::0", + "0:0:0:0:0:0:0::", + "0:0:0:0:0:0::", + "0:0:0:0:0::", + "0:0:0:0::", + "0:0:0::", + "0:0::", + "0::", + + # Additional cases: http://crisp.tweakblogs.net/blog/2031/ipv6-validation-%28and-caveats%29.html + "0:a:b:c:d:e:f::", + "::0:a:b:c:d:e:f", # syntactically correct, but bad form (::0:... could be combined) + "a:b:c:d:e:f:0::", + ].each do |ip| + it "should accept #{ip.inspect} as an IPv6 address" do + expect { @class.new(:name => "foo", :ip => ip) }.not_to raise_error + end + end + + # ...aaaand, some failure cases. + [":", + "02001:0000:1234:0000:0000:C1C0:ABCD:0876", # extra 0 not allowed! + "2001:0000:1234:0000:00001:C1C0:ABCD:0876", # extra 0 not allowed! + "2001:0000:1234:0000:0000:C1C0:ABCD:0876 0", # junk after valid address + "2001:0000:1234: 0000:0000:C1C0:ABCD:0876", # internal space + "3ffe:0b00:0000:0001:0000:0000:000a", # seven segments + "FF02:0000:0000:0000:0000:0000:0000:0000:0001", # nine segments + "3ffe:b00::1::a", # double "::" + "::1111:2222:3333:4444:5555:6666::", # double "::" + "1:2:3::4:5::7:8", # Double "::" + "12345::6:7:8", + # IPv4 embedded, but bad... + "1::5:400.2.3.4", "1::5:260.2.3.4", "1::5:256.2.3.4", "1::5:1.256.3.4", + "1::5:1.2.256.4", "1::5:1.2.3.256", "1::5:300.2.3.4", "1::5:1.300.3.4", + "1::5:1.2.300.4", "1::5:1.2.3.300", "1::5:900.2.3.4", "1::5:1.900.3.4", + "1::5:1.2.900.4", "1::5:1.2.3.900", "1::5:300.300.300.300", "1::5:3000.30.30.30", + "1::400.2.3.4", "1::260.2.3.4", "1::256.2.3.4", "1::1.256.3.4", + "1::1.2.256.4", "1::1.2.3.256", "1::300.2.3.4", "1::1.300.3.4", + "1::1.2.300.4", "1::1.2.3.300", "1::900.2.3.4", "1::1.900.3.4", + "1::1.2.900.4", "1::1.2.3.900", "1::300.300.300.300", "1::3000.30.30.30", + "::400.2.3.4", "::260.2.3.4", "::256.2.3.4", "::1.256.3.4", + "::1.2.256.4", "::1.2.3.256", "::300.2.3.4", "::1.300.3.4", + "::1.2.300.4", "::1.2.3.300", "::900.2.3.4", "::1.900.3.4", + "::1.2.900.4", "::1.2.3.900", "::300.300.300.300", "::3000.30.30.30", + "2001:1:1:1:1:1:255Z255X255Y255", # garbage instead of "." in IPv4 + "::ffff:192x168.1.26", # ditto + "::ffff:2.3.4", + "::ffff:257.1.2.3", + "1.2.3.4:1111:2222:3333:4444::5555", + "1.2.3.4:1111:2222:3333::5555", + "1.2.3.4:1111:2222::5555", + "1.2.3.4:1111::5555", + "1.2.3.4::5555", + "1.2.3.4::", + + # Testing IPv4 addresses represented as dotted-quads Leading zero's in + # IPv4 addresses not allowed: some systems treat the leading "0" in + # ".086" as the start of an octal number Update: The BNF in RFC-3986 + # explicitly defines the dec-octet (for IPv4 addresses) not to have a + # leading zero + "fe80:0000:0000:0000:0204:61ff:254.157.241.086", + "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4", + "1111:2222:3333:4444:5555:6666:00.00.00.00", + "1111:2222:3333:4444:5555:6666:000.000.000.000", + "1111:2222:3333:4444:5555:6666:256.256.256.256", + + "1111:2222:3333:4444::5555:", + "1111:2222:3333::5555:", + "1111:2222::5555:", + "1111::5555:", + "::5555:", + ":::", + "1111:", + ":", + + ":1111:2222:3333:4444::5555", + ":1111:2222:3333::5555", + ":1111:2222::5555", + ":1111::5555", + ":::5555", + ":::", + + # Additional test cases from http://rt.cpan.org/Public/Bug/Display.html?id=50693 + "123", + "ldkfj", + "2001::FFD3::57ab", + "2001:db8:85a3::8a2e:37023:7334", + "2001:db8:85a3::8a2e:370k:7334", + "1:2:3:4:5:6:7:8:9", + "1::2::3", + "1:::3:4:5", + "1:2:3::4:5:6:7:8:9", + + # Invalid data + "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX", + + # Too many components + "1111:2222:3333:4444:5555:6666:7777:8888:9999", + "1111:2222:3333:4444:5555:6666:7777:8888::", + "::2222:3333:4444:5555:6666:7777:8888:9999", + + # Too few components + "1111:2222:3333:4444:5555:6666:7777", + "1111:2222:3333:4444:5555:6666", + "1111:2222:3333:4444:5555", + "1111:2222:3333:4444", + "1111:2222:3333", + "1111:2222", + "1111", + + # Missing : + "11112222:3333:4444:5555:6666:7777:8888", + "1111:22223333:4444:5555:6666:7777:8888", + "1111:2222:33334444:5555:6666:7777:8888", + "1111:2222:3333:44445555:6666:7777:8888", + "1111:2222:3333:4444:55556666:7777:8888", + "1111:2222:3333:4444:5555:66667777:8888", + "1111:2222:3333:4444:5555:6666:77778888", + + # Missing : intended for :: + "1111:2222:3333:4444:5555:6666:7777:8888:", + "1111:2222:3333:4444:5555:6666:7777:", + "1111:2222:3333:4444:5555:6666:", + "1111:2222:3333:4444:5555:", + "1111:2222:3333:4444:", + "1111:2222:3333:", + "1111:2222:", + "1111:", + ":", + ":8888", + ":7777:8888", + ":6666:7777:8888", + ":5555:6666:7777:8888", + ":4444:5555:6666:7777:8888", + ":3333:4444:5555:6666:7777:8888", + ":2222:3333:4444:5555:6666:7777:8888", + ":1111:2222:3333:4444:5555:6666:7777:8888", + + # ::: + ":::2222:3333:4444:5555:6666:7777:8888", + "1111:::3333:4444:5555:6666:7777:8888", + "1111:2222:::4444:5555:6666:7777:8888", + "1111:2222:3333:::5555:6666:7777:8888", + "1111:2222:3333:4444:::6666:7777:8888", + "1111:2222:3333:4444:5555:::7777:8888", + "1111:2222:3333:4444:5555:6666:::8888", + "1111:2222:3333:4444:5555:6666:7777:::", + + # Double ::", + "::2222::4444:5555:6666:7777:8888", + "::2222:3333::5555:6666:7777:8888", + "::2222:3333:4444::6666:7777:8888", + "::2222:3333:4444:5555::7777:8888", + "::2222:3333:4444:5555:7777::8888", + "::2222:3333:4444:5555:7777:8888::", + + "1111::3333::5555:6666:7777:8888", + "1111::3333:4444::6666:7777:8888", + "1111::3333:4444:5555::7777:8888", + "1111::3333:4444:5555:6666::8888", + "1111::3333:4444:5555:6666:7777::", + + "1111:2222::4444::6666:7777:8888", + "1111:2222::4444:5555::7777:8888", + "1111:2222::4444:5555:6666::8888", + "1111:2222::4444:5555:6666:7777::", + + "1111:2222:3333::5555::7777:8888", + "1111:2222:3333::5555:6666::8888", + "1111:2222:3333::5555:6666:7777::", + + "1111:2222:3333:4444::6666::8888", + "1111:2222:3333:4444::6666:7777::", + + "1111:2222:3333:4444:5555::7777::", + + + # Too many components" + "1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4", + "1111:2222:3333:4444:5555:6666:7777:1.2.3.4", + "1111:2222:3333:4444:5555:6666::1.2.3.4", + "::2222:3333:4444:5555:6666:7777:1.2.3.4", + "1111:2222:3333:4444:5555:6666:1.2.3.4.5", + + # Too few components + "1111:2222:3333:4444:5555:1.2.3.4", + "1111:2222:3333:4444:1.2.3.4", + "1111:2222:3333:1.2.3.4", + "1111:2222:1.2.3.4", + "1111:1.2.3.4", + + # Missing : + "11112222:3333:4444:5555:6666:1.2.3.4", + "1111:22223333:4444:5555:6666:1.2.3.4", + "1111:2222:33334444:5555:6666:1.2.3.4", + "1111:2222:3333:44445555:6666:1.2.3.4", + "1111:2222:3333:4444:55556666:1.2.3.4", + "1111:2222:3333:4444:5555:66661.2.3.4", + + # Missing . + "1111:2222:3333:4444:5555:6666:255255.255.255", + "1111:2222:3333:4444:5555:6666:255.255255.255", + "1111:2222:3333:4444:5555:6666:255.255.255255", + + # Missing : intended for :: + ":1.2.3.4", + ":6666:1.2.3.4", + ":5555:6666:1.2.3.4", + ":4444:5555:6666:1.2.3.4", + ":3333:4444:5555:6666:1.2.3.4", + ":2222:3333:4444:5555:6666:1.2.3.4", + ":1111:2222:3333:4444:5555:6666:1.2.3.4", + + # ::: + ":::2222:3333:4444:5555:6666:1.2.3.4", + "1111:::3333:4444:5555:6666:1.2.3.4", + "1111:2222:::4444:5555:6666:1.2.3.4", + "1111:2222:3333:::5555:6666:1.2.3.4", + "1111:2222:3333:4444:::6666:1.2.3.4", + "1111:2222:3333:4444:5555:::1.2.3.4", + + # Double :: + "::2222::4444:5555:6666:1.2.3.4", + "::2222:3333::5555:6666:1.2.3.4", + "::2222:3333:4444::6666:1.2.3.4", + "::2222:3333:4444:5555::1.2.3.4", + + "1111::3333::5555:6666:1.2.3.4", + "1111::3333:4444::6666:1.2.3.4", + "1111::3333:4444:5555::1.2.3.4", + + "1111:2222::4444::6666:1.2.3.4", + "1111:2222::4444:5555::1.2.3.4", + + "1111:2222:3333::5555::1.2.3.4", + + # Missing parts + "::.", + "::..", + "::...", + "::1...", + "::1.2..", + "::1.2.3.", + "::.2..", + "::.2.3.", + "::.2.3.4", + "::..3.", + "::..3.4", + "::...4", + + # Extra : in front + ":1111:2222:3333:4444:5555:6666:7777::", + ":1111:2222:3333:4444:5555:6666::", + ":1111:2222:3333:4444:5555::", + ":1111:2222:3333:4444::", + ":1111:2222:3333::", + ":1111:2222::", + ":1111::", + ":::", + ":1111:2222:3333:4444:5555:6666::8888", + ":1111:2222:3333:4444:5555::8888", + ":1111:2222:3333:4444::8888", + ":1111:2222:3333::8888", + ":1111:2222::8888", + ":1111::8888", + ":::8888", + ":1111:2222:3333:4444:5555::7777:8888", + ":1111:2222:3333:4444::7777:8888", + ":1111:2222:3333::7777:8888", + ":1111:2222::7777:8888", + ":1111::7777:8888", + ":::7777:8888", + ":1111:2222:3333:4444::6666:7777:8888", + ":1111:2222:3333::6666:7777:8888", + ":1111:2222::6666:7777:8888", + ":1111::6666:7777:8888", + ":::6666:7777:8888", + ":1111:2222:3333::5555:6666:7777:8888", + ":1111:2222::5555:6666:7777:8888", + ":1111::5555:6666:7777:8888", + ":::5555:6666:7777:8888", + ":1111:2222::4444:5555:6666:7777:8888", + ":1111::4444:5555:6666:7777:8888", + ":::4444:5555:6666:7777:8888", + ":1111::3333:4444:5555:6666:7777:8888", + ":::3333:4444:5555:6666:7777:8888", + ":::2222:3333:4444:5555:6666:7777:8888", + ":1111:2222:3333:4444:5555:6666:1.2.3.4", + ":1111:2222:3333:4444:5555::1.2.3.4", + ":1111:2222:3333:4444::1.2.3.4", + ":1111:2222:3333::1.2.3.4", + ":1111:2222::1.2.3.4", + ":1111::1.2.3.4", + ":::1.2.3.4", + ":1111:2222:3333:4444::6666:1.2.3.4", + ":1111:2222:3333::6666:1.2.3.4", + ":1111:2222::6666:1.2.3.4", + ":1111::6666:1.2.3.4", + ":::6666:1.2.3.4", + ":1111:2222:3333::5555:6666:1.2.3.4", + ":1111:2222::5555:6666:1.2.3.4", + ":1111::5555:6666:1.2.3.4", + ":::5555:6666:1.2.3.4", + ":1111:2222::4444:5555:6666:1.2.3.4", + ":1111::4444:5555:6666:1.2.3.4", + ":::4444:5555:6666:1.2.3.4", + ":1111::3333:4444:5555:6666:1.2.3.4", + ":::2222:3333:4444:5555:6666:1.2.3.4", + + # Extra : at end + "1111:2222:3333:4444:5555:6666:7777:::", + "1111:2222:3333:4444:5555:6666:::", + "1111:2222:3333:4444:5555:::", + "1111:2222:3333:4444:::", + "1111:2222:3333:::", + "1111:2222:::", + "1111:::", + ":::", + "1111:2222:3333:4444:5555:6666::8888:", + "1111:2222:3333:4444:5555::8888:", + "1111:2222:3333:4444::8888:", + "1111:2222:3333::8888:", + "1111:2222::8888:", + "1111::8888:", + "::8888:", + "1111:2222:3333:4444:5555::7777:8888:", + "1111:2222:3333:4444::7777:8888:", + "1111:2222:3333::7777:8888:", + "1111:2222::7777:8888:", + "1111::7777:8888:", + "::7777:8888:", + "1111:2222:3333:4444::6666:7777:8888:", + "1111:2222:3333::6666:7777:8888:", + "1111:2222::6666:7777:8888:", + "1111::6666:7777:8888:", + "::6666:7777:8888:", + "1111:2222:3333::5555:6666:7777:8888:", + "1111:2222::5555:6666:7777:8888:", + "1111::5555:6666:7777:8888:", + "::5555:6666:7777:8888:", + "1111:2222::4444:5555:6666:7777:8888:", + "1111::4444:5555:6666:7777:8888:", + "::4444:5555:6666:7777:8888:", + "1111::3333:4444:5555:6666:7777:8888:", + "::3333:4444:5555:6666:7777:8888:", + "::2222:3333:4444:5555:6666:7777:8888:", + ].each do |ip| + it "should reject #{ip.inspect} as an IPv6 address" do + expect { @class.new(:name => "foo", :ip => ip) }.to raise_error + end + end + it "should not accept spaces in resourcename" do proc { @class.new(:name => "foo bar") }.should raise_error end diff --git a/spec/unit/type/schedule_spec.rb b/spec/unit/type/schedule_spec.rb index b302d95cd..d407057a8 100755 --- a/spec/unit/type/schedule_spec.rb +++ b/spec/unit/type/schedule_spec.rb @@ -82,17 +82,17 @@ describe Puppet::Type.type(:schedule) do it "should match when the start time is before the current time and the end time is after the current time" do @schedule[:range] = "10:59:50 - 11:00:10" - @schedule.should be_match + @schedule.must be_match end it "should not match when the start time is after the current time" do @schedule[:range] = "11:00:05 - 11:00:10" - @schedule.should_not be_match + @schedule.must_not be_match end it "should not match when the end time is previous to the current time" do @schedule[:range] = "10:59:50 - 10:59:55" - @schedule.should be_match + @schedule.must_not be_match end it "should throw an error if the upper limit is less than the lower limit" do @@ -103,17 +103,17 @@ describe Puppet::Type.type(:schedule) do it "should not match the current time fails between an array of ranges" do @schedule[:range] = ["4-6", "20-23"] - @schedule.should_not be_match + @schedule.must_not be_match end it "should match the lower array of ranges" do @schedule[:range] = ["9-11", "14-16"] - @schedule.should be_match + @schedule.must be_match end it "should match the upper array of ranges" do @schedule[:range] = ["4-6", "11-12"] - @schedule.should be_match + @schedule.must be_match end end diff --git a/spec/unit/type_spec.rb b/spec/unit/type_spec.rb index 3b5f0cba6..5fa9f4534 100755 --- a/spec/unit/type_spec.rb +++ b/spec/unit/type_spec.rb @@ -588,6 +588,35 @@ describe Puppet::Type, :fails_on_windows => true do resource.should_not be_suitable end end + + describe "::ensurable?" do + before :each do + class TestEnsurableType < Puppet::Type + def exists?; end + def create; end + def destroy; end + end + end + + it "is true if the class has exists?, create, and destroy methods defined" do + TestEnsurableType.should be_ensurable + end + + it "is false if exists? is not defined" do + TestEnsurableType.class_eval { remove_method(:exists?) } + TestEnsurableType.should_not be_ensurable + end + + it "is false if create is not defined" do + TestEnsurableType.class_eval { remove_method(:create) } + TestEnsurableType.should_not be_ensurable + end + + it "is false if destroy is not defined" do + TestEnsurableType.class_eval { remove_method(:destroy) } + TestEnsurableType.should_not be_ensurable + end + end end describe Puppet::Type::RelationshipMetaparam do diff --git a/spec/unit/util/anonymous_filelock_spec.rb b/spec/unit/util/anonymous_filelock_spec.rb new file mode 100644 index 000000000..784ac0fca --- /dev/null +++ b/spec/unit/util/anonymous_filelock_spec.rb @@ -0,0 +1,78 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/anonymous_filelock' + +describe Puppet::Util::AnonymousFilelock do + require 'puppet_spec/files' + include PuppetSpec::Files + + before(:each) do + @lockfile = tmpfile("lock") + @lock = Puppet::Util::AnonymousFilelock.new(@lockfile) + end + + it "should be anonymous" do + @lock.should be_anonymous + end + + describe "#lock" do + it "should return false if already locked" do + @lock.stubs(:locked?).returns(true) + @lock.lock.should be_false + end + + it "should return true if it successfully locked" do + @lock.lock.should be_true + end + + it "should create a lock file" do + @lock.lock + + File.should be_exists(@lockfile) + end + + it "should create a lock file containing a message" do + @lock.lock("message") + + File.read(@lockfile).should == "message" + end + end + + describe "#unlock" do + it "should return true when unlocking" do + @lock.lock + @lock.unlock.should be_true + end + + it "should return false when not locked" do + @lock.unlock.should be_false + end + + it "should clear the lock file" do + File.open(@lockfile, 'w') { |fd| fd.print("locked") } + @lock.unlock + File.should_not be_exists(@lockfile) + end + end + + it "should be locked when locked" do + @lock.lock + @lock.should be_locked + end + + it "should not be locked when not locked" do + @lock.should_not be_locked + end + + it "should not be locked when unlocked" do + @lock.lock + @lock.unlock + @lock.should_not be_locked + end + + it "should return the lock message" do + @lock.lock("lock message") + @lock.message.should == "lock message" + end +end
\ No newline at end of file diff --git a/spec/unit/util/execution_stub_spec.rb b/spec/unit/util/execution_stub_spec.rb index 57305e315..b66f88f83 100755 --- a/spec/unit/util/execution_stub_spec.rb +++ b/spec/unit/util/execution_stub_spec.rb @@ -16,7 +16,8 @@ describe Puppet::Util::ExecutionStub do Puppet::Util::ExecutionStub.current_value.should == nil end - it "should restore normal execution after 'reset' is called" do + # fails on windows, see #11740 + it "should restore normal execution after 'reset' is called", :fails_on_windows => true do # Note: "true" exists at different paths in different OSes if Puppet.features.microsoft_windows? true_command = [Puppet::Util.which('cmd.exe').tr('/', '\\'), '/c', 'exit 0'] diff --git a/spec/unit/util/instrumentation/data_spec.rb b/spec/unit/util/instrumentation/data_spec.rb new file mode 100755 index 000000000..c2465f622 --- /dev/null +++ b/spec/unit/util/instrumentation/data_spec.rb @@ -0,0 +1,44 @@ +#!/usr/bin/env rspec + +require 'spec_helper' +require 'matchers/json' +require 'puppet/util/instrumentation' +require 'puppet/util/instrumentation/data' + +describe Puppet::Util::Instrumentation::Data do + Puppet::Util::Instrumentation::Data + + before(:each) do + @listener = stub 'listener', :name => "name" + Puppet::Util::Instrumentation.stubs(:[]).with("name").returns(@listener) + end + + it "should indirect instrumentation_data" do + Puppet::Util::Instrumentation::Data.indirection.name.should == :instrumentation_data + end + + it "should lookup the corresponding listener" do + Puppet::Util::Instrumentation.expects(:[]).with("name").returns(@listener) + Puppet::Util::Instrumentation::Data.new("name") + end + + it "should error if the listener can not be found" do + Puppet::Util::Instrumentation.expects(:[]).with("name").returns(nil) + expect { Puppet::Util::Instrumentation::Data.new("name") }.to raise_error + end + + it "should return pson data" do + data = Puppet::Util::Instrumentation::Data.new("name") + @listener.stubs(:data).returns({ :this_is_data => "here also" }) + data.should set_json_attribute('name').to("name") + data.should set_json_attribute('this_is_data').to("here also") + end + + it "should not error if the underlying listener doesn't have data" do + lambda { Puppet::Util::Instrumentation::Data.new("name").to_pson }.should_not raise_error + end + + it "should return a hash containing data when unserializing from pson" do + Puppet::Util::Instrumentation::Data.from_pson({:name => "name"}).should == {:name => "name"} + end +end
\ No newline at end of file diff --git a/spec/unit/util/instrumentation/indirection_probe_spec.rb b/spec/unit/util/instrumentation/indirection_probe_spec.rb new file mode 100644 index 000000000..654825c9a --- /dev/null +++ b/spec/unit/util/instrumentation/indirection_probe_spec.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env rspec + +require 'spec_helper' +require 'matchers/json' +require 'puppet/util/instrumentation' +require 'puppet/util/instrumentation/indirection_probe' + +describe Puppet::Util::Instrumentation::IndirectionProbe do + Puppet::Util::Instrumentation::IndirectionProbe + + it "should indirect instrumentation_probe" do + Puppet::Util::Instrumentation::IndirectionProbe.indirection.name.should == :instrumentation_probe + end + + it "should return pson data" do + probe = Puppet::Util::Instrumentation::IndirectionProbe.new("probe") + probe.should set_json_attribute('name').to("probe") + end +end
\ No newline at end of file diff --git a/spec/unit/util/instrumentation/instrumentable_spec.rb b/spec/unit/util/instrumentation/instrumentable_spec.rb new file mode 100755 index 000000000..dd2ad3084 --- /dev/null +++ b/spec/unit/util/instrumentation/instrumentable_spec.rb @@ -0,0 +1,186 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +require 'puppet/util/instrumentation' +require 'puppet/util/instrumentation/instrumentable' + +describe Puppet::Util::Instrumentation::Instrumentable::Probe do + + before(:each) do + Puppet::Util::Instrumentation.stubs(:start) + Puppet::Util::Instrumentation.stubs(:stop) + + class ProbeTest + def mymethod(arg1, arg2, arg3) + :it_worked + end + end + end + + after(:each) do + if ProbeTest.method_defined?(:instrumented_mymethod) + ProbeTest.class_eval { + remove_method(:mymethod) + alias_method(:mymethod, :instrumented_mymethod) + } + end + Puppet::Util::Instrumentation::Instrumentable.clear_probes + end + + describe "when enabling a probe" do + it "should raise an error if the probe is already enabled" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + lambda { probe.enable }.should raise_error + end + + it "should rename the original method name" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + ProbeTest.new.should respond_to(:instrumented_mymethod) + end + + it "should create a new method of the original name" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + ProbeTest.new.should respond_to(:mymethod) + end + end + + describe "when disabling a probe" do + it "should raise an error if the probe is already enabled" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + lambda { probe.disable }.should raise_error + end + + it "should rename the original method name" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + probe.disable + + Puppet::Util::Instrumentation.expects(:start).never + Puppet::Util::Instrumentation.expects(:stop).never + ProbeTest.new.mymethod(1,2,3).should == :it_worked + end + + it "should remove the created method" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + probe.disable + ProbeTest.new.should_not respond_to(:instrumented_mymethod) + end + end + + describe "when a probe is called" do + it "should call the original method" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + test = ProbeTest.new + test.expects(:instrumented_mymethod).with(1,2,3) + test.mymethod(1,2,3) + end + + it "should start the instrumentation" do + Puppet::Util::Instrumentation.expects(:start) + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + test = ProbeTest.new + test.mymethod(1,2,3) + end + + it "should stop the instrumentation" do + Puppet::Util::Instrumentation.expects(:stop) + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + test = ProbeTest.new + test.mymethod(1,2,3) + end + + describe "and the original method raises an exception" do + it "should propagate the exception" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + test = ProbeTest.new + test.expects(:instrumented_mymethod).with(1,2,3).raises + lambda { test.mymethod(1,2,3) }.should raise_error + end + + it "should stop the instrumentation" do + Puppet::Util::Instrumentation.expects(:stop) + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) + probe.enable + test = ProbeTest.new + test.expects(:instrumented_mymethod).with(1,2,3).raises + lambda { test.mymethod(1,2,3) }.should raise_error + end + end + + describe "with a static label" do + it "should send the label to the instrumentation layer" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :label => :mylabel) + probe.enable + test = ProbeTest.new + Puppet::Util::Instrumentation.expects(:start).with { |label,data| label == :mylabel }.returns(42) + Puppet::Util::Instrumentation.expects(:stop).with(:mylabel, 42, {}) + test.mymethod(1,2,3) + end + end + + describe "with a dynamic label" do + it "should send the evaluated label to the instrumentation layer" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :label => Proc.new { |parent,args| "dynamic#{args[0]}" } ) + probe.enable + test = ProbeTest.new + Puppet::Util::Instrumentation.expects(:start).with { |label,data| label == "dynamic1" }.returns(42) + Puppet::Util::Instrumentation.expects(:stop).with("dynamic1",42,{}) + test.mymethod(1,2,3) + end + end + + describe "with static data" do + it "should send the data to the instrumentation layer" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :data => { :static_data => "nothing" }) + probe.enable + test = ProbeTest.new + Puppet::Util::Instrumentation.expects(:start).with { |label,data| data == { :static_data => "nothing" }} + test.mymethod(1,2,3) + end + end + + describe "with dynamic data" do + it "should send the evaluated label to the instrumentation layer" do + probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :data => Proc.new { |parent, args| { :key => args[0] } } ) + probe.enable + test = ProbeTest.new + Puppet::Util::Instrumentation.expects(:start).with { |label,data| data == { :key => 1 } } + Puppet::Util::Instrumentation.expects(:stop) + test.mymethod(1,2,3) + end + end + end +end + +describe Puppet::Util::Instrumentation::Instrumentable do + before(:each) do + class ProbeTest2 + extend Puppet::Util::Instrumentation::Instrumentable + probe :mymethod + def mymethod(arg1,arg2,arg3) + end + end + end + + after do + Puppet::Util::Instrumentation::Instrumentable.clear_probes + end + + it "should allow probe definition" do + Puppet::Util::Instrumentation::Instrumentable.probe_names.should be_include("ProbeTest2.mymethod") + end + + it "should be able to enable all probes" do + Puppet::Util::Instrumentation::Instrumentable.enable_probes + ProbeTest2.new.should respond_to(:instrumented_mymethod) + end +end
\ No newline at end of file diff --git a/spec/unit/util/instrumentation/listener_spec.rb b/spec/unit/util/instrumentation/listener_spec.rb new file mode 100755 index 000000000..bc49d265c --- /dev/null +++ b/spec/unit/util/instrumentation/listener_spec.rb @@ -0,0 +1,100 @@ +#!/usr/bin/env rspec + +require 'spec_helper' +require 'matchers/json' + +require 'puppet/util/instrumentation' +require 'puppet/util/instrumentation/listener' + +describe Puppet::Util::Instrumentation::Listener do + + Listener = Puppet::Util::Instrumentation::Listener + + before(:each) do + @delegate = stub 'listener', :notify => nil, :name => 'listener' + @listener = Listener.new(@delegate) + @listener.enabled = true + end + + it "should indirect instrumentation_listener" do + Listener.indirection.name.should == :instrumentation_listener + end + + it "should raise an error if delegate doesn't support notify" do + lambda { Listener.new(Object.new) }.should raise_error + end + + it "should not be enabled by default" do + Listener.new(@delegate).should_not be_enabled + end + + it "should delegate notification" do + @delegate.expects(:notify).with(:event, :start, {}) + listener = Listener.new(@delegate) + listener.notify(:event, :start, {}) + end + + it "should not listen is not enabled" do + @listener.enabled = false + @listener.should_not be_listen_to(:label) + end + + it "should listen to all label if created without pattern" do + @listener.should be_listen_to(:improbable_label) + end + + it "should listen to specific string pattern" do + listener = Listener.new(@delegate, "specific") + listener.enabled = true + listener.should be_listen_to(:specific) + end + + it "should not listen to non-matching string pattern" do + listener = Listener.new(@delegate, "specific") + listener.enabled = true + listener.should_not be_listen_to(:unspecific) + end + + it "should listen to specific regex pattern" do + listener = Listener.new(@delegate, /spe.*/) + listener.enabled = true + listener.should be_listen_to(:specific_pattern) + end + + it "should not listen to non matching regex pattern" do + listener = Listener.new(@delegate, /^match.*/) + listener.enabled = true + listener.should_not be_listen_to(:not_matching) + end + + it "should delegate its name to the underlying listener" do + @delegate.expects(:name).returns("myname") + @listener.name.should == "myname" + end + + it "should delegate data fetching to the underlying listener" do + @delegate.expects(:data).returns(:data) + @listener.data.should == {:data => :data } + end + + describe "when serializing to pson" do + it "should return a pson object containing pattern, name and status" do + @listener.should set_json_attribute('enabled').to(true) + @listener.should set_json_attribute('name').to("listener") + end + end + + describe "when deserializing from pson" do + it "should lookup the archetype listener from the instrumentation layer" do + Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener) + Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener"}) + end + + it "should create a new listener shell instance delegating to the archetypal listener" do + Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener) + @listener.stubs(:listener).returns(@delegate) + Puppet::Util::Instrumentation::Listener.expects(:new).with(@delegate, nil, true) + Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener", "enabled" => true}) + end + end +end
\ No newline at end of file diff --git a/spec/unit/util/instrumentation/listeners/log_spec.rb b/spec/unit/util/instrumentation/listeners/log_spec.rb new file mode 100755 index 000000000..8359625a1 --- /dev/null +++ b/spec/unit/util/instrumentation/listeners/log_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' +require 'puppet/util/instrumentation' + +Puppet::Util::Instrumentation.init +log = Puppet::Util::Instrumentation.listener(:log) + +describe log do + before(:each) do + @log = log.new + end + + it "should have a notify method" do + @log.should respond_to(:notify) + end + + it "should have a data method" do + @log.should respond_to(:data) + end + + it "should keep data for stop event" do + @log.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456790)}) + @log.data.should == {:test=>["test took 1.0"]} + end + + it "should not keep data for start event" do + @log.notify(:test, :start, { :started => Time.at(123456789)}) + @log.data.should be_empty + end + + it "should not keep more than 20 events per label" do + 25.times { @log.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456790)}) } + @log.data[:test].size.should == 20 + end +end
\ No newline at end of file diff --git a/spec/unit/util/instrumentation/listeners/performance_spec.rb b/spec/unit/util/instrumentation/listeners/performance_spec.rb new file mode 100755 index 000000000..4cecbe3f6 --- /dev/null +++ b/spec/unit/util/instrumentation/listeners/performance_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' +require 'puppet/util/instrumentation' + +Puppet::Util::Instrumentation.init +performance = Puppet::Util::Instrumentation.listener(:performance) + +describe performance do + before(:each) do + @performance = performance.new + end + + it "should have a notify method" do + @performance.should respond_to(:notify) + end + + it "should have a data method" do + @performance.should respond_to(:data) + end + + it "should keep data for stop event" do + @performance.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456790)}) + @performance.data.should == {:test=>{:average=>1.0, :count=>1, :min=>1.0, :max=>1.0, :sum=>1.0}} + end + + it "should accumulate performance statistics" do + @performance.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456790)}) + @performance.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456791)}) + + @performance.data.should == {:test=>{:average=>1.5, :count=>2, :min=>1.0, :max=>2.0, :sum=>3.0}} + end + + it "should not keep data for start event" do + @performance.notify(:test, :start, { :started => Time.at(123456789)}) + @performance.data.should be_empty + end +end
\ No newline at end of file diff --git a/spec/unit/util/instrumentation/listeners/process_name_spec.rb b/spec/unit/util/instrumentation/listeners/process_name_spec.rb new file mode 100755 index 000000000..521257214 --- /dev/null +++ b/spec/unit/util/instrumentation/listeners/process_name_spec.rb @@ -0,0 +1,201 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/util/instrumentation' + +Puppet::Util::Instrumentation.init +process_name = Puppet::Util::Instrumentation.listener(:process_name) + +describe process_name do + before(:each) do + @process_name = process_name.new + end + + it "should have a notify method" do + @process_name.should respond_to(:notify) + end + + it "should not have a data method" do + @process_name.should_not respond_to(:data) + end + + describe "when managing thread activity" do + before(:each) do + @process_name.stubs(:setproctitle) + @process_name.stubs(:base).returns("base") + end + + it "should be able to append activity" do + thread1 = stub 'thread1' + @process_name.push_activity(:thread1,"activity1") + @process_name.push_activity(:thread1,"activity2") + + @process_name.reason[:thread1].should == ["activity1", "activity2"] + end + + it "should be able to remove activity" do + @process_name.push_activity(:thread1,"activity1") + @process_name.push_activity(:thread1,"activity1") + @process_name.pop_activity(:thread1) + + @process_name.reason[:thread1].should == ["activity1"] + end + + it "should maintain activity thread by thread" do + @process_name.push_activity(:thread1,"activity1") + @process_name.push_activity(:thread2,"activity2") + + @process_name.reason[:thread1].should == ["activity1"] + @process_name.reason[:thread2].should == ["activity2"] + end + + it "should set process title" do + @process_name.expects(:setproctitle) + + @process_name.push_activity("thread1","activity1") + end + end + + describe "when computing the current process name" do + before(:each) do + @process_name.stubs(:setproctitle) + @process_name.stubs(:base).returns("base") + end + + it "should include every running thread activity" do + thread1 = stub 'thread1', :inspect => "\#<Thread:0xdeadbeef run>", :hash => 1 + thread2 = stub 'thread2', :inspect => "\#<Thread:0x12344321 run>", :hash => 0 + + @process_name.push_activity(thread1,"Compiling node1.domain.com") + @process_name.push_activity(thread2,"Compiling node4.domain.com") + @process_name.push_activity(thread1,"Parsing file site.pp") + @process_name.push_activity(thread2,"Parsing file node.pp") + + @process_name.process_name.should =~ /12344321 Compiling node4.domain.com,Parsing file node.pp/ + @process_name.process_name.should =~ /deadbeef Compiling node1.domain.com,Parsing file site.pp/ + end + end + + describe "when finding base process name" do + {:master => "master", :agent => "agent", :user => "puppet"}.each do |program,base| + it "should return #{base} for #{program}" do + Puppet.run_mode.stubs(:name).returns(program) + @process_name.base.should == base + end + end + end + + describe "when finding a thread id" do + it "should return the id from the thread inspect string" do + thread = stub 'thread', :inspect => "\#<Thread:0x1234abdc run>" + @process_name.thread_id(thread).should == "1234abdc" + end + end + + describe "when scrolling the instrumentation string" do + it "should rotate the string of various step" do + @process_name.rotate("this is a rotation", 10).should == "rotation -- this is a " + end + + it "should not rotate the string for the 0 offset" do + @process_name.rotate("this is a rotation", 0).should == "this is a rotation" + end + end + + describe "when setting process name" do + before(:each) do + @process_name.stubs(:process_name).returns("12345 activity") + @process_name.stubs(:base).returns("base") + @oldname = $0 + end + + after(:each) do + $0 = @oldname + end + + it "should do it if the feature is enabled" do + @process_name.setproctitle + + $0.should == "base: 12345 activity" + end + end + + describe "when subscribed" do + before(:each) do + thread = stub 'thread', :inspect => "\#<Thread:0x1234abdc run>" + Thread.stubs(:current).returns(thread) + end + + it "should start the scroller" do + Thread.expects(:new) + @process_name.subscribed + end + end + + describe "when unsubscribed" do + before(:each) do + @thread = stub 'scroller', :inspect => "\#<Thread:0x1234abdc run>" + Thread.stubs(:new).returns(@thread) + Thread.stubs(:kill) + @oldname = $0 + @process_name.subscribed + end + + after(:each) do + $0 = @oldname + end + + it "should stop the scroller" do + Thread.expects(:kill).with(@thread) + @process_name.unsubscribed + end + + it "should reset the process name" do + $0 = "let's see what happens" + @process_name.unsubscribed + $0.should == @oldname + end + end + + describe "when setting a probe" do + before(:each) do + thread = stub 'thread', :inspect => "\#<Thread:0x1234abdc run>" + Thread.stubs(:current).returns(thread) + Thread.stubs(:new) + @process_name.active = true + end + + it "should push current thread activity and execute the block" do + @process_name.notify(:instrumentation, :start, {}) + $0.should == "puppet: 1234abdc instrumentation" + @process_name.notify(:instrumentation, :stop, {}) + end + + it "should finally pop the activity" do + @process_name.notify(:instrumentation, :start, {}) + @process_name.notify(:instrumentation, :stop, {}) + $0.should == "puppet: " + end + end + + describe "when scrolling" do + it "should do nothing for shorter process names" do + @process_name.expects(:setproctitle).never + @process_name.scroll + end + + it "should call setproctitle" do + @process_name.stubs(:process_name).returns("x" * 60) + @process_name.expects(:setproctitle) + @process_name.scroll + end + + it "should increment rotation offset" do + name = "x" * 60 + @process_name.stubs(:process_name).returns(name) + @process_name.expects(:rotate).once.with(name,1).returns("") + @process_name.expects(:rotate).once.with(name,2).returns("") + @process_name.scroll + @process_name.scroll + end + end +end
\ No newline at end of file diff --git a/spec/unit/util/instrumentation_spec.rb b/spec/unit/util/instrumentation_spec.rb new file mode 100755 index 000000000..0d19ee03c --- /dev/null +++ b/spec/unit/util/instrumentation_spec.rb @@ -0,0 +1,181 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +require 'puppet/util/instrumentation' + +describe Puppet::Util::Instrumentation do + + Instrumentation = Puppet::Util::Instrumentation + + after(:each) do + Instrumentation.clear + end + + it "should instance-load instrumentation listeners" do + Instrumentation.instance_loader(:listener).should be_instance_of(Puppet::Util::Autoload) + end + + it "should have a method for registering instrumentation listeners" do + Instrumentation.should respond_to(:new_listener) + end + + it "should have a method for retrieving instrumentation listener by name" do + Instrumentation.should respond_to(:listener) + end + + describe "when registering listeners" do + it "should evaluate the supplied block as code for a class" do + Instrumentation.expects(:genclass).returns(Class.new { def notify(label, event, data) ; end }) + Instrumentation.new_listener(:testing, :label_pattern => :for_this_label, :event => :all) { } + end + + it "should subscribe a new listener instance" do + Instrumentation.expects(:genclass).returns(Class.new { def notify(label, event, data) ; end }) + Instrumentation.new_listener(:testing, :label_pattern => :for_this_label, :event => :all) { } + Instrumentation.listeners.size.should == 1 + Instrumentation.listeners[0].pattern.should == "for_this_label" + end + + it "should be possible to access listeners by name" do + Instrumentation.expects(:genclass).returns(Class.new { def notify(label, event, data) ; end }) + Instrumentation.new_listener(:testing, :label_pattern => :for_this_label, :event => :all) { } + Instrumentation["testing"].should_not be_nil + end + + it "should be possible to store a new listener by name" do + listener = stub 'listener' + Instrumentation["testing"] = listener + Instrumentation["testing"].should == listener + end + + it "should fail if listener is already subscribed" do + listener = stub 'listener', :notify => nil, :name => "mylistener" + Instrumentation.subscribe(listener, :for_this_label, :all) + expect { Instrumentation.subscribe(listener, :for_this_label, :all) }.to raise_error + end + + it 'should call #unsubscribed' do + listener = stub 'listener', :notify => nil, :name => "mylistener" + + listener.expects(:subscribed) + + Instrumentation.subscribe(listener, :for_this_label, :all) + end + end + + describe "when unsubscribing listener" do + it "should remove it from the listeners" do + listener = stub 'listener', :notify => nil, :name => "mylistener" + Instrumentation.subscribe(listener, :for_this_label, :all) + Instrumentation.unsubscribe(listener) + Instrumentation.listeners.size.should == 0 + end + + it "should warn if the listener wasn't subscribed" do + listener = stub 'listener', :notify => nil, :name => "mylistener" + Puppet.expects(:warning) + Instrumentation.unsubscribe(listener) + end + + it 'should call #unsubscribed' do + listener = stub 'listener', :notify => nil, :name => "mylistener" + Instrumentation.subscribe(listener, :for_this_label, :all) + + listener.expects(:unsubscribed) + + Instrumentation.unsubscribe(listener) + end + end + + describe "when firing events" do + it "should be able to find all listeners matching a label" do + listener = stub 'listener', :notify => nil, :name => "mylistener" + Instrumentation.subscribe(listener, :for_this_label, :all) + Instrumentation.listeners[0].enabled = true + + count = 0 + Instrumentation.each_listener(:for_this_label) { |l| count += 1 } + count.should == 1 + end + + it "should fire events to matching listeners" do + listener = stub 'listener', :notify => nil, :name => "mylistener" + Instrumentation.subscribe(listener, :for_this_label, :all) + Instrumentation.listeners[0].enabled = true + + listener.expects(:notify).with(:for_this_label, :start, {}) + + Instrumentation.publish(:for_this_label, :start, {}) + end + + it "should not fire events to non-matching listeners" do + listener1 = stub 'listener1', :notify => nil, :name => "mylistener1" + listener2 = stub 'listener2', :notify => nil, :name => "mylistener2" + Instrumentation.subscribe(listener1, :for_this_label, :all) + Instrumentation.listeners[0].enabled = true + Instrumentation.subscribe(listener2, :for_this_other_label, :all) + Instrumentation.listeners[1].enabled = true + + listener1.expects(:notify).never + listener2.expects(:notify).with(:for_this_other_label, :start, {}) + + Instrumentation.publish(:for_this_other_label, :start, {}) + end + end + + describe "when instrumenting code" do + before(:each) do + Instrumentation.stubs(:publish) + end + describe "with a block" do + it "should execute it" do + executed = false + Instrumentation.instrument(:event) do + executed = true + end + executed.should be_true + end + + it "should publish an event before execution" do + Instrumentation.expects(:publish).with { |label,event,data| label == :event && event == :start } + Instrumentation.instrument(:event) {} + end + + it "should publish an event after execution" do + Instrumentation.expects(:publish).with { |label,event,data| label == :event && event == :stop } + Instrumentation.instrument(:event) {} + end + + it "should publish the event even when block raised an exception" do + Instrumentation.expects(:publish).with { |label,event,data| label == :event } + lambda { Instrumentation.instrument(:event) { raise "not working" } }.should raise_error + end + + it "should retain start end finish time of the event" do + Instrumentation.expects(:publish).with { |label,event,data| data.include?(:started) and data.include?(:finished) } + Instrumentation.instrument(:event) {} + end + end + + describe "without a block" do + it "should raise an error if stop is called with no matching start" do + lambda{ Instrumentation.stop(:event) }.should raise_error + end + + it "should publish an event on stop" do + Instrumentation.expects(:publish).with { |label,event,data| event == :start } + Instrumentation.expects(:publish).with { |label,event,data| event == :stop and data.include?(:started) and data.include?(:finished) } + data = {} + Instrumentation.start(:event, data) + Instrumentation.stop(:event, 1, data) + end + + it "should return a different id per event" do + data = {} + Instrumentation.start(:event, data).should == 1 + Instrumentation.start(:event, data).should == 2 + end + end + end +end
\ No newline at end of file diff --git a/spec/unit/util/pidlock_spec.rb b/spec/unit/util/pidlock_spec.rb new file mode 100644 index 000000000..37f3ca4f4 --- /dev/null +++ b/spec/unit/util/pidlock_spec.rb @@ -0,0 +1,182 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/pidlock' + +describe Puppet::Util::Pidlock do + require 'puppet_spec/files' + include PuppetSpec::Files + + before(:each) do + @lockfile = tmpfile("lock") + @lock = Puppet::Util::Pidlock.new(@lockfile) + end + + it "should not be anonymous" do + @lock.should_not be_anonymous + end + + describe "#lock" do + it "should not be locked at start" do + @lock.should_not be_locked + end + + it "should not be mine at start" do + @lock.should_not be_mine + end + + it "should become locked" do + @lock.lock + @lock.should be_locked + end + + it "should become mine" do + @lock.lock + @lock.should be_mine + end + + it "should be possible to lock multiple times" do + @lock.lock + lambda { @lock.lock }.should_not raise_error + end + + it "should return true when locking" do + @lock.lock.should be_true + end + + it "should return true if locked by me" do + @lock.lock + @lock.lock.should be_true + end + + it "should return false if locked by someone else" do + Process.stubs(:kill) + File.open(@lockfile, "w") { |fd| fd.print('0') } + + @lock.lock.should be_false + end + + it "should create a lock file" do + @lock.lock + File.should be_exists(@lockfile) + end + + it "should create a lock file containing our pid" do + @lock.lock + File.read(@lockfile).to_i.should == Process.pid.to_i + end + end + + describe "#unlock" do + it "should not be locked anymore" do + @lock.lock + @lock.unlock + @lock.should_not be_locked + end + + it "should return false if not locked" do + @lock.unlock.should be_false + end + + it "should return true if properly unlocked" do + @lock.lock + @lock.unlock.should be_true + end + + it "should get rid of the lock file" do + @lock.lock + @lock.unlock + File.should_not be_exists(@lockfile) + end + end + + describe "#locked?" do + it "should return true if locked" do + @lock.lock + @lock.should be_locked + end + end + + describe "with a stale lock" do + before(:each) do + Process.stubs(:kill).with(0, 6789) + Process.stubs(:kill).with(0, 1234).raises(Errno::ESRCH) + Process.stubs(:pid).returns(6789) + File.open(@lockfile, 'w') { |fd| fd.write("1234") } + end + + it "should not be locked" do + @lock.should_not be_locked + end + + describe "#lock" do + it "should clear stale locks" do + @lock.locked? + File.should_not be_exists(@lockfile) + end + + it "should replace with new locks" do + @lock.lock + File.should be_exists(@lockfile) + @lock.lock_pid.should == 6789 + @lock.should be_mine + @lock.should be_locked + end + end + + describe "#unlock" do + it "should not be allowed" do + @lock.unlock.should be_false + end + + it "should not remove the lock file" do + @lock.unlock + File.should be_exists(@lockfile) + end + end + end + + describe "with another process lock" do + before(:each) do + Process.stubs(:kill).with(0, 6789) + Process.stubs(:kill).with(0, 1234) + Process.stubs(:pid).returns(6789) + File.open(@lockfile, 'w') { |fd| fd.write("1234") } + end + + it "should be locked" do + @lock.should be_locked + end + + it "should not be mine" do + @lock.should_not be_mine + end + + describe "#lock" do + it "should not be possible" do + @lock.lock.should be_false + end + + it "should not overwrite the lock" do + @lock.lock + @lock.should_not be_mine + end + end + + describe "#unlock" do + it "should not be possible" do + @lock.unlock.should be_false + end + + it "should not remove the lock file" do + @lock.unlock + File.should be_exists(@lockfile) + end + + it "should still not be our lock" do + @lock.unlock + @lock.should_not be_mine + end + end + end +end
\ No newline at end of file diff --git a/spec/unit/util/queue/stomp_spec.rb b/spec/unit/util/queue/stomp_spec.rb index 6799becea..7730ab7cb 100755 --- a/spec/unit/util/queue/stomp_spec.rb +++ b/spec/unit/util/queue/stomp_spec.rb @@ -13,7 +13,7 @@ describe 'Puppet::Util::Queue::Stomp', :if => Puppet.features.stomp?, :'fails_on before do # So we make sure we never create a real client instance. # Otherwise we'll try to connect, and that's bad. - Stomp::Client.stubs(:new).returns stub("client") + Stomp::Client.stubs(:new).returns stub("client", :publish => true) end it 'should be registered with Puppet::Util::Queue as :stomp type' do @@ -22,7 +22,7 @@ describe 'Puppet::Util::Queue::Stomp', :if => Puppet.features.stomp?, :'fails_on describe "when initializing" do it "should create a Stomp client instance" do - Stomp::Client.expects(:new).returns stub("stomp_client") + Stomp::Client.expects(:new).returns stub("stomp_client", :publish => true) Puppet::Util::Queue::Stomp.new end @@ -65,7 +65,7 @@ describe 'Puppet::Util::Queue::Stomp', :if => Puppet.features.stomp?, :'fails_on describe "when publishing a message" do before do - @client = stub 'client' + @client = stub 'client', :publish => true Stomp::Client.stubs(:new).returns @client @queue = Puppet::Util::Queue::Stomp.new end @@ -84,11 +84,16 @@ describe 'Puppet::Util::Queue::Stomp', :if => Puppet.features.stomp?, :'fails_on @client.expects(:publish).with { |queue, msg, options| options[:persistent] == true } @queue.publish_message('fooqueue', 'Smite!') end + + it "should use send when the gem does not support publish" do + Stomp::Client.stubs(:new).returns(stub('client', :send => true)) + Puppet::Util::Queue::Stomp.new.publish_message('fooqueue', 'Smite!') + end end describe "when subscribing to a queue" do before do - @client = stub 'client', :acknowledge => true + @client = stub 'client', :acknowledge => true, :publish => true Stomp::Client.stubs(:new).returns @client @queue = Puppet::Util::Queue::Stomp.new end diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb index 9c8cc7588..892b932b2 100755 --- a/spec/unit/util/rdoc/parser_spec.rb +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -135,7 +135,7 @@ describe RDoc::Parser, :'fails_on_ruby_1.9.2' => true do describe "when finding modules from filepath" do before :each do - Puppet::Module.stubs(:modulepath).returns("/path/to/modules") + Puppet::Node::Environment.any_instance.stubs(:modulepath).returns("/path/to/modules") end it "should return the module name for modulized puppet manifests" do diff --git a/spec/unit/util/reference_spec.rb b/spec/unit/util/reference_spec.rb index 219a673ef..aa16299c7 100644 --- a/spec/unit/util/reference_spec.rb +++ b/spec/unit/util/reference_spec.rb @@ -8,20 +8,30 @@ describe Puppet::Util::Reference do Puppet::Util::Reference.newreference :testreference, :doc => "A peer of the type and configuration references, but with no useful information" do my_term = "A term" my_definition = <<-EOT -The definition of this term. -We should be able to handle multi-line definitions. + The definition of this term, marked by a colon and a space. + We should be able to handle multi-line definitions. Each subsequent + line should left-align with the first word character after the colon + used as the definition marker. -We should be able to handle multi-paragraph definitions. + We should be able to handle multi-paragraph definitions. + + Leading indentation should be stripped from the definition, which allows + us to indent the source string for cosmetic purposes. EOT my_fragment = markdown_definitionlist(my_term, my_definition) end Puppet::Util::Reference.reference(:testreference).send(:to_markdown, true) my_fragment.should == <<-EOT A term -: The definition of this term. - We should be able to handle multi-line definitions. +: The definition of this term, marked by a colon and a space. + We should be able to handle multi-line definitions. Each subsequent + line should left-align with the first word character after the colon + used as the definition marker. + + We should be able to handle multi-paragraph definitions. - We should be able to handle multi-paragraph definitions. + Leading indentation should be stripped from the definition, which allows + us to indent the source string for cosmetic purposes. EOT end diff --git a/spec/unit/util/retryaction_spec.rb b/spec/unit/util/retryaction_spec.rb new file mode 100644 index 000000000..90f8e8eb2 --- /dev/null +++ b/spec/unit/util/retryaction_spec.rb @@ -0,0 +1,62 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/retryaction' + +describe Puppet::Util::RetryAction do + let (:exceptions) {{ Puppet::Error => 'Puppet Error Exception' }} + + it 'should retry on any exception if no acceptable exceptions given' do + Puppet::Util::RetryAction.expects(:sleep).with( (((2 ** 1) -1) * 0.1) ) + Puppet::Util::RetryAction.expects(:sleep).with( (((2 ** 2) -1) * 0.1) ) + + expect do + Puppet::Util::RetryAction.retry_action( :retries => 2 ) do + raise ArgumentError, 'Fake Failure' + end + end.to raise_exception(Puppet::Util::RetryAction::RetryException::RetriesExceeded) + end + + it 'should retry on acceptable exceptions' do + Puppet::Util::RetryAction.expects(:sleep).with( (((2 ** 1) -1) * 0.1) ) + Puppet::Util::RetryAction.expects(:sleep).with( (((2 ** 2) -1) * 0.1) ) + + expect do + Puppet::Util::RetryAction.retry_action( :retries => 2, :retry_exceptions => exceptions) do + raise Puppet::Error, 'Fake Failure' + end + end.to raise_exception(Puppet::Util::RetryAction::RetryException::RetriesExceeded) + end + + it 'should not retry on unacceptable exceptions' do + Puppet::Util::RetryAction.expects(:sleep).never + + expect do + Puppet::Util::RetryAction.retry_action( :retries => 2, :retry_exceptions => exceptions) do + raise ArgumentError + end + end.to raise_exception(ArgumentError) + end + + it 'should succeed if nothing is raised' do + Puppet::Util::RetryAction.expects(:sleep).never + + Puppet::Util::RetryAction.retry_action( :retries => 2) do + true + end + end + + it 'should succeed if an expected exception is raised retried and succeeds' do + should_retry = nil + Puppet::Util::RetryAction.expects(:sleep).once + + Puppet::Util::RetryAction.retry_action( :retries => 2, :retry_exceptions => exceptions) do + if should_retry + true + else + should_retry = true + raise Puppet::Error, 'Fake error' + end + end + end +end diff --git a/spec/unit/util/suidmanager_spec.rb b/spec/unit/util/suidmanager_spec.rb index 45a351f1b..575762f3c 100755 --- a/spec/unit/util/suidmanager_spec.rb +++ b/spec/unit/util/suidmanager_spec.rb @@ -308,3 +308,22 @@ describe Puppet::Util::SUIDManager do end end end + +describe 'Puppet::Util::SUIDManager#groups=' do + subject do + Puppet::Util::SUIDManager + end + + + it "(#3419) should rescue Errno::EINVAL on OS X" do + Process.expects(:groups=).raises(Errno::EINVAL, 'blew up') + subject.expects(:osx_maj_ver).returns('10.7').twice + subject.groups = ['list', 'of', 'groups'] + end + + it "(#3419) should fail if an Errno::EINVAL is raised NOT on OS X" do + Process.expects(:groups=).raises(Errno::EINVAL, 'blew up') + subject.expects(:osx_maj_ver).returns(false) + expect { subject.groups = ['list', 'of', 'groups'] }.should raise_error(Errno::EINVAL) + end +end diff --git a/spec/unit/util/symbolic_file_mode_spec.rb b/spec/unit/util/symbolic_file_mode_spec.rb new file mode 100755 index 000000000..a6e9509f7 --- /dev/null +++ b/spec/unit/util/symbolic_file_mode_spec.rb @@ -0,0 +1,182 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/util/symbolic_file_mode' + +describe Puppet::Util::SymbolicFileMode do + include Puppet::Util::SymbolicFileMode + + describe "#valid_symbolic_mode?" do + %w{ + 0 0000 1 1 7 11 77 111 777 11 + 0 00000 01 01 07 011 077 0111 0777 011 + = - + u= g= o= a= u+ g+ o+ a+ u- g- o- a- ugo= ugoa= ugugug= + a=,u=,g= a=,g+ + =rwx +rwx -rwx + 644 go-w =rw,+X +X 755 u=rwx,go=rx u=rwx,go=u-w go= g=u-w + 755 0755 + }.each do |input| + it "should treat #{input.inspect} as valid" do + valid_symbolic_mode?(input).should be_true + end + end + + [0000, 0111, 0640, 0755, 0777].each do |input| + it "should treat the int #{input.to_s(8)} as value" do + valid_symbolic_mode?(input).should be_true + end + end + + %w{ + -1 -8 8 9 18 19 91 81 000000 11111 77777 + 0-1 0-8 08 09 018 019 091 081 0000000 011111 077777 + u g o a ug uo ua ag + }.each do |input| + it "should treat #{input.inspect} as invalid" do + valid_symbolic_mode?(input).should be_false + end + end + end + + describe "#normalize_symbolic_mode" do + it "should turn an int into a string" do + normalize_symbolic_mode(12).should be_an_instance_of String + end + + it "should not add a leading zero to an int" do + normalize_symbolic_mode(12).should_not =~ /^0/ + end + + it "should not add a leading zero to a string with a number" do + normalize_symbolic_mode("12").should_not =~ /^0/ + end + + it "should string a leading zero from a number" do + normalize_symbolic_mode("012").should == '12' + end + + it "should pass through any other string" do + normalize_symbolic_mode("u=rwx").should == 'u=rwx' + end + end + + describe "#symbolic_mode_to_int" do + { + "0654" => 00654, + "u+r" => 00400, + "g+r" => 00040, + "a+r" => 00444, + "a+x" => 00111, + "o+t" => 01000, + "o+t" => 01000, + ["o-t", 07777] => 06777, + ["a-x", 07777] => 07666, + ["a-rwx", 07777] => 07000, + ["ug-rwx", 07777] => 07007, + "a+x,ug-rwx" => 00001, + # My experimentation on debian suggests that +g ignores the sgid flag + ["a+g", 02060] => 02666, + # My experimentation on debian suggests that -g ignores the sgid flag + ["a-g", 02666] => 02000, + "g+x,a+g" => 00111, + # +X without exec set in the original should not set anything + "u+x,g+X" => 00100, + "g+X" => 00000, + # +X only refers to the original, *unmodified* file mode! + ["u+x,a+X", 0600] => 00700, + # Examples from the MacOS chmod(1) manpage + "0644" => 00644, + ["go-w", 07777] => 07755, + ["=rw,+X", 07777] => 07777, + ["=rw,+X", 07766] => 07777, + ["=rw,+X", 07676] => 07777, + ["=rw,+X", 07667] => 07777, + ["=rw,+X", 07666] => 07666, + "0755" => 00755, + "u=rwx,go=rx" => 00755, + "u=rwx,go=u-w" => 00755, + ["go=", 07777] => 07700, + ["g=u-w", 07777] => 07757, + ["g=u-w", 00700] => 00750, + ["g=u-w", 00600] => 00640, + ["g=u-w", 00500] => 00550, + ["g=u-w", 00400] => 00440, + ["g=u-w", 00300] => 00310, + ["g=u-w", 00200] => 00200, + ["g=u-w", 00100] => 00110, + ["g=u-w", 00000] => 00000, + # Cruel, but legal, use of the action set. + ["g=u+r-w", 0300] => 00350, + # Empty assignments. + ["u=", 00000] => 00000, + ["u=", 00600] => 00000, + ["ug=", 00000] => 00000, + ["ug=", 00600] => 00000, + ["ug=", 00660] => 00000, + ["ug=", 00666] => 00006, + ["=", 00000] => 00000, + ["=", 00666] => 00000, + ["+", 00000] => 00000, + ["+", 00124] => 00124, + ["-", 00000] => 00000, + ["-", 00124] => 00124, + }.each do |input, result| + from = input.is_a?(Array) ? "#{input[0]}, 0#{input[1].to_s(8)}" : input + it "should map #{from.inspect} to #{result.inspect}" do + symbolic_mode_to_int(*input).should == result + end + end + + # Now, test some failure modes. + it "should fail if no mode is given" do + expect { symbolic_mode_to_int('') }. + to raise_error Puppet::Error, /empty mode string/ + end + + %w{u g o ug uo go ugo a uu u/x u!x u=r,,g=r}.each do |input| + it "should fail if no (valid) action is given: #{input.inspect}" do + expect { symbolic_mode_to_int(input) }. + to raise_error Puppet::Error, /Missing action/ + end + end + + %w{u+q u-rwF u+rw,g+rw,o+RW}.each do |input| + it "should fail with unknown op #{input.inspect}" do + expect { symbolic_mode_to_int(input) }. + to raise_error Puppet::Error, /Unknown operation/ + end + end + + it "should refuse to subtract the conditional execute op" do + expect { symbolic_mode_to_int("o-rwX") }. + to raise_error Puppet::Error, /only works with/ + end + + it "should refuse to set to the conditional execute op" do + expect { symbolic_mode_to_int("o=rwX") }. + to raise_error Puppet::Error, /only works with/ + end + + %w{8 08 9 09 118 119}.each do |input| + it "should fail for decimal modes: #{input.inspect}" do + expect { symbolic_mode_to_int(input) }. + to raise_error Puppet::Error, /octal/ + end + end + + it "should set the execute bit on a directory, without exec in original" do + symbolic_mode_to_int("u+X", 0444, true).to_s(8).should == "544" + symbolic_mode_to_int("g+X", 0444, true).to_s(8).should == "454" + symbolic_mode_to_int("o+X", 0444, true).to_s(8).should == "445" + symbolic_mode_to_int("+X", 0444, true).to_s(8).should == "555" + end + + it "should set the execute bit on a file with exec in the original" do + symbolic_mode_to_int("+X", 0544).to_s(8).should == "555" + end + + it "should not set the execute bit on a file without exec on the original even if set by earlier DSL" do + symbolic_mode_to_int("u+x,go+X", 0444).to_s(8).should == "544" + end + end +end diff --git a/spec/unit/util/zaml_spec.rb b/spec/unit/util/zaml_spec.rb index d77cf99d2..858ae6044 100755 --- a/spec/unit/util/zaml_spec.rb +++ b/spec/unit/util/zaml_spec.rb @@ -1,4 +1,16 @@ #!/usr/bin/env rspec +# encoding: UTF-8 +# +# The above encoding line is a magic comment to set the default source encoding +# of this file for the Ruby interpreter. It must be on the first or second +# line of the file if an interpreter is in use. In Ruby 1.9 and later, the +# source encoding determines the encoding of String and Regexp objects created +# from this source file. This explicit encoding is important becuase otherwise +# Ruby will pick an encoding based on LANG or LC_CTYPE environment variables. +# These may be different from site to site so it's important for us to +# establish a consistent behavior. For more information on M17n please see: +# http://links.puppetlabs.com/understanding_m17n + require 'spec_helper' require 'puppet/util/monkey_patches' @@ -60,3 +72,28 @@ describe "Pure ruby yaml implementation" do x2[2].should equal(x2) end end + +# Note, many of these tests will pass on Ruby 1.8 but fail on 1.9 if the patch +# fix is not applied to Puppet or there's a regression. These version +# dependant failures are intentional since the string encoding behavior changed +# significantly in 1.9. +describe "UTF-8 encoded String#to_yaml (Bug #11246)" do + # JJM All of these snowmen are different representations of the same + # UTF-8 encoded string. + let(:snowman) { 'Snowman: [☃]' } + let(:snowman_escaped) { "Snowman: [\xE2\x98\x83]" } + + describe "UTF-8 String Literal" do + subject { snowman } + + it "should serialize to YAML" do + subject.to_yaml + end + it "should serialize and deserialize to the same thing" do + YAML.load(subject.to_yaml).should == subject + end + it "should serialize and deserialize to a String compatible with a UTF-8 encoded Regexp" do + YAML.load(subject.to_yaml).should =~ /☃/u + end + end +end |