diff options
| author | Matt Robinson <matt@puppetlabs.com> | 2011-03-16 11:19:10 -0700 |
|---|---|---|
| committer | Matt Robinson <matt@puppetlabs.com> | 2011-03-16 11:19:10 -0700 |
| commit | de8fea8ac8b25f45ffb07a2ef63a6da0cbaf0c41 (patch) | |
| tree | f520b1400b4d35e63a0b1977b508d51d9422a88a /spec | |
| parent | 86c60354da1d5a2a54baf6dbd92677a12701423d (diff) | |
| parent | c60c6cacaca6b8e34c29835f9e5749fc380b8e0b (diff) | |
| download | puppet-de8fea8ac8b25f45ffb07a2ef63a6da0cbaf0c41.tar.gz puppet-de8fea8ac8b25f45ffb07a2ef63a6da0cbaf0c41.tar.xz puppet-de8fea8ac8b25f45ffb07a2ef63a6da0cbaf0c41.zip | |
Merge branch '2.6.next' into 2.6.x
* 2.6.next: (102 commits)
(#5073) Download plugins even if you're filtering on tags
Fix #5610: Prevent unnecessary RAL lookups
Revert "Merge branch 'ticket/2.6.x/5605' of git://github.com/stschulte/puppet into 2.6.next"
(#6723) Fix withenv environment restoration bug
(#6689) Remove extraneous include of Puppet::Util in InventoryActiveRecord
Remove extra trailing whitespace from lib/puppet/resource.rb
(#5428) More fully "stub" Puppet::Resource::Reference for use with storedconfigs
(#6707) Fix typo in rest_authconfig.rb
(#6689) Make inventory_active_record terminus search quickly
(#5392) Give a better error when realizing a non-existant resource
(#2645) Adding a less-stubby test to verify the "system" attribute's behavior
maint: Remove serialization of InventoryFact values
maint: Rename InventoryHost to InventoryNode
Fixed #2645 - Added support for creating system users
maint: Remove spec run noise
maint:Refactor of mount provider integration tests
(#6338) Support searching on metadata in InventoryActiveRecord terminus
(#6338) Implement search for InventoryActiveRecord facts terminus
(#6338) Add an InventoryActiveRecord terminus for Facts
Added integration tests for the mount provider
...
Diffstat (limited to 'spec')
39 files changed, 1477 insertions, 546 deletions
diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb index 7b85bcacb..20d87c228 100755 --- a/spec/integration/parser/parser_spec.rb +++ b/spec/integration/parser/parser_spec.rb @@ -109,5 +109,12 @@ describe Puppet::Parser::Parser do it "should correctly set the arrow type of a relationship" do "Notify[foo] <~ Notify[bar]".should parse_with { |rel| rel.arrow == "<~" } end + + it "should be able to parse deep hash access" do + %q{ + $hash = { 'a' => { 'b' => { 'c' => 'it works' } } } + $out = $hash['a']['b']['c'] + }.should parse_with { |v| v.value.is_a?(Puppet::Parser::AST::ASTHash) } + end end end diff --git a/spec/integration/provider/mount_spec.rb b/spec/integration/provider/mount_spec.rb new file mode 100644 index 000000000..d6f25fe1d --- /dev/null +++ b/spec/integration/provider/mount_spec.rb @@ -0,0 +1,151 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_bucket/dipper' + +describe "mount provider (integration)" do + include PuppetSpec::Files + + def create_fake_fstab(initially_contains_entry) + File.open(@fake_fstab, 'w') do |f| + if initially_contains_entry + f.puts("/dev/disk1s1\t/Volumes/foo_disk\tmsdos\tlocal\t0\t0") + end + end + end + + before :each do + @fake_fstab = tmpfile('fstab') + @current_options = "local" + @current_device = "/dev/disk1s1" + Puppet::Type.type(:mount).defaultprovider.stubs(:default_target).returns(@fake_fstab) + Facter.stubs(:value).with(:operatingsystem).returns('Darwin') + Puppet::Util::ExecutionStub.set do |command, options| + case command[0] + when %r{/s?bin/mount} + if command.length == 1 + if @mounted + "#{@current_device} on /Volumes/foo_disk (msdos, #{@current_options})\n" + else + '' + end + else + command.length.should == 4 + command[1].should == '-o' + command[3].should == '/Volumes/foo_disk' + @mounted.should == false # verify that we don't try to call "mount" redundantly + @current_options = command[2] + @current_device = check_fstab(true) + @mounted = true + '' + end + when %r{/s?bin/umount} + command.length.should == 2 + command[1].should == '/Volumes/foo_disk' + @mounted.should == true # "umount" doesn't work when device not mounted (see #6632) + @mounted = false + '' + else + fail "Unexpected command #{command.inspect} executed" + end + end + end + + after :each do + Puppet::Type::Mount::ProviderParsed.clear # Work around bug #6628 + end + + def check_fstab(expected_to_be_present) + # Verify that the fake fstab has the expected data in it + fstab_contents = File.read(@fake_fstab).lines.map(&:chomp).reject { |x| x =~ /^#|^$/ } + if expected_to_be_present + fstab_contents.length().should == 1 + device, rest_of_line = fstab_contents[0].split(/\t/,2) + rest_of_line.should == "/Volumes/foo_disk\tmsdos\t#{@desired_options}\t0\t0" + device + else + fstab_contents.length().should == 0 + nil + end + end + + def run_in_catalog(settings) + resource = Puppet::Type.type(:mount).new(settings.merge(:name => "/Volumes/foo_disk", + :device => "/dev/disk1s1", :fstype => "msdos")) + Puppet::FileBucket::Dipper.any_instance.stubs(:backup) # Don't backup to the filebucket + resource.expects(:err).never + catalog = Puppet::Resource::Catalog.new + catalog.host_config = false # Stop Puppet from doing a bunch of magic + catalog.add_resource resource + catalog.apply + end + + [false, true].each do |initial_state| + describe "When initially #{initial_state ? 'mounted' : 'unmounted'}" do + before :each do + @mounted = initial_state + end + + [false, true].each do |initial_fstab_entry| + describe "When there is #{initial_fstab_entry ? 'an' : 'no'} initial fstab entry" do + before :each do + create_fake_fstab(initial_fstab_entry) + end + + [:defined, :present, :mounted, :unmounted, :absent].each do |ensure_setting| + expected_final_state = case ensure_setting + when :mounted + true + when :unmounted, :absent + false + when :defined, :present + initial_state + else + fail "Unknown ensure_setting #{ensure_setting}" + end + expected_fstab_data = (ensure_setting != :absent) + describe "When setting ensure => #{ensure_setting}" do + ["local", "journaled"].each do |options_setting| + describe "When setting options => #{options_setting}" do + it "should leave the system in the #{expected_final_state ? 'mounted' : 'unmounted'} state, #{expected_fstab_data ? 'with' : 'without'} data in /etc/fstab" do + @desired_options = options_setting + run_in_catalog(:ensure=>ensure_setting, :options => options_setting) + @mounted.should == expected_final_state + if expected_fstab_data + check_fstab(expected_fstab_data).should == "/dev/disk1s1" + else + check_fstab(expected_fstab_data).should == nil + end + if @mounted + if ![:defined, :present].include?(ensure_setting) + @current_options.should == @desired_options + elsif initial_fstab_entry + @current_options.should == @desired_options + else + @current_options.should == 'local' #Workaround for #6645 + end + end + end + end + end + end + end + end + end + end + end + + describe "When the wrong device is mounted" do + it "should remount the correct device" do + pending "Due to bug 6309" + @mounted = true + @current_device = "/dev/disk2s2" + create_fake_fstab(true) + @desired_options = "local" + run_in_catalog(:ensure=>:mounted, :options=>'local') + @current_device.should=="/dev/disk1s1" + @mounted.should==true + @current_options.should=='local' + check_fstab(true).should == "/dev/disk1s1" + end + end +end diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index 4b91e5ef9..31f4adee6 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -173,7 +173,12 @@ describe Puppet::Type.type(:file) do it "should be able to recurse over a nonexistent file" do @path = tmpfile("file_integration_tests") - @file = Puppet::Type::File.new(:name => @path, :mode => 0644, :recurse => true, :backup => false) + @file = Puppet::Type::File.new( + :name => @path, + :mode => 0644, + :recurse => true, + :backup => false + ) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @@ -186,7 +191,12 @@ describe Puppet::Type.type(:file) do build_path(@path) - @file = Puppet::Type::File.new(:name => @path, :mode => 0644, :recurse => true, :backup => false) + @file = Puppet::Type::File.new( + :name => @path, + :mode => 0644, + :recurse => true, + :backup => false + ) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @@ -393,10 +403,8 @@ describe Puppet::Type.type(:file) do dest = tmpfile("files_with_content") - file = Puppet::Type.type(:file).new( - - :name => dest, - + file = Puppet::Type.type(:file).new( + :name => dest, :content => "this is some content, yo" ) @@ -411,11 +419,9 @@ describe Puppet::Type.type(:file) do dest = tmpfile("files_with_content") - file = Puppet::Type.type(:file).new( - - :name => dest, - :ensure => "file", - + file = Puppet::Type.type(:file).new( + :name => dest, + :ensure => "file", :content => "this is some content, yo" ) @@ -433,12 +439,10 @@ describe Puppet::Type.type(:file) do File.open(dest, "w") { |f| f.puts "boo" } - file = Puppet::Type.type(:file).new( - - :name => dest, + file = Puppet::Type.type(:file).new( + :name => dest, :ensure => :absent, :source => source, - :backup => false ) @@ -465,24 +469,23 @@ describe Puppet::Type.type(:file) do File.open(@purgee, "w") { |f| f.puts "footest" } - @lfobj = Puppet::Type.newfile( - - :title => "localfile", - :path => @localfile, + @lfobj = Puppet::Type.newfile( + :title => "localfile", + :path => @localfile, :content => "rahtest\n", - :ensure => :file, - - :backup => false + :ensure => :file, + :backup => false ) - @destobj = Puppet::Type.newfile( - :title => "destdir", :path => @destdir, - :source => @sourcedir, - :backup => false, - :purge => true, - - :recurse => true) + @destobj = Puppet::Type.newfile( + :title => "destdir", + :path => @destdir, + :source => @sourcedir, + :backup => false, + :purge => true, + :recurse => true + ) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @lfobj, @destobj diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ed4e826c9..ae4edb2d9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -34,6 +34,7 @@ RSpec.configure do |config| Puppet.settings.clear Puppet::Node::Environment.clear Puppet::Util::Storage.clear + Puppet::Util::ExecutionStub.reset if defined?($tmpfiles) $tmpfiles.each do |file| @@ -64,6 +65,7 @@ RSpec.configure do |config| # these globals are set by Application $puppet_application_mode = nil $puppet_application_name = nil + Signal.stubs(:trap) # Set the confdir and vardir to gibberish so that tests # have to be correctly mocked. diff --git a/spec/unit/application/agent_spec.rb b/spec/unit/application/agent_spec.rb index ff504eedf..8f498d4ba 100755 --- a/spec/unit/application/agent_spec.rb +++ b/spec/unit/application/agent_spec.rb @@ -50,12 +50,8 @@ describe Puppet::Application::Agent do end describe "in preinit" do - before :each do - @puppetd.stubs(:trap) - end - it "should catch INT" do - @puppetd.expects(:trap).with { |arg,block| arg == :INT } + Signal.expects(:trap).with { |arg,block| arg == :INT } @puppetd.preinit end @@ -530,11 +526,11 @@ describe Puppet::Application::Agent do @puppetd.onetime end - it "should always exit with 0 if --noop" do + it "should exit with the report's computer exit status, even if --noop is set." do Puppet.stubs(:[]).with(:noop).returns(true) report = stub 'report', :exit_status => 666 @agent.stubs(:run).returns(report) - @puppetd.expects(:exit).with(0) + @puppetd.expects(:exit).with(666) @puppetd.onetime end diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index 4e1744206..d4f39abe0 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -52,7 +52,6 @@ describe Puppet::Application::Apply do before :each do Puppet::Log.stubs(:newdestination) - Puppet.stubs(:trap) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) Puppet::FileBucket::Dipper.stubs(:new) @@ -78,7 +77,7 @@ describe Puppet::Application::Apply do end it "should set INT trap" do - @apply.expects(:trap).with(:INT) + Signal.expects(:trap).with(:INT) @apply.setup end @@ -167,6 +166,13 @@ describe Puppet::Application::Apply do @apply.expects(:exit).with(1) @apply.parseonly end + + it "should exit with exit code 1 if error, even if --noop is set" do + Puppet[:noop] = true + @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) + @apply.expects(:exit).with(1) + @apply.parseonly + end end describe "the main command" do @@ -328,6 +334,15 @@ describe Puppet::Application::Apply do @apply.main end + it "should exit with report's computed exit status, even if --noop is set" do + Puppet.stubs(:[]).with(:noop).returns(true) + @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) + Puppet::Transaction::Report.any_instance.stubs(:exit_status).returns(666) + @apply.expects(:exit).with(666) + + @apply.main + end + it "should always exit with 0 if option is disabled" do Puppet.stubs(:[]).with(:noop).returns(false) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(false) diff --git a/spec/unit/application/cert_spec.rb b/spec/unit/application/cert_spec.rb index 4663fc938..b3257916b 100755 --- a/spec/unit/application/cert_spec.rb +++ b/spec/unit/application/cert_spec.rb @@ -51,7 +51,7 @@ describe Puppet::Application::Cert do it "should set cert_mode to :destroy for --clean" do @cert_app.handle_clean(0) - @cert_app.cert_mode.should == :destroy + @cert_app.subcommand.should == :destroy end it "should set all to true for --all" do @@ -68,7 +68,7 @@ describe Puppet::Application::Cert do it "should set cert_mode to #{method} with option --#{method}" do @cert_app.send("handle_#{method}".to_sym, nil) - @cert_app.cert_mode.should == method + @cert_app.subcommand.should == method end end @@ -114,19 +114,19 @@ describe Puppet::Application::Cert do end it "should set the ca_location to :local if the cert_mode is generate" do - @cert_app.find_mode('--generate') + @cert_app.subcommand = 'generate' Puppet::SSL::Host.expects(:ca_location=).with(:local) @cert_app.setup end it "should set the ca_location to :local if the cert_mode is destroy" do - @cert_app.find_mode('--destroy') + @cert_app.subcommand = 'destroy' Puppet::SSL::Host.expects(:ca_location=).with(:local) @cert_app.setup end it "should set the ca_location to :only if the cert_mode is print" do - @cert_app.find_mode('--print') + @cert_app.subcommand = 'print' Puppet::SSL::Host.expects(:ca_location=).with(:only) @cert_app.setup end @@ -171,24 +171,64 @@ describe Puppet::Application::Cert do @cert_app.main end - it "should delegate to ca.apply with current set cert_mode" do - @cert_app.cert_mode = "currentmode" + it "should revoke cert if cert_mode is clean" do + @cert_app.subcommand = :destroy @cert_app.command_line.stubs(:args).returns(["host"]) - @ca.expects(:apply).with { |cert_mode,to| cert_mode == "currentmode" } + @ca.expects(:apply).with { |cert_mode,to| cert_mode == :revoke } + @ca.expects(:apply).with { |cert_mode,to| cert_mode == :destroy } @cert_app.main end + end - it "should revoke cert if cert_mode is clean" do - @cert_app.cert_mode = :destroy - @cert_app.command_line.stubs(:args).returns(["host"]) + describe "when identifying subcommands" do + before :each do + @cert_app.all = false + @ca = stub_everything 'ca' + @cert_app.ca = @ca + end - @ca.expects(:apply).with { |cert_mode,to| cert_mode == :revoke } - @ca.expects(:apply).with { |cert_mode,to| cert_mode == :destroy } + it "should not fail when no command is given" do + # Make the help method silent for testing; this is a bit nasty, but we + # can't identify a cleaner method. Help welcome. --daniel 2011-02-22 + Puppet.features.stubs(:usage?).returns(false) + @cert_app.stubs(:puts) - @cert_app.main + @cert_app.command_line.stubs(:args).returns([]) + expect { @cert_app.parse_options }.should raise_error SystemExit end + %w{list revoke generate sign print verify fingerprint}.each do |cmd| + short = cmd[0,1] + [cmd, "--#{cmd}", "-#{short}"].each do |option| + # In our command line '-v' was eaten by 'verbose', so we can't consume + # it here; this is a special case from our otherwise standard + # processing. --daniel 2011-02-22 + next if option == "-v" + + it "should recognise '#{option}'" do + args = [option, "fun.example.com"] + + @cert_app.command_line.stubs(:args).returns(args) + @cert_app.parse_options + @cert_app.subcommand.should == cmd.to_sym + + args.should == ["fun.example.com"] + end + end + end + + %w{clean --clean -c}.each do |ugly| + it "should recognise the '#{ugly}' option as destroy" do + args = [ugly, "fun.example.com"] + + @cert_app.command_line.stubs(:args).returns(args) + @cert_app.parse_options + @cert_app.subcommand.should == :destroy + + args.should == ["fun.example.com"] + end + end end end diff --git a/spec/unit/application/filebucket_spec.rb b/spec/unit/application/filebucket_spec.rb index e6272f179..95135c7eb 100644 --- a/spec/unit/application/filebucket_spec.rb +++ b/spec/unit/application/filebucket_spec.rb @@ -56,7 +56,7 @@ describe Puppet::Application::Filebucket do end it "should trap INT" do - @filebucket.expects(:trap).with(:INT) + Signal.expects(:trap).with(:INT) @filebucket.setup end diff --git a/spec/unit/application/queue_spec.rb b/spec/unit/application/queue_spec.rb index bd0d53ab1..f8ebbd0b4 100755 --- a/spec/unit/application/queue_spec.rb +++ b/spec/unit/application/queue_spec.rb @@ -29,12 +29,8 @@ describe Puppet::Application::Queue do end describe "in preinit" do - before :each do - @queue.stubs(:trap) - end - it "should catch INT" do - @queue.expects(:trap).with { |arg,block| arg == :INT } + Signal.expects(:trap).with { |arg,block| arg == :INT } @queue.preinit end diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb index c57f39fb5..4080263e7 100755 --- a/spec/unit/configurer/downloader_spec.rb +++ b/spec/unit/configurer/downloader_spec.rb @@ -5,6 +5,8 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/configurer/downloader' describe Puppet::Configurer::Downloader do + require 'puppet_spec/files' + include PuppetSpec::Files it "should require a name" do lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError) end @@ -96,25 +98,35 @@ describe Puppet::Configurer::Downloader do describe "when creating the catalog to do the downloading" do before do - @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") + @dler = Puppet::Configurer::Downloader.new("foo", "/download/path", "source") end it "should create a catalog and add the file to it" do - file = mock 'file' - catalog = mock 'catalog' - - @dler.expects(:file).returns file - - Puppet::Resource::Catalog.expects(:new).returns catalog - catalog.expects(:add_resource).with(file) + catalog = @dler.catalog + catalog.resources.size.should == 1 + catalog.resources.first.class.should == Puppet::Type::File + catalog.resources.first.name.should == "/download/path" + end - @dler.catalog.should equal(catalog) + it "should specify that it is not managing a host catalog" do + @dler.catalog.host_config.should == false end + end describe "when downloading" do before do - @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") + @dl_name = tmpfile("downloadpath") + source_name = tmpfile("source") + File.open(source_name, 'w') {|f| f.write('hola mundo') } + @dler = Puppet::Configurer::Downloader.new("foo", @dl_name, source_name) + end + + it "should not skip downloaded resources when filtering on tags" do + Puppet[:tags] = 'maytag' + @dler.evaluate + + File.exists?(@dl_name).should be_true end it "should log that it is downloading" do diff --git a/spec/unit/daemon_spec.rb b/spec/unit/daemon_spec.rb index e24db7881..39592b7c9 100755 --- a/spec/unit/daemon_spec.rb +++ b/spec/unit/daemon_spec.rb @@ -29,13 +29,9 @@ describe Puppet::Daemon do end describe "when setting signal traps" do - before do - @daemon.stubs(:trap) - end - {:INT => :stop, :TERM => :stop, :HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}.each do |signal, method| it "should log and call #{method} when it receives #{signal}" do - @daemon.expects(:trap).with(signal).yields + Signal.expects(:trap).with(signal).yields Puppet.expects(:notice) diff --git a/spec/unit/indirector/facts/inventory_active_record_spec.rb b/spec/unit/indirector/facts/inventory_active_record_spec.rb new file mode 100644 index 000000000..9558abde2 --- /dev/null +++ b/spec/unit/indirector/facts/inventory_active_record_spec.rb @@ -0,0 +1,163 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'sqlite3' rescue nil +require 'tempfile' +require 'puppet/rails' + +describe "Puppet::Node::Facts::InventoryActiveRecord", :if => (Puppet.features.rails? and defined? SQLite3) do + let(:terminus) { Puppet::Node::Facts::InventoryActiveRecord.new } + + before :all do + require 'puppet/indirector/facts/inventory_active_record' + @dbfile = Tempfile.new("testdb") + @dbfile.close + end + + after :all do + Puppet::Node::Facts.indirection.reset_terminus_class + @dbfile.unlink + end + + before :each do + Puppet::Node::Facts.terminus_class = :inventory_active_record + Puppet[:dbadapter] = 'sqlite3' + Puppet[:dblocation] = @dbfile.path + Puppet[:railslog] = "/dev/null" + Puppet::Rails.init + end + + after :each do + Puppet::Rails.teardown + end + + describe "#save" do + it "should use an existing node if possible" do + node = Puppet::Rails::InventoryNode.new(:name => "foo", :timestamp => Time.now) + node.save + Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin").save + + Puppet::Rails::InventoryNode.count.should == 1 + Puppet::Rails::InventoryNode.first.should == node + end + + it "should create a new node if one can't be found" do + # This test isn't valid if there are nodes to begin with + Puppet::Rails::InventoryNode.count.should == 0 + + Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin").save + + Puppet::Rails::InventoryNode.count.should == 1 + Puppet::Rails::InventoryNode.first.name.should == "foo" + end + + it "should save the facts" do + Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin").save + + Puppet::Rails::InventoryFact.all.map{|f| [f.name,f.value]}.should =~ [["uptime_days","60"],["kernel","Darwin"]] + end + + it "should remove the previous facts for an existing node" do + Puppet::Node::Facts.new("foo", "uptime_days" => "30", "kernel" => "Darwin").save + bar_facts = Puppet::Node::Facts.new("bar", "uptime_days" => "35", "kernel" => "Linux") + foo_facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "is_virtual" => "false") + bar_facts.save + foo_facts.save + + Puppet::Node::Facts.find("bar").should == bar_facts + Puppet::Node::Facts.find("foo").should == foo_facts + Puppet::Rails::InventoryFact.all.map{|f| [f.name,f.value]}.should_not include(["uptime_days", "30"], ["kernel", "Darwin"]) + end + end + + describe "#find" do + before do + @foo_facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") + @bar_facts = Puppet::Node::Facts.new("bar", "uptime_days" => "30", "kernel" => "Linux") + @foo_facts.save + @bar_facts.save + end + + it "should identify facts by node name" do + Puppet::Node::Facts.find("foo").should == @foo_facts + end + + it "should return nil if no node instance can be found" do + Puppet::Node::Facts.find("non-existent node").should == nil + end + end + + describe "#search" do + def search_request(conditions) + Puppet::Indirector::Request.new(:facts, :search, nil, conditions) + end + + before :each do + @now = Time.now + @foo = Puppet::Node::Facts.new("foo", "fact1" => "value1", "fact2" => "value2", "uptime_days" => "30") + @bar = Puppet::Node::Facts.new("bar", "fact1" => "value1", "uptime_days" => "60") + @baz = Puppet::Node::Facts.new("baz", "fact1" => "value2", "fact2" => "value1", "uptime_days" => "90") + @bat = Puppet::Node::Facts.new("bat") + @foo.timestamp = @now - 3600*1 + @bar.timestamp = @now - 3600*3 + @baz.timestamp = @now - 3600*5 + @bat.timestamp = @now - 3600*7 + @foo.save + @bar.save + @baz.save + @bat.save + end + + it "should return node names that match 'equal' constraints" do + request = search_request('facts.fact1.eq' => 'value1', + 'facts.fact2.eq' => 'value2') + terminus.search(request).should == ["foo"] + end + + it "should return node names that match 'not equal' constraints" do + request = search_request('facts.fact1.ne' => 'value2') + terminus.search(request).should == ["bar","foo"] + end + + it "should return node names that match strict inequality constraints" do + request = search_request('facts.uptime_days.gt' => '20', + 'facts.uptime_days.lt' => '70') + terminus.search(request).should == ["bar","foo"] + end + + it "should return node names that match non-strict inequality constraints" do + request = search_request('facts.uptime_days.ge' => '30', + 'facts.uptime_days.le' => '60') + terminus.search(request).should == ["bar","foo"] + end + + it "should return node names whose facts are within a given timeframe" do + request = search_request('meta.timestamp.ge' => @now - 3600*5, + 'meta.timestamp.le' => @now - 3600*1) + terminus.search(request).should == ["bar","baz","foo"] + end + + it "should return node names whose facts are from a specific time" do + request = search_request('meta.timestamp.eq' => @now - 3600*3) + terminus.search(request).should == ["bar"] + end + + it "should return node names whose facts are not from a specific time" do + request = search_request('meta.timestamp.ne' => @now - 3600*1) + terminus.search(request).should == ["bar","bat","baz"] + end + + it "should perform strict searches on nodes by timestamp" do + request = search_request('meta.timestamp.gt' => @now - 3600*5, + 'meta.timestamp.lt' => @now - 3600*1) + terminus.search(request).should == ["bar"] + end + + it "should search nodes based on both facts and timestamp values" do + request = search_request('facts.uptime_days.gt' => '45', + 'meta.timestamp.lt' => @now - 3600*4) + terminus.search(request).should == ["baz"] + end + end +end + diff --git a/spec/unit/indirector/facts/yaml_spec.rb b/spec/unit/indirector/facts/yaml_spec.rb index e7bac3471..c625c8ee3 100755 --- a/spec/unit/indirector/facts/yaml_spec.rb +++ b/spec/unit/indirector/facts/yaml_spec.rb @@ -10,9 +10,9 @@ describe Puppet::Node::Facts::Yaml do Puppet::Node::Facts::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end - it "should have documentation" do Puppet::Node::Facts::Yaml.doc.should_not be_nil + Puppet::Node::Facts::Yaml.doc.should_not be_empty end it "should be registered with the facts indirection" do @@ -20,7 +20,221 @@ describe Puppet::Node::Facts::Yaml do Puppet::Node::Facts::Yaml.indirection.should equal(indirection) end - it "should have its name set to :facts" do + it "should have its name set to :yaml" do Puppet::Node::Facts::Yaml.name.should == :yaml end + + describe "#search" do + def assert_search_matches(matching, nonmatching, query) + request = Puppet::Indirector::Request.new(:inventory, :search, nil, query) + + Dir.stubs(:glob).returns(matching.keys + nonmatching.keys) + [matching, nonmatching].each do |examples| + examples.each do |key, value| + YAML.stubs(:load_file).with(key).returns value + end + end + Puppet::Node::Facts::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name} + end + + it "should return node names that match the search query options" do + assert_search_matches({ + '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '4'), + '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "i386", 'processor_count' => '4', 'randomfact' => 'foo') + }, + { + "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '4'), + "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'), + "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5'), + "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3", 'processor_count' => '4'), + }, + {'facts.architecture' => 'i386', 'facts.processor_count' => '4'} + ) + end + + it "should return empty array when no nodes match the search query options" do + assert_search_matches({}, { + "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '10'), + "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'), + "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5'), + "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3", 'processor_count' => '4'), + }, + {'facts.processor_count.lt' => '4', 'facts.processor_count.gt' => '4'} + ) + end + + + it "should return node names that match the search query options with the greater than operator" do + assert_search_matches({ + '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), + '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '10', 'randomfact' => 'foo') + }, + { + "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '4'), + "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '3'), + "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), + }, + {'facts.processor_count.gt' => '4'} + ) + end + + it "should return node names that match the search query options with the less than operator" do + assert_search_matches({ + '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), + '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '30', 'randomfact' => 'foo') + }, + { + "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '50' ), + "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '100'), + "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), + }, + {'facts.processor_count.lt' => '50'} + ) + end + + it "should return node names that match the search query options with the less than or equal to operator" do + assert_search_matches({ + '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), + '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo') + }, + { + "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '100' ), + "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5000'), + "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), + }, + {'facts.processor_count.le' => '50'} + ) + end + + it "should return node names that match the search query options with the greater than or equal to operator" do + assert_search_matches({ + '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '100'), + '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo') + }, + { + "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '40'), + "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '9' ), + "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), + }, + {'facts.processor_count.ge' => '50'} + ) + end + + it "should return node names that match the search query options with the not equal operator" do + assert_search_matches({ + '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => 'arm' ), + '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => 'powerpc', 'randomfact' => 'foo') + }, + { + "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "i386" ), + "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '9' ), + "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), + }, + {'facts.architecture.ne' => 'i386'} + ) + end + + def apply_timestamp(facts, timestamp) + facts.timestamp = timestamp + facts + end + + it "should be able to query based on meta.timestamp.gt" do + assert_search_matches({ + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + }, + { + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + {'meta.timestamp.gt' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.le" do + assert_search_matches({ + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + { + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + }, + {'meta.timestamp.le' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.lt" do + assert_search_matches({ + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + { + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + {'meta.timestamp.lt' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.ge" do + assert_search_matches({ + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + { + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + {'meta.timestamp.ge' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.eq" do + assert_search_matches({ + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + { + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + {'meta.timestamp.eq' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp" do + assert_search_matches({ + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + { + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + {'meta.timestamp' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.ne" do + assert_search_matches({ + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + { + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + {'meta.timestamp.ne' => '2010-10-15'} + ) + end + end end diff --git a/spec/unit/indirector/queue_spec.rb b/spec/unit/indirector/queue_spec.rb index 00463ee0f..bbe00c75f 100755 --- a/spec/unit/indirector/queue_spec.rb +++ b/spec/unit/indirector/queue_spec.rb @@ -114,6 +114,9 @@ describe Puppet::Indirector::Queue, :if => Puppet.features.pson? do @store_class.client.expects(:subscribe).yields("foo") @store_class.expects(:intern).raises ArgumentError Puppet.expects(:err) + + @store_class.expects(:puts) + @store_class.subscribe {|o| o } end end diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index 37dad7e25..0b4873f5f 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -367,9 +367,9 @@ describe Puppet::Module do mod.stubs(:path).returns "/a/foo" FileTest.expects(:exist?).with("/a/foo/plugins").returns true - mod.expects(:warning) - mod.plugin_directory.should == "/a/foo/plugins" + @logs.first.message.should == "using the deprecated 'plugins' directory for ruby extensions; please move to 'lib'" + @logs.first.level.should == :warning end it "should default to 'lib' for the plugins directory" do diff --git a/spec/unit/network/handler/fileserver_spec.rb b/spec/unit/network/handler/fileserver_spec.rb index b37d4f551..9d34e9cdd 100644 --- a/spec/unit/network/handler/fileserver_spec.rb +++ b/spec/unit/network/handler/fileserver_spec.rb @@ -158,7 +158,7 @@ describe Puppet::Network::Handler::FileServer do end it "should not fail for inexistant plugins type" do - lambda { @mount.list("puppet/parser",true,false) }.should_not raise_error + @mount.list("puppet/parser",true,false) end end diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb index 23a291cf3..9a8780c62 100644 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -76,6 +76,34 @@ describe Puppet::Network::HTTP::API::V1 do @tester.uri2indirection("GET", "/env/foos/bar", {}).method.should == :search end + it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do + @tester.uri2indirection("GET", "/env/facts/bar", {}).method.should == :find + end + + it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do + @tester.uri2indirection("PUT", "/env/facts/bar", {}).method.should == :save + end + + it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do + @tester.uri2indirection("GET", "/env/inventory/search", {}).method.should == :search + end + + it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is facts_search" do + @tester.uri2indirection("GET", "/env/facts_search/bar", {}).method.should == :search + end + + it "should change indirection name to 'facts' if the http method is a GET and the indirection name is facts_search" do + @tester.uri2indirection("GET", "/env/facts_search/bar", {}).indirection_name.should == :facts + end + + it "should not change indirection name from 'facts' if the http method is a GET and the indirection name is facts" do + @tester.uri2indirection("GET", "/env/facts/bar", {}).indirection_name.should == :facts + end + + it "should change indirection name to 'status' if the http method is a GET and the indirection name is statuses" do + @tester.uri2indirection("GET", "/env/statuses/bar", {}).indirection_name.should == :status + 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", {}).method.should == :destroy end diff --git a/spec/unit/network/http/compression_spec.rb b/spec/unit/network/http/compression_spec.rb index c5bbbb064..3828ec59c 100644..100755 --- a/spec/unit/network/http/compression_spec.rb +++ b/spec/unit/network/http/compression_spec.rb @@ -178,7 +178,7 @@ describe "http compression" do end it "should raise the error the second time" do - @inflater.expects(:inflate).raises(Zlib::DataError.new("not a zlib stream")) + @inflater.stubs(:inflate).raises(Zlib::DataError.new("not a zlib stream")) Zlib::Inflate.expects(:new).with.returns(@inflater) lambda { @adapter.uncompress("chunk") }.should raise_error end diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index 8464ae68e..68c7b9aa3 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -344,17 +344,20 @@ describe Puppet::Network::HTTP::Handler do @handler.do_search(@irequest, @request, @response) end - it "should return a 404 when searching returns an empty array" do - @model_class.stubs(:name).returns "my name" - @handler.expects(:set_response).with { |response, body, status| status == 404 } + it "should return [] when searching returns an empty array" do + @handler.expects(:accept_header).with(@request).returns "one,two" @model_class.stubs(:search).returns([]) + @model_class.expects(:render_multiple).with(@oneformat, []).returns "[]" + + + @handler.expects(:set_response).with { |response, data| data == "[]" } @handler.do_search(@irequest, @request, @response) end it "should return a 404 when searching returns nil" do @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } - @model_class.stubs(:search).returns([]) + @model_class.stubs(:search).returns(nil) @handler.do_search(@irequest, @request, @response) end end diff --git a/spec/unit/node/facts_spec.rb b/spec/unit/node/facts_spec.rb index 394db7913..cb2aa3dc7 100755 --- a/spec/unit/node/facts_spec.rb +++ b/spec/unit/node/facts_spec.rb @@ -109,5 +109,29 @@ describe Puppet::Node::Facts, "when indirecting" do facts = Puppet::Node::Facts.new("me", "one" => "two", "three" => "four") facts.values[:_timestamp].should be_instance_of(Time) end + + describe "using pson" do + before :each do + @timestamp = Time.parse("Thu Oct 28 11:16:31 -0700 2010") + @expiration = Time.parse("Thu Oct 28 11:21:31 -0700 2010") + end + + it "should accept properly formatted pson" do + pson = %Q({"name": "foo", "expiration": "#{@expiration}", "timestamp": "#{@timestamp}", "values": {"a": "1", "b": "2", "c": "3"}}) + format = Puppet::Network::FormatHandler.format('pson') + facts = format.intern(Puppet::Node::Facts,pson) + facts.name.should == 'foo' + facts.expiration.should == @expiration + facts.values.should == {'a' => '1', 'b' => '2', 'c' => '3', :_timestamp => @timestamp} + end + + it "should generate properly formatted pson" do + Time.stubs(:now).returns(@timestamp) + facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3}) + facts.expiration = @expiration + pson = PSON.parse(facts.to_pson) + pson.should == {"name"=>"foo", "timestamp"=>@timestamp.to_s, "expiration"=>@expiration.to_s, "values"=>{"a"=>1, "b"=>2, "c"=>3}} + end + end end end diff --git a/spec/unit/parser/ast/collection_spec.rb b/spec/unit/parser/ast/collection_spec.rb index 392a2c0f0..cc33075b7 100755 --- a/spec/unit/parser/ast/collection_spec.rb +++ b/spec/unit/parser/ast/collection_spec.rb @@ -4,20 +4,21 @@ require File.dirname(__FILE__) + '/../../../spec_helper' describe Puppet::Parser::AST::Collection do before :each do - @scope = stub_everything 'scope' - @mytype = stub_everything('mytype') - @scope.stubs(:find_resource_type).returns @mytype - @compiler = stub_everything 'compile' - @scope.stubs(:compiler).returns(@compiler) + @mytype = Puppet::Resource::Type.new(:definition, "mytype") + @environment = Puppet::Node::Environment.new + @environment.known_resource_types.add @mytype + + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode", :environment => @environment)) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @overrides = stub_everything 'overrides' @overrides.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) - end it "should evaluate its query" do query = mock 'query' collection = Puppet::Parser::AST::Collection.new :query => query, :form => :virtual + collection.type = 'mytype' query.expects(:safeevaluate).with(@scope) @@ -26,8 +27,8 @@ describe Puppet::Parser::AST::Collection do it "should instantiate a Collector for this type" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test" - @test_type = stub 'type', :name => 'test' - @scope.expects(:find_resource_type).with('test').returns @test_type + @test_type = Puppet::Resource::Type.new(:definition, "test") + @environment.known_resource_types.add @test_type Puppet::Parser::Collector.expects(:new).with(@scope, "test", nil, nil, :virtual) @@ -35,7 +36,7 @@ describe Puppet::Parser::AST::Collection do end it "should tell the compiler about this collector" do - collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test" + collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype" Puppet::Parser::Collector.stubs(:new).returns("whatever") @compiler.expects(:add_collection).with("whatever") @@ -45,7 +46,7 @@ describe Puppet::Parser::AST::Collection do it "should evaluate overriden paramaters" do collector = stub_everything 'collector' - collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test", :override => @overrides + collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype", :override => @overrides Puppet::Parser::Collector.stubs(:new).returns(collector) @overrides.expects(:safeevaluate).with(@scope) @@ -55,7 +56,7 @@ describe Puppet::Parser::AST::Collection do it "should tell the collector about overrides" do collector = mock 'collector' - collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test", :override => @overrides + collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype", :override => @overrides Puppet::Parser::Collector.stubs(:new).returns(collector) collector.expects(:add_override) @@ -63,5 +64,8 @@ describe Puppet::Parser::AST::Collection do collection.evaluate(@scope) end - + it "should fail when evaluating undefined resource types" do + collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "bogus" + lambda { collection.evaluate(@scope) }.should raise_error "Resource type bogus doesn't exist" + end end diff --git a/spec/unit/parser/lexer_spec.rb b/spec/unit/parser/lexer_spec.rb index 860326973..4ef242cf5 100755 --- a/spec/unit/parser/lexer_spec.rb +++ b/spec/unit/parser/lexer_spec.rb @@ -529,6 +529,22 @@ describe Puppet::Parser::Lexer, "when lexing comments" do @lexer.fullscan end + it "should add a new comment stack level on LPAREN" do + @lexer.string = "(" + + @lexer.expects(:commentpush) + + @lexer.fullscan + end + + it "should pop the current comment on RPAREN" do + @lexer.string = ")" + + @lexer.expects(:commentpop) + + @lexer.fullscan + end + it "should return the current comments on getcomment" do @lexer.string = "# comment" @lexer.fullscan diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb index 07e2d220b..2ed279fd9 100755 --- a/spec/unit/parser/parser_spec.rb +++ b/spec/unit/parser/parser_spec.rb @@ -76,6 +76,12 @@ describe Puppet::Parser do end + describe "when parsing selector" do + it "should support hash access on the left hand side" do + lambda { @parser.parse("$h = { 'a' => 'b' } $a = $h['a'] ? { 'b' => 'd', default => undef }") }.should_not raise_error + end + end + describe "when parsing 'if'" do it "not, it should create the correct ast objects" do ast::Not.expects(:new).with { |h| h[:value].is_a?(ast::Boolean) } @@ -253,7 +259,7 @@ describe Puppet::Parser do before do @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" @parser.stubs(:lexer).returns @lexer - @class = stub 'class', :use_docs => false + @class = Puppet::Resource::Type.new(:hostclass, "myclass", :use_docs => false) end it "should return a new instance of the provided class created with the provided options" do @@ -276,7 +282,7 @@ describe Puppet::Parser do it "should include docs when the AST class uses them" do @class.expects(:use_docs).returns true @class.stubs(:new) - @parser.expects(:ast_context).with{ |a| a[0] == true }.returns({}) + @parser.expects(:ast_context).with{ |docs, line| docs == true }.returns({}) @parser.ast(@class, :file => "/bar") end @@ -304,6 +310,33 @@ describe Puppet::Parser do it "should return an array of nodes" do @parser.newnode(@nodename).should be_instance_of(Array) end + + it "should initialize the ast context with the correct line number" do + @parser.expects(:ast_context).with { |a,b| b == 123 }.returns({}) + @parser.newnode(@nodename, { :line => 123 }) + end + end + + %w{class define}.each do |entity| + describe "when creating a #{entity}" do + before :each do + @parser.stubs(:ast_context).returns({}) + + @name = stub "#{entity}name", :is_a? => false, :value => "foo" + end + + it "should create and add the correct resource type" do + instance = stub 'instance' + Puppet::Resource::Type.expects(:new).returns(instance) + @parser.known_resource_types.expects(:add).with(instance) + @parser.send("new#{entity}", @name) + end + + it "should initialize the ast context with the correct line number" do + @parser.expects(:ast_context).with { |a,b| b == 123 }.returns({}) + @parser.send("new#{entity}", @name, { :line => 123 }) + end + end end describe "when retrieving a specific node" do diff --git a/spec/unit/provider/mount/parsed_spec.rb b/spec/unit/provider/mount/parsed_spec.rb index 5a1c986b1..cf29bd358 100755 --- a/spec/unit/provider/mount/parsed_spec.rb +++ b/spec/unit/provider/mount/parsed_spec.rb @@ -5,15 +5,17 @@ require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet_spec/files' require 'puppettest/support/utils' require 'puppettest/fileparsing' module ParsedMountTesting include PuppetTest::Support::Utils include PuppetTest::FileParsing + include PuppetSpec::Files def fake_fstab - os = Facter['operatingsystem'] + os = Facter.value(:operatingsystem) if os == "Solaris" name = "solaris.fstab" elsif os == "FreeBSD" @@ -22,171 +24,254 @@ module ParsedMountTesting # Catchall for other fstabs name = "linux.fstab" end - oldpath = @provider_class.default_target fakefile(File::join("data/types/mount", name)) end - def mkmountargs - mount = nil - - if defined?(@pcount) - @pcount += 1 + def fake_mountoutput + os = Facter.value(:operatingsystem) + if os == "Darwin" + name = "darwin.mount" + elsif os == "HP-UX" + name = "hpux.mount" + elsif os == "Solaris" + name = "solaris.mount" + elsif os == "AIX" + name = "aix.mount" else - @pcount = 1 - end - args = { - :name => "/fspuppet#{@pcount}", - :device => "/dev/dsk#{@pcount}", - } - - @provider_class.fields(:parsed).each do |field| - args[field] = "fake#{field}#{@pcount}" unless args.include? field + # Catchall for other fstabs + name = "linux.mount" end - - args + fakefile(File::join("data/providers/mount/parsed", name)) end - def mkmount - hash = mkmountargs - #hash[:provider] = @provider_class.name - - fakeresource = stub :type => :mount, :name => hash[:name] - fakeresource.stubs(:[]).with(:name).returns(hash[:name]) - fakeresource.stubs(:should).with(:target).returns(nil) - - mount = @provider_class.new(fakeresource) - hash[:record_type] = :parsed - hash[:ensure] = :present - mount.property_hash = hash - - mount - end - - # Here we just create a fake host type that answers to all of the methods - # but does not modify our actual system. - def mkfaketype - @provider.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) - end end provider_class = Puppet::Type.type(:mount).provider(:parsed) describe provider_class do + before :each do @mount_class = Puppet::Type.type(:mount) - @provider_class = @mount_class.provider(:parsed) + @provider = @mount_class.provider(:parsed) + end + + # LAK:FIXME I can't mock Facter because this test happens at parse-time. + it "should default to /etc/vfstab on Solaris" do + pending "This test only works on Solaris" unless Facter.value(:operatingsystem) == 'Solaris' + Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/vfstab' + end + + it "should default to /etc/fstab on anything else" do + pending "This test does not work on Solaris" if Facter.value(:operatingsystem) == 'Solaris' + Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/fstab' end + describe "when parsing a line" do - describe provider_class do - include ParsedMountTesting + it "should not crash on incomplete lines in fstab" do + parse = @provider.parse <<-FSTAB +/dev/incomplete +/dev/device name +FSTAB + lambda{ @provider.to_line(parse[0]) }.should_not raise_error + end - it "should be able to parse all of the example mount tabs" do - tab = fake_fstab - @provider = @provider_class - # LAK:FIXME Again, a relatively bad test, but I don't know how to rspec-ify this. - # I suppose this is more of an integration test? I dunno. - fakedataparse(tab) do - # Now just make we've got some mounts we know will be there - hashes = @provider_class.target_records(tab).find_all { |i| i.is_a? Hash } - (hashes.length > 0).should be_true - root = hashes.find { |i| i[:name] == "/" } + describe "on Solaris", :if => Facter.value(:operatingsystem) == 'Solaris' do - proc { @provider_class.to_file(hashes) }.should_not raise_error + before :each do + @example_line = "/dev/dsk/c0d0s0 /dev/rdsk/c0d0s0 \t\t / \t ufs 1 no\t-" end - end - # LAK:FIXME I can't mock Facter because this test happens at parse-time. - it "should default to /etc/vfstab on Solaris and /etc/fstab everywhere else" do - should = case Facter.value(:operatingsystem) - when "Solaris"; "/etc/vfstab" - else - "/etc/fstab" - end - Puppet::Type.type(:mount).provider(:parsed).default_target.should == should - end + it "should extract device from the first field" do + @provider.parse_line(@example_line)[:device].should == '/dev/dsk/c0d0s0' + end - it "should not crash on incomplete lines in fstab" do - parse = @provider_class.parse <<-FSTAB -/dev/incomplete -/dev/device name - FSTAB + it "should extract blockdevice from second field" do + @provider.parse_line(@example_line)[:blockdevice].should == "/dev/rdsk/c0d0s0" + end - lambda{ @provider_class.to_line(parse[0]) }.should_not raise_error - end - end + it "should extract name from third field" do + @provider.parse_line(@example_line)[:name].should == "/" + end - describe provider_class, " when mounting an absent filesystem" do - include ParsedMountTesting + it "should extract fstype from fourth field" do + @provider.parse_line(@example_line)[:fstype].should == "ufs" + end - # #730 - Make sure 'flush' is called when a mount is moving from absent to mounted - it "should flush the fstab to disk" do - mount = mkmount + it "should extract pass from fifth field" do + @provider.parse_line(@example_line)[:pass].should == "1" + end - # Mark the mount as absent - mount.property_hash[:ensure] = :absent + it "should extract atboot from sixth field" do + @provider.parse_line(@example_line)[:atboot].should == "no" + end - mount.stubs(:mountcmd) # just so we don't actually try to mount anything + it "should extract options from seventh field" do + @provider.parse_line(@example_line)[:options].should == "-" + end - mount.expects(:flush) - mount.mount end - end - describe provider_class, " when modifying the filesystem tab" do - include ParsedMountTesting - before do - Puppet.settings.stubs(:use) - # Never write to disk, only to RAM. - #@provider_class.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) - @provider_class.stubs(:target_object).returns(Puppet::Util::FileType.filetype(:ram).new("eh")) - @provider_class.clear + describe "on other platforms than Solaris", :if => Facter.value(:operatingsystem) != 'Solaris' do - @mount = mkmount - @target = @provider_class.default_target - end + before :each do + @example_line = "/dev/vg00/lv01\t/spare \t \t ext3 defaults\t1 2" + end - it "should write the mount to disk when :flush is called" do - old_text = @provider_class.target_object(@provider_class.default_target).read + it "should extract device from the first field" do + @provider.parse_line(@example_line)[:device].should == '/dev/vg00/lv01' + end - @mount.flush + it "should extract name from second field" do + @provider.parse_line(@example_line)[:name].should == "/spare" + end + + it "should extract fstype from third field" do + @provider.parse_line(@example_line)[:fstype].should == "ext3" + end + + it "should extract options from fourth field" do + @provider.parse_line(@example_line)[:options].should == "defaults" + end + + it "should extract dump from fifth field" do + @provider.parse_line(@example_line)[:dump].should == "1" + end + + it "should extract options from sixth field" do + @provider.parse_line(@example_line)[:pass].should == "2" + end - text = @provider_class.target_object(@provider_class.default_target).read - text.should == old_text + @mount.class.to_line(@mount.property_hash) + "\n" end + end - describe provider_class, " when parsing information about the root filesystem", :if => Facter["operatingsystem"].value != "Darwin" do + describe "mountinstances" do include ParsedMountTesting - before do - @mount = @mount_class.new :name => "/" - @provider = @mount.provider + it "should get name from mountoutput found on Solaris" do + Facter.stubs(:value).with(:operatingsystem).returns 'Solaris' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts.size.should == 6 + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/proc', :mounted => :yes } + mounts[2].should == { :name => '/etc/mnttab', :mounted => :yes } + mounts[3].should == { :name => '/tmp', :mounted => :yes } + mounts[4].should == { :name => '/export/home', :mounted => :yes } + mounts[5].should == { :name => '/ghost', :mounted => :yes } end - it "should have a filesystem tab" do - FileTest.should be_exist(@provider_class.default_target) + it "should get name from mountoutput found on HP-UX" do + Facter.stubs(:value).with(:operatingsystem).returns 'HP-UX' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts.size.should == 17 + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/devices', :mounted => :yes } + mounts[2].should == { :name => '/dev', :mounted => :yes } + mounts[3].should == { :name => '/system/contract', :mounted => :yes } + mounts[4].should == { :name => '/proc', :mounted => :yes } + mounts[5].should == { :name => '/etc/mnttab', :mounted => :yes } + mounts[6].should == { :name => '/etc/svc/volatile', :mounted => :yes } + mounts[7].should == { :name => '/system/object', :mounted => :yes } + mounts[8].should == { :name => '/etc/dfs/sharetab', :mounted => :yes } + mounts[9].should == { :name => '/lib/libc.so.1', :mounted => :yes } + mounts[10].should == { :name => '/dev/fd', :mounted => :yes } + mounts[11].should == { :name => '/tmp', :mounted => :yes } + mounts[12].should == { :name => '/var/run', :mounted => :yes } + mounts[13].should == { :name => '/export', :mounted => :yes } + mounts[14].should == { :name => '/export/home', :mounted => :yes } + mounts[15].should == { :name => '/rpool', :mounted => :yes } + mounts[16].should == { :name => '/ghost', :mounted => :yes } end - it "should find the root filesystem" do - @provider_class.prefetch("/" => @mount) - @mount.provider.property_hash[:ensure].should == :present + it "should get name from mountoutput found on Darwin" do + Facter.stubs(:value).with(:operatingsystem).returns 'Darwin' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts.size.should == 6 + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/dev', :mounted => :yes } + mounts[2].should == { :name => '/net', :mounted => :yes } + mounts[3].should == { :name => '/home', :mounted => :yes } + mounts[4].should == { :name => '/usr', :mounted => :yes } + mounts[5].should == { :name => '/ghost', :mounted => :yes } end - it "should determine that the root fs is mounted" do - @provider_class.prefetch("/" => @mount) - @mount.provider.should be_mounted + it "should get name from mountoutput found on Linux" do + Facter.stubs(:value).with(:operatingsystem).returns 'Gentoo' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/lib64/rc/init.d', :mounted => :yes } + mounts[2].should == { :name => '/sys', :mounted => :yes } + mounts[3].should == { :name => '/usr/portage', :mounted => :yes } + mounts[4].should == { :name => '/ghost', :mounted => :yes } end + + it "should get name from mountoutput found on AIX" do + Facter.stubs(:value).with(:operatingsystem).returns 'AIX' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/tmp', :mounted => :yes } + mounts[2].should == { :name => '/home', :mounted => :yes } + mounts[3].should == { :name => '/usr', :mounted => :yes } + mounts[4].should == { :name => '/usr/code', :mounted => :yes } + end + + it "should raise an error if a line is not understandable" do + @provider.stubs(:mountcmd).returns("bazinga!") + lambda { @provider.mountinstances }.should raise_error Puppet::Error + end + end - describe provider_class, " when mounting and unmounting" do + describe "when prefetching" do include ParsedMountTesting - it "should call the 'mount' command to mount the filesystem" + before :each do + # Note: we have to stub default_target before creating resources + # because it is used by Puppet::Type::Mount.new to populate the + # :target property. + @provider.stubs(:default_target).returns fake_fstab + + @res_ghost = Puppet::Type::Mount.new(:name => '/ghost') # in no fake fstab + @res_mounted = Puppet::Type::Mount.new(:name => '/') # in every fake fstab + @res_unmounted = Puppet::Type::Mount.new(:name => '/boot') # in every fake fstab + @res_absent = Puppet::Type::Mount.new(:name => '/absent') # in no fake fstab + + # Simulate transaction.rb:prefetch + @resource_hash = {} + [@res_ghost, @res_mounted, @res_unmounted, @res_absent].each do |resource| + @resource_hash[resource.name] = resource + end + + @provider.stubs(:mountcmd).returns File.read(fake_mountoutput) + end + + it "should set :ensure to :unmounted if found in fstab but not mounted" do + @provider.prefetch(@resource_hash) + @res_unmounted.provider.get(:ensure).should == :unmounted + end + + it "should set :ensure to :mounted if found in fstab and mounted" do + @provider.prefetch(@resource_hash) + @res_ghost.provider.get(:ensure).should == :ghost + end + + it "should set :ensure to :ghost if not found in fstab but mounted" do + @provider.prefetch(@resource_hash) + @res_mounted.provider.get(:ensure).should == :mounted + end - it "should call the 'unmount' command to unmount the filesystem" + it "should set :ensure to :absent if not found in fstab and not mounted" do + @provider.prefetch(@resource_hash) + @res_absent.provider.get(:ensure).should == :absent + end - it "should specify the filesystem when remounting a filesystem" end + end diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb index f567a4a40..3fc8a8664 100755 --- a/spec/unit/provider/mount_spec.rb +++ b/spec/unit/provider/mount_spec.rb @@ -19,18 +19,13 @@ describe Puppet::Provider::Mount do describe Puppet::Provider::Mount, " when mounting" do - it "should use the 'mountcmd' method to mount" do - @mounter.stubs(:options).returns(nil) - @mounter.expects(:mountcmd) - - @mounter.mount + before :each do + @mounter.stubs(:get).with(:ensure).returns(:mounted) end - it "should flush before mounting if a flush method exists" do - @mounter.meta_def(:flush) { } - @mounter.expects(:flush) - @mounter.stubs(:mountcmd) + it "should use the 'mountcmd' method to mount" do @mounter.stubs(:options).returns(nil) + @mounter.expects(:mountcmd) @mounter.mount end @@ -48,6 +43,23 @@ describe Puppet::Provider::Mount do @mounter.mount end + + it "should update the :ensure state to :mounted if it was :unmounted before" do + @mounter.expects(:mountcmd) + @mounter.stubs(:options).returns(nil) + @mounter.expects(:get).with(:ensure).returns(:unmounted) + @mounter.expects(:set).with(:ensure => :mounted) + @mounter.mount + end + + it "should update the :ensure state to :ghost if it was :absent before" do + @mounter.expects(:mountcmd) + @mounter.stubs(:options).returns(nil) + @mounter.expects(:get).with(:ensure).returns(:absent) + @mounter.expects(:set).with(:ensure => :ghost) + @mounter.mount + end + end describe Puppet::Provider::Mount, " when remounting" do @@ -77,69 +89,58 @@ describe Puppet::Provider::Mount do describe Puppet::Provider::Mount, " when unmounting" do + before :each do + @mounter.stubs(:get).with(:ensure).returns(:unmounted) + end + it "should call the :umount command with the resource name" do @mounter.expects(:umount).with(@name) @mounter.unmount end - end - - describe Puppet::Provider::Mount, " when determining if it is mounted" do - - it "should parse the results of running the mount command with no arguments" do - Facter.stubs(:value).returns("whatever") - @mounter.expects(:mountcmd).returns("") - @mounter.mounted? + it "should update the :ensure state to :absent if it was :ghost before" do + @mounter.expects(:umount).with(@name).returns true + @mounter.expects(:get).with(:ensure).returns(:ghost) + @mounter.expects(:set).with(:ensure => :absent) + @mounter.unmount end - it "should match ' on /private/var/automount<name>' if the operating system is Darwin" do - Facter.stubs(:value).with("operatingsystem").returns("Darwin") - @mounter.expects(:mountcmd).returns("/dev/whatever on /private/var/automount/\ndevfs on /dev") - - @mounter.should be_mounted + it "should update the :ensure state to :unmounted if it was :mounted before" do + @mounter.expects(:umount).with(@name).returns true + @mounter.expects(:get).with(:ensure).returns(:mounted) + @mounter.expects(:set).with(:ensure => :unmounted) + @mounter.unmount end - it "should match ' on <name>' if the operating system is Darwin" do - Facter.stubs(:value).with("operatingsystem").returns("Darwin") - @mounter.expects(:mountcmd).returns("/dev/disk03 on / (local, journaled)\ndevfs on /dev") - - @mounter.should be_mounted - end + end - it "should match '^<name> on' if the operating system is Solaris" do - Facter.stubs(:value).with("operatingsystem").returns("Solaris") - @mounter.expects(:mountcmd).returns("/ on /dev/dsk/whatever\n/var on /dev/dsk/other") + describe Puppet::Provider::Mount, " when determining if it is mounted" do - @mounter.should be_mounted + it "should query the property_hash" do + @mounter.expects(:get).with(:ensure).returns(:mounted) + @mounter.mounted? end - it "should match '^<name> on' if the operating system is HP-UX" do - Facter.stubs(:value).with("operatingsystem").returns("HP-UX") - @mounter.expects(:mountcmd).returns("/ on /dev/dsk/whatever\n/var on /dev/dsk/other") - - @mounter.should be_mounted + it "should return true if prefetched value is :mounted" do + @mounter.stubs(:get).with(:ensure).returns(:mounted) + @mounter.mounted? == true end - it "should match mounted devices if the operating system is AIX" do - Facter.stubs(:value).with("operatingsystem").returns("AIX") - mount_data = File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'unit', 'provider', 'mount', 'mount-output.aix.txt')) - @mounter.expects(:mountcmd).returns(mount_data) - - @mounter.should be_mounted + it "should return true if prefetched value is :ghost" do + @mounter.stubs(:get).with(:ensure).returns(:ghost) + @mounter.mounted? == true end - it "should match ' on <name>' if the operating system is not Darwin, Solaris, or HP-UX" do - Facter.stubs(:value).with("operatingsystem").returns("Debian") - @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on / and stuff\n/dev/other/disk on /var and stuff") - - @mounter.should be_mounted + it "should return false if prefetched value is :absent" do + @mounter.stubs(:get).with(:ensure).returns(:absent) + @mounter.mounted? == false end - it "should not be considered mounted if it did not match the mount output" do - Facter.stubs(:value).with("operatingsystem").returns("Debian") - @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on /something/else and stuff\n/dev/other/disk on /var and stuff") - - @mounter.should_not be_mounted + it "should return false if prefetched value is :unmounted" do + @mounter.stubs(:get).with(:ensure).returns(:unmounted) + @mounter.mounted? == false end + end + end diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb index 11e9233e0..2e5be165a 100755 --- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb +++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb @@ -104,6 +104,7 @@ describe provider_class do before :each do @resource = stub("resource", :name => "foo") @resource.stubs(:[]).returns "foo" + @resource.class.stubs(:key_attributes).returns( [:name] ) @provider = provider_class.new(@resource) provider_class.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) diff --git a/spec/unit/provider/user/user_role_add_spec.rb b/spec/unit/provider/user/user_role_add_spec.rb index 9cf649267..f73942389 100644 --- a/spec/unit/provider/user/user_role_add_spec.rb +++ b/spec/unit/provider/user/user_role_add_spec.rb @@ -115,6 +115,7 @@ describe provider_class do describe "when allow duplicate is enabled" do before do @resource.expects(:allowdupe?).returns true + @resource.stubs(:system?) @provider.stubs(:is_role?).returns(false) @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-o") } diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb index 9ebba596c..fd75c43f3 100755 --- a/spec/unit/provider/user/useradd_spec.rb +++ b/spec/unit/provider/user/useradd_spec.rb @@ -15,6 +15,7 @@ describe provider_class do # #1360 it "should add -o when allowdupe is enabled and the user is being created" do @resource.expects(:allowdupe?).returns true + @resource.expects(:system?).returns true @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-o") } @provider.create @@ -27,6 +28,14 @@ describe provider_class do @provider.uid = 150 end + it "should add -r when system is enabled" do + @resource.expects(:allowdupe?).returns true + @resource.expects(:system?).returns true + @provider.stubs(:execute) + @provider.expects(:execute).with { |args| args.include?("-r") } + @provider.create + end + it "should set password age rules" do provider_class.has_feature :manages_password_age @resource = Puppet::Type.type(:user).new :name => "myuser", :password_min_age => 5, :password_max_age => 10, :provider => :useradd @@ -53,6 +62,23 @@ describe provider_class do end end + describe "when checking to add system users" do + it "should check system users" do + @resource.expects(:system?) + @provider.check_system_users + end + + it "should return an array with a flag if it's a system user" do + @resource.stubs(:system?).returns true + @provider.check_system_users.must == ["-r"] + end + + it "should return an empty array if it's not a system user" do + @resource.stubs(:system?).returns false + @provider.check_system_users.must == [] + end + end + describe "when checking manage home" do it "should check manage home" do @resource.expects(:managehome?) @@ -88,6 +114,7 @@ describe provider_class do before do @resource.stubs(:allowdupe?).returns true @resource.stubs(:managehome?).returns true + @resource.stubs(:system?).returns true end it "should call command with :add" do @@ -115,20 +142,32 @@ describe provider_class do @provider.addcmd end + it "should return an array with -r if system? is true" do + resource = Puppet::Type.type(:user).new( :name => "bob", :system => true) + + provider_class.new( resource ).addcmd.should include("-r") + end + + it "should return an array without -r if system? is false" do + resource = Puppet::Type.type(:user).new( :name => "bob", :system => false) + + provider_class.new( resource ).addcmd.should_not include("-r") + end + it "should return an array with full command" do @provider.stubs(:command).with(:add).returns("useradd") @provider.stubs(:add_properties).returns(["-G", "somegroup"]) @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:[]).with(:expiry).returns("somedate") - @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", '-e somedate', "someuser"] + @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", '-e somedate', "-r", "someuser"] end - it "should return an array without -e if expery is undefined full command" do + it "should return an array without -e if expiry is undefined full command" do @provider.stubs(:command).with(:add).returns("useradd") @provider.stubs(:add_properties).returns(["-G", "somegroup"]) @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:[]).with(:expiry).returns nil - @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", "someuser"] + @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", "-r", "someuser"] end end @@ -136,6 +175,7 @@ describe provider_class do before do @resource.stubs(:allowdupe?).returns true @resource.stubs(:managehome?).returns true + @resource.stubs(:system?).returns true end it "should call command with :pass" do diff --git a/spec/unit/reports/store_spec.rb b/spec/unit/reports/store_spec.rb index 1acb5badd..9d9042386 100644 --- a/spec/unit/reports/store_spec.rb +++ b/spec/unit/reports/store_spec.rb @@ -11,7 +11,7 @@ describe processor do describe "#process" do include PuppetSpec::Files before :each do - Puppet[:reportdir] = tmpdir('reports') + Puppet[:reportdir] = tmpdir('reports') << '/reports' @report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report2.6.x.yaml')).extend processor end diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index ff31b2492..345ccd06e 100755 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -99,11 +99,11 @@ describe Puppet::Resource do end it 'should fail if strict is set and type does not exist' do - lambda { Puppet::Resource.new('foo', 'title', {:strict=>true}) }.should raise_error(ArgumentError, 'Invalid resource type foo') + lambda { Puppet::Resource.new('foo', 'title', {:strict=>true}) }.should raise_error(ArgumentError, 'Invalid resource type foo') end it 'should fail if strict is set and class does not exist' do - lambda { Puppet::Resource.new('Class', 'foo', {:strict=>true}) }.should raise_error(ArgumentError, 'Could not find declared class foo') + lambda { Puppet::Resource.new('Class', 'foo', {:strict=>true}) }.should raise_error(ArgumentError, 'Could not find declared class foo') end it "should fail if the title is a hash and the type is not a valid resource reference string" do @@ -463,6 +463,28 @@ describe Puppet::Resource do end end + describe "when loading 0.25.x storedconfigs YAML" do + before :each do + @old_storedconfig_yaml = %q{--- !ruby/object:Puppet::Resource::Reference +builtin_type: +title: /tmp/bar +type: File +} + end + + it "should deserialize a Puppet::Resource::Reference without exceptions" do + lambda { YAML.load(@old_storedconfig_yaml) }.should_not raise_error + end + + it "should deserialize as a Puppet::Resource::Reference as a Puppet::Resource" do + YAML.load(@old_storedconfig_yaml).class.should == Puppet::Resource + end + + it "should to_hash properly" do + YAML.load(@old_storedconfig_yaml).to_hash.should == { :path => "/tmp/bar" } + end + end + describe "when converting to a RAL resource" do it "should use the resource type's :new method to create the resource if the resource is of a builtin type" do resource = Puppet::Resource.new("file", @basepath+"/my/file") @@ -486,19 +508,23 @@ describe Puppet::Resource do describe "when converting to puppet code" do before do - @resource = Puppet::Resource.new("one::two", "/my/file", :parameters => {:noop => true, :foo => %w{one two}}) - end - - it "should print the type and title" do - @resource.to_manifest.should be_include("one::two { '/my/file':\n") - end - - it "should print each parameter, with the value single-quoted" do - @resource.to_manifest.should be_include(" noop => 'true'") + @resource = Puppet::Resource.new("one::two", "/my/file", + :parameters => { + :noop => true, + :foo => %w{one two}, + :ensure => 'present', + } + ) end - it "should print array values appropriately" do - @resource.to_manifest.should be_include(" foo => ['one','two']") + it "should align, sort and add trailing commas to attributes with ensure first" do + @resource.to_manifest.should == <<-HEREDOC.gsub(/^\s{8}/, '').gsub(/\n$/, '') + one::two { '/my/file': + ensure => 'present', + foo => ['one', 'two'], + noop => 'true', + } + HEREDOC end end diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb index 5ee26cc52..bd2b2adaf 100755 --- a/spec/unit/type/file/content_spec.rb +++ b/spec/unit/type/file/content_spec.rb @@ -4,15 +4,14 @@ Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f content = Puppet::Type.type(:file).attrclass(:content) describe content do + include PuppetSpec::Files before do - @resource = Puppet::Type.type(:file).new :path => "/foo/bar" + @filename = tmpfile('testfile') + @resource = Puppet::Type.type(:file).new :path => @filename + File.open(@filename, 'w') {|f| f.write "initial file content"} content.stubs(:standalone?).returns(false) end - it "should be a subclass of Property" do - content.superclass.must == Puppet::Property - end - describe "when determining the checksum type" do it "should use the type specified in the source checksum if a source is set" do @resource[:source] = "/foo" @@ -249,10 +248,10 @@ describe content do describe "when writing" do before do @content = content.new(:resource => @resource) - @fh = stub_everything end it "should attempt to read from the filebucket if no actual content nor source exists" do + @fh = File.open(@filename, 'w') @content.should = "{md5}foo" @content.resource.bucket.class.any_instance.stubs(:getfile).returns "foo" @content.write(@fh) @@ -302,166 +301,68 @@ describe content do describe "from local source" do before(:each) do - @content.stubs(:actual_content).returns(nil) - @source = stub_everything 'source', :local? => true, :full_path => "/path/to/source" - @resource.stubs(:parameter).with(:source).returns @source - - @sum = stub_everything 'sum' - @resource.stubs(:parameter).with(:checksum).returns(@sum) - - @digest = stub_everything 'digest' - @sum.stubs(:sum_stream).yields(@digest) - - @file = stub_everything 'file' - File.stubs(:open).yields(@file) - @file.stubs(:read).with(8192).returns("chunk1").then.returns("chunk2").then.returns(nil) - end - - it "should open the local file" do - File.expects(:open).with("/path/to/source", "r") - @content.write(@fh) - end + @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false + @sourcename = tmpfile('source') + @source_content = "source file content"*10000 + @sourcefile = File.open(@sourcename, 'w') {|f| f.write @source_content} - it "should read the local file by chunks" do - @file.expects(:read).with(8192).returns("chunk1").then.returns(nil) - @content.write(@fh) + @content = @resource.newattr(:content) + @source = @resource.newattr(:source) + @source.stubs(:metadata).returns stub_everything('metadata', :source => @sourcename, :ftype => 'file') end - it "should write each chunk to the file" do - @fh.expects(:print).with("chunk1").then.with("chunk2") - @content.write(@fh) - end - - it "should pass each chunk to the current sum stream" do - @digest.expects(:<<).with("chunk1").then.with("chunk2") - @content.write(@fh) + it "should copy content from the source to the file" do + @resource.write(@source) + File.read(@filename).should == @source_content end it "should return the checksum computed" do - @sum.stubs(:sum_stream).yields(@digest).returns("checksum") - @content.write(@fh).should == "checksum" + File.open(@filename, 'w') do |file| + @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" + end end end describe "from remote source" do before(:each) do - @response = stub_everything 'mock response', :code => "404" + @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false + @response = stub_everything 'response', :code => "200" + @source_content = "source file content"*10000 + @response.stubs(:read_body).multiple_yields(*(["source file content"]*10000)) + @conn = stub_everything 'connection' @conn.stubs(:request_get).yields(@response) Puppet::Network::HttpPool.stubs(:http_instance).returns @conn - @content.stubs(:actual_content).returns(nil) - @source = stub_everything 'source', :local? => false, :full_path => "/path/to/source", :server => "server", :port => 1234 - @resource.stubs(:parameter).with(:source).returns @source - - @sum = stub_everything 'sum' - @resource.stubs(:parameter).with(:checksum).returns(@sum) - - @digest = stub_everything 'digest' - @sum.stubs(:sum_stream).yields(@digest) - end - - it "should open a network connection to source server and port" do - Puppet::Network::HttpPool.expects(:http_instance).with("server", 1234).returns @conn - @content.write(@fh) - end - - it "should send the correct indirection uri" do - @conn.expects(:request_get).with { |uri,headers| uri == "/production/file_content/path/to/source" }.yields(@response) - @content.write(@fh) + @content = @resource.newattr(:content) + @sourcename = "puppet:///test/foo" + @source = @resource.newattr(:source) + @source.stubs(:metadata).returns stub_everything('metadata', :source => @sourcename, :ftype => 'file') end - it "should return nil if source is not found" do - @response.expects(:code).returns("404") - @content.write(@fh).should == nil + it "should write the contents to the file" do + @resource.write(@source) + File.read(@filename).should == @source_content end it "should not write anything if source is not found" do - @response.expects(:code).returns("404") - @fh.expects(:print).never - @content.write(@fh).should == nil + @response.stubs(:code).returns("404") + lambda {@resource.write(@source)}.should raise_error(Net::HTTPError) { |e| e.message =~ /404/ } + File.read(@filename).should == "initial file content" end it "should raise an HTTP error in case of server error" do - @response.expects(:code).returns("500") - lambda { @content.write(@fh) }.should raise_error - end - - it "should write content by chunks" do - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - @fh.expects(:print).with("chunk1").then.with("chunk2") - @content.write(@fh) - end - - it "should pass each chunk to the current sum stream" do - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - @digest.expects(:<<).with("chunk1").then.with("chunk2") - @content.write(@fh) + @response.stubs(:code).returns("500") + lambda { @content.write(@fh) }.should raise_error { |e| e.message.include? @source_content } end it "should return the checksum computed" do - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - @sum.expects(:sum_stream).yields(@digest).returns("checksum") - @content.write(@fh).should == "checksum" - end - - it "should get the current accept encoding header value" do - @content.expects(:add_accept_encoding) - @content.write(@fh) - end - - it "should uncompress body on error" do - @response.expects(:code).returns("500") - @response.expects(:body).returns("compressed body") - @content.expects(:uncompress_body).with(@response).returns("uncompressed") - lambda { @content.write(@fh) }.should raise_error { |e| e.message =~ /uncompressed/ } - end - - it "should uncompress chunk by chunk" do - uncompressor = stub_everything 'uncompressor' - @content.expects(:uncompress).with(@response).yields(uncompressor) - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - - uncompressor.expects(:uncompress).with("chunk1").then.with("chunk2") - @content.write(@fh) - end - - it "should write uncompressed chunks to the file" do - uncompressor = stub_everything 'uncompressor' - @content.expects(:uncompress).with(@response).yields(uncompressor) - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - - uncompressor.expects(:uncompress).with("chunk1").returns("uncompressed1") - uncompressor.expects(:uncompress).with("chunk2").returns("uncompressed2") - - @fh.expects(:print).with("uncompressed1") - @fh.expects(:print).with("uncompressed2") - - @content.write(@fh) - end - - it "should pass each uncompressed chunk to the current sum stream" do - uncompressor = stub_everything 'uncompressor' - @content.expects(:uncompress).with(@response).yields(uncompressor) - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - - uncompressor.expects(:uncompress).with("chunk1").returns("uncompressed1") - uncompressor.expects(:uncompress).with("chunk2").returns("uncompressed2") - - @digest.expects(:<<).with("uncompressed1").then.with("uncompressed2") - @content.write(@fh) + File.open(@filename, 'w') do |file| + @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" + end end end - describe "from a filebucket" do - end - # These are testing the implementation rather than the desired behaviour; while that bites, there are a whole # pile of other methods in the File type that depend on intimate details of this implementation and vice-versa. # If these blow up, you are gonna have to review the callers to make sure they don't explode! --daniel 2011-02-01 diff --git a/spec/unit/type/file/selinux_spec.rb b/spec/unit/type/file/selinux_spec.rb index 043471dec..a2444acd9 100644 --- a/spec/unit/type/file/selinux_spec.rb +++ b/spec/unit/type/file/selinux_spec.rb @@ -66,6 +66,11 @@ Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f @sel.default.must == expectedresult end + it "should return nil for defaults if selinux_ignore_defaults is true" do + @resource[:selinux_ignore_defaults] = :true + @sel.default.must be_nil + end + it "should be able to set a new context" do stat = stub 'stat', :ftype => "foo" @sel.should = %w{newone} diff --git a/spec/unit/type/mount_spec.rb b/spec/unit/type/mount_spec.rb index 0d74042e3..fdb67f7d5 100755 --- a/spec/unit/type/mount_spec.rb +++ b/spec/unit/type/mount_spec.rb @@ -11,10 +11,14 @@ describe Puppet::Type.type(:mount) do mount = Puppet::Type.type(:mount).new(:name => "yay") mount.should(:ensure).should be_nil end + + it "should have :name as the only keyattribut" do + Puppet::Type.type(:mount).key_attributes.should == [:name] + end end describe Puppet::Type.type(:mount), "when validating attributes" do - [:name, :remounts].each do |param| + [:name, :remounts, :provider].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:mount).attrtype(param).should == :param end @@ -38,9 +42,16 @@ describe Puppet::Type.type(:mount)::Ensure, "when validating values" do mount.should(:ensure).should == :defined end + it "should support :present as a value to :ensure" do + Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) + end + + it "should support :defined as a value to :ensure" do + Puppet::Type.type(:mount).new(:name => "yay", :ensure => :defined) + end + it "should support :unmounted as a value to :ensure" do - mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :unmounted) - mount.should(:ensure).should == :unmounted + Puppet::Type.type(:mount).new(:name => "yay", :ensure => :unmounted) end it "should support :absent as a value to :ensure" do @@ -54,7 +65,8 @@ end describe Puppet::Type.type(:mount)::Ensure do before :each do - @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock + provider_properties = {} + @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :property_hash => provider_properties Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) @mount = Puppet::Type.type(:mount).new(:name => "yay", :check => :ensure) @@ -74,134 +86,150 @@ describe Puppet::Type.type(:mount)::Ensure do end end - describe Puppet::Type.type(:mount)::Ensure, "when retrieving its current state" do - - it "should return the provider's value if it is :absent" do - @provider.expects(:ensure).returns(:absent) - @ensure.retrieve.should == :absent - end - - it "should return :mounted if the provider indicates it is mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:mounted?).returns(true) - @ensure.retrieve.should == :mounted - end - - it "should return :unmounted if the provider indicates it is not mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:mounted?).returns(false) - @ensure.retrieve.should == :unmounted - end - end - describe Puppet::Type.type(:mount)::Ensure, "when changing the host" do - it "should destroy itself if it should be absent" do - @provider.stubs(:mounted?).returns(false) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end - - it "should unmount itself before destroying if it is mounted and should be absent" do - @provider.expects(:mounted?).returns(true) - @provider.expects(:unmount) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end - - it "should create itself if it is absent and should be defined" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:mounted?).returns(true) - - @provider.stubs(:mounted?).returns(false) - @provider.expects(:create) - @ensure.should = :defined - @ensure.sync - end - - it "should not unmount itself if it is mounted and should be defined" do - @provider.stubs(:ensure).returns(:mounted) - @provider.stubs(:mounted?).returns(true) - - @provider.stubs(:create) - @provider.expects(:mount).never - @provider.expects(:unmount).never - @ensure.should = :defined - @ensure.sync - end - - it "should not mount itself if it is unmounted and should be defined" do - @provider.stubs(:ensure).returns(:unmounted) - @provider.stubs(:mounted?).returns(false) - - @ensure.stubs(:syncothers) - @provider.stubs(:create) + def test_ensure_change(options) + @provider.stubs(:get).with(:ensure).returns options[:from] + @provider.stubs(:ensure).returns options[:from] + @provider.stubs(:mounted?).returns([:mounted,:ghost].include? options[:from]) + @provider.expects(:create).times(options[:create] || 0) + @provider.expects(:destroy).times(options[:destroy] || 0) @provider.expects(:mount).never - @provider.expects(:unmount).never - @ensure.should = :present - @ensure.sync - end - - it "should unmount itself if it is mounted and should be unmounted" do - @provider.stubs(:ensure).returns(:present) - @provider.stubs(:mounted?).returns(true) - + @provider.expects(:unmount).times(options[:unmount] || 0) @ensure.stubs(:syncothers) - @provider.expects(:unmount) - @ensure.should = :unmounted + @ensure.should = options[:to] @ensure.sync - end + (!!@provider.property_hash[:needs_mount]).should == (!!options[:mount]) + end + + it "should create itself when changing from :ghost to :present" do + test_ensure_change(:from => :ghost, :to => :present, :create => 1) + end + + it "should create itself when changing from :absent to :present" do + test_ensure_change(:from => :absent, :to => :present, :create => 1) + end - it "should create and mount itself if it does not exist and should be mounted" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:mounted?).returns(false) - @provider.expects(:create) - @ensure.stubs(:syncothers) - @provider.expects(:mount) - @ensure.should = :mounted - @ensure.sync - end + it "should create itself and unmount when changing from :ghost to :unmounted" do + test_ensure_change(:from => :ghost, :to => :unmounted, :create => 1, :unmount => 1) + end + + it "should unmount resource when changing from :mounted to :unmounted" do + test_ensure_change(:from => :mounted, :to => :unmounted, :unmount => 1) + end + + it "should create itself when changing from :absent to :unmounted" do + test_ensure_change(:from => :absent, :to => :unmounted, :create => 1) + end + + it "should unmount resource when changing from :ghost to :absent" do + test_ensure_change(:from => :ghost, :to => :absent, :unmount => 1) + end + + it "should unmount and destroy itself when changing from :mounted to :absent" do + test_ensure_change(:from => :mounted, :to => :absent, :destroy => 1, :unmount => 1) + end + + it "should destroy itself when changing from :unmounted to :absent" do + test_ensure_change(:from => :unmounted, :to => :absent, :destroy => 1) + end + + it "should create itself when changing from :ghost to :mounted" do + test_ensure_change(:from => :ghost, :to => :mounted, :create => 1) + end + + it "should create itself and mount when changing from :absent to :mounted" do + test_ensure_change(:from => :absent, :to => :mounted, :create => 1, :mount => 1) + end + + it "should mount resource when changing from :unmounted to :mounted" do + test_ensure_change(:from => :unmounted, :to => :mounted, :mount => 1) + end + + + it "should be in sync if it is :absent and should be :absent" do + @ensure.should = :absent + @ensure.safe_insync?(:absent).should == true + end + + it "should be out of sync if it is :absent and should be :defined" do + @ensure.should = :defined + @ensure.safe_insync?(:absent).should == false + end + + it "should be out of sync if it is :absent and should be :mounted" do + @ensure.should = :mounted + @ensure.safe_insync?(:absent).should == false + end + + it "should be out of sync if it is :absent and should be :unmounted" do + @ensure.should = :unmounted + @ensure.safe_insync?(:absent).should == false + end + + it "should be out of sync if it is :mounted and should be :absent" do + @ensure.should = :absent + @ensure.safe_insync?(:mounted).should == false + end + + it "should be in sync if it is :mounted and should be :defined" do + @ensure.should = :defined + @ensure.safe_insync?(:mounted).should == true + end + + it "should be in sync if it is :mounted and should be :mounted" do + @ensure.should = :mounted + @ensure.safe_insync?(:mounted).should == true + end + + it "should be out in sync if it is :mounted and should be :unmounted" do + @ensure.should = :unmounted + @ensure.safe_insync?(:mounted).should == false + end + + + it "should be out of sync if it is :unmounted and should be :absent" do + @ensure.should = :absent + @ensure.safe_insync?(:unmounted).should == false + end + + it "should be in sync if it is :unmounted and should be :defined" do + @ensure.should = :defined + @ensure.safe_insync?(:unmounted).should == true + end + + it "should be out of sync if it is :unmounted and should be :mounted" do + @ensure.should = :mounted + @ensure.safe_insync?(:unmounted).should == false + end + + it "should be in sync if it is :unmounted and should be :unmounted" do + @ensure.should = :unmounted + @ensure.safe_insync?(:unmounted).should == true + end - it "should mount itself if it is present and should be mounted" do - @provider.stubs(:ensure).returns(:present) - @provider.stubs(:mounted?).returns(false) - @ensure.stubs(:syncothers) - @provider.expects(:mount) - @ensure.should = :mounted - @ensure.sync - end - it "should create but not mount itself if it is absent and mounted and should be mounted" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:mounted?).returns(true) - @ensure.stubs(:syncothers) - @provider.expects(:create) - @ensure.should = :mounted - @ensure.sync - end - - it "should be insync if it is mounted and should be defined" do - @ensure.should = :defined - @ensure.safe_insync?(:mounted).should == true - end - - it "should be insync if it is unmounted and should be defined" do - @ensure.should = :defined - @ensure.safe_insync?(:unmounted).should == true - end + it "should be out of sync if it is :ghost and should be :absent" do + @ensure.should = :absent + @ensure.safe_insync?(:ghost).should == false + end - it "should be insync if it is mounted and should be present" do - @ensure.should = :present - @ensure.safe_insync?(:mounted).should == true - end + it "should be out of sync if it is :ghost and should be :defined" do + @ensure.should = :defined + @ensure.safe_insync?(:ghost).should == false + end + + it "should be out of sync if it is :ghost and should be :mounted" do + @ensure.should = :mounted + @ensure.safe_insync?(:ghost).should == false + end + + it "should be out of sync if it is :ghost and should be :unmounted" do + @ensure.should = :unmounted + @ensure.safe_insync?(:ghost).should == false + end - it "should be insync if it is unmounted and should be present" do - @ensure.should = :present - @ensure.safe_insync?(:unmounted).should == true - end - end + end describe Puppet::Type.type(:mount), "when responding to events" do @@ -258,4 +286,21 @@ describe Puppet::Type.type(:mount), "when modifying an existing mount entry" do @catalog.apply end + + it "should umount before flushing changes to disk" do + syncorder = sequence('syncorder') + @mount.provider.expects(:options).returns 'soft' + @mount.provider.expects(:ensure).returns :mounted + + @mount.provider.expects(:unmount).in_sequence(syncorder) + @mount.provider.expects(:options=).in_sequence(syncorder).with 'hard' + @mount.expects(:flush).in_sequence(syncorder) # Call inside syncothers + @mount.expects(:flush).in_sequence(syncorder) # I guess transaction or anything calls flush again + + @mount[:ensure] = :unmounted + @mount[:options] = 'hard' + + @catalog.apply + end + end diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb index 297134446..5a84af443 100755 --- a/spec/unit/type/user_spec.rb +++ b/spec/unit/type/user_spec.rb @@ -43,6 +43,10 @@ describe user do user.provider_feature(:manages_password_age).should_not be_nil end + it "should have a system_users feature" do + user.provider_feature(:system_users).should_not be_nil + end + describe "instances" do it "should have a valid provider" do user.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) diff --git a/spec/unit/type_spec.rb b/spec/unit/type_spec.rb index b7a08977e..6d9d0b234 100755 --- a/spec/unit/type_spec.rb +++ b/spec/unit/type_spec.rb @@ -434,7 +434,7 @@ describe Puppet::Type do patterns.length.should == 1 patterns[0].length.should == 2 end - + it "should have a regexp that captures the entire string" do patterns = @type_class.title_patterns string = "abc\n\tdef" @@ -570,4 +570,15 @@ describe Puppet::Type.metaparamclass(:audit) do @resource[:audit] = :noop @resource.parameter(:noop).should be_nil end + + describe "when generating the uniqueness key" do + it "should include all of the key_attributes in alphabetical order by attribute name" do + Puppet::Type.type(:file).stubs(:key_attributes).returns [:path, :mode, :owner] + Puppet::Type.type(:file).stubs(:title_patterns).returns( + [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] + ) + res = Puppet::Type.type(:file).new( :title => '/my/file', :path => '/my/file', :owner => 'root', :content => 'hello' ) + res.uniqueness_key.should == [ nil, 'root', '/my/file'] + end + end end diff --git a/spec/unit/util/execution_spec.rb b/spec/unit/util/execution_spec.rb new file mode 100644 index 000000000..312dd3b8e --- /dev/null +++ b/spec/unit/util/execution_spec.rb @@ -0,0 +1,49 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Util::Execution do + include Puppet::Util::Execution + describe "#withenv" do + before :each do + @original_path = ENV["PATH"] + @new_env = {:PATH => "/some/bogus/path"} + end + + it "should change environment variables within the block then reset environment variables to their original values" do + withenv @new_env do + ENV["PATH"].should == "/some/bogus/path" + end + ENV["PATH"].should == @original_path + end + + it "should reset environment variables to their original values even if the block fails" do + begin + withenv @new_env do + ENV["PATH"].should == "/some/bogus/path" + raise "This is a failure" + end + rescue + end + ENV["PATH"].should == @original_path + end + + it "should reset environment variables even when they are set twice" do + # Setting Path & Environment parameters in Exec type can cause weirdness + @new_env["PATH"] = "/someother/bogus/path" + withenv @new_env do + # When assigning duplicate keys, can't guarantee order of evaluation + ENV["PATH"].should =~ /\/some.*\/bogus\/path/ + end + ENV["PATH"].should == @original_path + end + + it "should remove any new environment variables after the block ends" do + @new_env[:FOO] = "bar" + withenv @new_env do + ENV["FOO"].should == "bar" + end + ENV["FOO"].should == nil + end + end +end diff --git a/spec/unit/util/execution_stub_spec.rb b/spec/unit/util/execution_stub_spec.rb new file mode 100644 index 000000000..14cf9c67a --- /dev/null +++ b/spec/unit/util/execution_stub_spec.rb @@ -0,0 +1,35 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Util::ExecutionStub do + it "should use the provided stub code when 'set' is called" do + Puppet::Util::ExecutionStub.set do |command, options| + command.should == ['/bin/foo', 'bar'] + "stub output" + end + Puppet::Util::ExecutionStub.current_value.should_not == nil + Puppet::Util.execute(['/bin/foo', 'bar']).should == "stub output" + end + + it "should automatically restore normal execution at the conclusion of each spec test" do + # Note: this test relies on the previous test creating a stub. + Puppet::Util::ExecutionStub.current_value.should == nil + end + + it "should restore normal execution after 'reset' is called" do + true_command = Puppet::Util.which('true') # Note: "true" exists at different paths in different OSes + stub_call_count = 0 + Puppet::Util::ExecutionStub.set do |command, options| + command.should == [true_command] + stub_call_count += 1 + 'stub called' + end + Puppet::Util.execute([true_command]).should == 'stub called' + stub_call_count.should == 1 + Puppet::Util::ExecutionStub.reset + Puppet::Util::ExecutionStub.current_value.should == nil + Puppet::Util.execute([true_command]).should == '' + stub_call_count.should == 1 + end +end diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb index 28c33c295..b4453ae86 100755 --- a/spec/unit/util/rdoc/parser_spec.rb +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -43,6 +43,18 @@ describe RDoc::Parser do @parser.scan.should be_a(RDoc::PuppetTopLevel) end + + it "should scan the top level even if the file has already parsed" do + known_type = stub 'known_types' + env = stub 'env' + Puppet::Node::Environment.stubs(:new).returns(env) + env.stubs(:known_resource_types).returns(known_type) + known_type.expects(:watching_file?).with("module/manifests/init.pp").returns(true) + + @parser.expects(:scan_top_level) + + @parser.scan + end end describe "when scanning top level entities" do @@ -339,7 +351,7 @@ describe RDoc::Parser do describe "when scanning for includes and requires" do def create_stmt(name) - stmt_value = stub "#{name}_value", :value => "myclass" + stmt_value = stub "#{name}_value", :to_s => "myclass" Puppet::Parser::AST::Function.new( :name => name, @@ -357,13 +369,13 @@ describe RDoc::Parser do it "should also scan mono-instruction code" do @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } - @parser.scan_for_include_or_require(@class,create_stmt("include")) + @parser.scan_for_include_or_require(@class, create_stmt("include")) end it "should register recursively includes to the current container" do @code.stubs(:children).returns([ create_stmt("include") ]) - @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } + @class.expects(:add_include)#.with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, [@code]) end diff --git a/spec/unit/util/settings_spec.rb b/spec/unit/util/settings_spec.rb index 7bca44b76..07b712c08 100755 --- a/spec/unit/util/settings_spec.rb +++ b/spec/unit/util/settings_spec.rb @@ -284,7 +284,8 @@ describe Puppet::Util::Settings do @settings = Puppet::Util::Settings.new @settings.setdefaults :section, :config => ["/my/file", "a"], - :one => ["ONE", "a"] + :one => ["ONE", "a"], + :two => ["TWO", "b"] FileTest.stubs(:exist?).returns true Puppet.stubs(:run_mode).returns stub('run_mode', :name => :mymode) end @@ -331,12 +332,20 @@ describe Puppet::Util::Settings do @settings.value(:one, "env").should == "envval" end - it "should interpolate found values using the current environment" do + it 'should use the current environment for $environment' do @settings.setdefaults :main, :myval => ["$environment/foo", "mydocs"] @settings.value(:myval, "myenv").should == "myenv/foo" end + it "should interpolate found values using the current environment" do + text = "[main]\none = mainval\n[myname]\none = nameval\ntwo = $one/two\n" + @settings.stubs(:read_file).returns(text) + @settings.parse + + @settings.value(:two, "myname").should == "nameval/two" + end + it "should return values in a specified environment before values in the main or name sections" do text = "[env]\none = envval\n[main]\none = mainval\n[myname]\none = nameval\n" @settings.stubs(:read_file).returns(text) |
